diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 11:03:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 11:03:38 -0400 |
commit | 7115e3fcf45514db7525a05365b10454ff7f345e (patch) | |
tree | 17450e6337d559cc35dae6a7a73abab01ac63f00 | |
parent | 1f6e05171bb5cc32a4d6437ab2269fc21d169ca7 (diff) | |
parent | c752d04066a36ae30b29795f3fa3f536292c1f8c (diff) |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (121 commits)
perf symbols: Increase symbol KSYM_NAME_LEN size
perf hists browser: Refuse 'a' hotkey on non symbolic views
perf ui browser: Use libslang to read keys
perf tools: Fix tracing info recording
perf hists browser: Elide DSO column when it is set to just one DSO, ditto for threads
perf hists: Don't consider filtered entries when calculating column widths
perf hists: Don't decay total_period for filtered entries
perf hists browser: Honour symbol_conf.show_{nr_samples,total_period}
perf hists browser: Do not exit on tab key with single event
perf annotate browser: Don't change selection line when returning from callq
perf tools: handle endianness of feature bitmap
perf tools: Add prelink suggestion to dso update message
perf script: Fix unknown feature comment
perf hists browser: Apply the dso and thread filters when merging new batches
perf hists: Move the dso and thread filters from hist_browser
perf ui browser: Honour the xterm colors
perf top tui: Give color hints just on the percentage, like on --stdio
perf ui browser: Make the colors configurable and change the defaults
perf tui: Remove unneeded call to newtCls on startup
perf hists: Don't format the percentage on hist_entry__snprintf
...
Fix up conflicts in arch/x86/kernel/kprobes.c manually.
Ingo's tree did the insane "add volatile to const array", which just
doesn't make sense ("volatile const"?). But we could remove the const
*and* make the array volatile to make doubly sure that gcc doesn't
optimize it away..
Also fix up kernel/trace/ring_buffer.c non-data-conflicts manually: the
reader_lock has been turned into a raw lock by the core locking merge,
and there was a new user of it introduced in this perf core merge. Make
sure that new use also uses the raw accessor functions.
117 files changed, 6208 insertions, 2879 deletions
diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index b92b9445255d..6c4e9aaa70c1 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig | |||
@@ -10,6 +10,7 @@ config M32R | |||
10 | select HAVE_GENERIC_HARDIRQS | 10 | select HAVE_GENERIC_HARDIRQS |
11 | select GENERIC_IRQ_PROBE | 11 | select GENERIC_IRQ_PROBE |
12 | select GENERIC_IRQ_SHOW | 12 | select GENERIC_IRQ_SHOW |
13 | select GENERIC_ATOMIC64 | ||
13 | 14 | ||
14 | config SBUS | 15 | config SBUS |
15 | bool | 16 | bool |
diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h index 4886a68f267e..fd3f9f18cf3f 100644 --- a/arch/x86/include/asm/nmi.h +++ b/arch/x86/include/asm/nmi.h | |||
@@ -22,27 +22,26 @@ void arch_trigger_all_cpu_backtrace(void); | |||
22 | #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace | 22 | #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace |
23 | #endif | 23 | #endif |
24 | 24 | ||
25 | /* | 25 | #define NMI_FLAG_FIRST 1 |
26 | * Define some priorities for the nmi notifier call chain. | 26 | |
27 | * | 27 | enum { |
28 | * Create a local nmi bit that has a higher priority than | 28 | NMI_LOCAL=0, |
29 | * external nmis, because the local ones are more frequent. | 29 | NMI_UNKNOWN, |
30 | * | 30 | NMI_MAX |
31 | * Also setup some default high/normal/low settings for | 31 | }; |
32 | * subsystems to registers with. Using 4 bits to separate | 32 | |
33 | * the priorities. This can go a lot higher if needed be. | 33 | #define NMI_DONE 0 |
34 | */ | 34 | #define NMI_HANDLED 1 |
35 | 35 | ||
36 | #define NMI_LOCAL_SHIFT 16 /* randomly picked */ | 36 | typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); |
37 | #define NMI_LOCAL_BIT (1ULL << NMI_LOCAL_SHIFT) | 37 | |
38 | #define NMI_HIGH_PRIOR (1ULL << 8) | 38 | int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long, |
39 | #define NMI_NORMAL_PRIOR (1ULL << 4) | 39 | const char *); |
40 | #define NMI_LOW_PRIOR (1ULL << 0) | 40 | |
41 | #define NMI_LOCAL_HIGH_PRIOR (NMI_LOCAL_BIT | NMI_HIGH_PRIOR) | 41 | void unregister_nmi_handler(unsigned int, const char *); |
42 | #define NMI_LOCAL_NORMAL_PRIOR (NMI_LOCAL_BIT | NMI_NORMAL_PRIOR) | ||
43 | #define NMI_LOCAL_LOW_PRIOR (NMI_LOCAL_BIT | NMI_LOW_PRIOR) | ||
44 | 42 | ||
45 | void stop_nmi(void); | 43 | void stop_nmi(void); |
46 | void restart_nmi(void); | 44 | void restart_nmi(void); |
45 | void local_touch_nmi(void); | ||
47 | 46 | ||
48 | #endif /* _ASM_X86_NMI_H */ | 47 | #endif /* _ASM_X86_NMI_H */ |
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 094fb30817ab..f61c62f7d5d8 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h | |||
@@ -29,6 +29,9 @@ | |||
29 | #define ARCH_PERFMON_EVENTSEL_INV (1ULL << 23) | 29 | #define ARCH_PERFMON_EVENTSEL_INV (1ULL << 23) |
30 | #define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL | 30 | #define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL |
31 | 31 | ||
32 | #define AMD_PERFMON_EVENTSEL_GUESTONLY (1ULL << 40) | ||
33 | #define AMD_PERFMON_EVENTSEL_HOSTONLY (1ULL << 41) | ||
34 | |||
32 | #define AMD64_EVENTSEL_EVENT \ | 35 | #define AMD64_EVENTSEL_EVENT \ |
33 | (ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32)) | 36 | (ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32)) |
34 | #define INTEL_ARCH_EVENT_MASK \ | 37 | #define INTEL_ARCH_EVENT_MASK \ |
@@ -43,14 +46,17 @@ | |||
43 | #define AMD64_RAW_EVENT_MASK \ | 46 | #define AMD64_RAW_EVENT_MASK \ |
44 | (X86_RAW_EVENT_MASK | \ | 47 | (X86_RAW_EVENT_MASK | \ |
45 | AMD64_EVENTSEL_EVENT) | 48 | AMD64_EVENTSEL_EVENT) |
49 | #define AMD64_NUM_COUNTERS 4 | ||
50 | #define AMD64_NUM_COUNTERS_F15H 6 | ||
51 | #define AMD64_NUM_COUNTERS_MAX AMD64_NUM_COUNTERS_F15H | ||
46 | 52 | ||
47 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c | 53 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c |
48 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) | 54 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) |
49 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX 0 | 55 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX 0 |
50 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT \ | 56 | #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT \ |
51 | (1 << (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX)) | 57 | (1 << (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX)) |
52 | 58 | ||
53 | #define ARCH_PERFMON_BRANCH_MISSES_RETIRED 6 | 59 | #define ARCH_PERFMON_BRANCH_MISSES_RETIRED 6 |
54 | 60 | ||
55 | /* | 61 | /* |
56 | * Intel "Architectural Performance Monitoring" CPUID | 62 | * Intel "Architectural Performance Monitoring" CPUID |
@@ -110,6 +116,35 @@ union cpuid10_edx { | |||
110 | */ | 116 | */ |
111 | #define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) | 117 | #define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) |
112 | 118 | ||
119 | /* | ||
120 | * IBS cpuid feature detection | ||
121 | */ | ||
122 | |||
123 | #define IBS_CPUID_FEATURES 0x8000001b | ||
124 | |||
125 | /* | ||
126 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | ||
127 | * bit 0 is used to indicate the existence of IBS. | ||
128 | */ | ||
129 | #define IBS_CAPS_AVAIL (1U<<0) | ||
130 | #define IBS_CAPS_FETCHSAM (1U<<1) | ||
131 | #define IBS_CAPS_OPSAM (1U<<2) | ||
132 | #define IBS_CAPS_RDWROPCNT (1U<<3) | ||
133 | #define IBS_CAPS_OPCNT (1U<<4) | ||
134 | #define IBS_CAPS_BRNTRGT (1U<<5) | ||
135 | #define IBS_CAPS_OPCNTEXT (1U<<6) | ||
136 | |||
137 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ | ||
138 | | IBS_CAPS_FETCHSAM \ | ||
139 | | IBS_CAPS_OPSAM) | ||
140 | |||
141 | /* | ||
142 | * IBS APIC setup | ||
143 | */ | ||
144 | #define IBSCTL 0x1cc | ||
145 | #define IBSCTL_LVT_OFFSET_VALID (1ULL<<8) | ||
146 | #define IBSCTL_LVT_OFFSET_MASK 0x0F | ||
147 | |||
113 | /* IbsFetchCtl bits/masks */ | 148 | /* IbsFetchCtl bits/masks */ |
114 | #define IBS_FETCH_RAND_EN (1ULL<<57) | 149 | #define IBS_FETCH_RAND_EN (1ULL<<57) |
115 | #define IBS_FETCH_VAL (1ULL<<49) | 150 | #define IBS_FETCH_VAL (1ULL<<49) |
@@ -124,6 +159,8 @@ union cpuid10_edx { | |||
124 | #define IBS_OP_MAX_CNT 0x0000FFFFULL | 159 | #define IBS_OP_MAX_CNT 0x0000FFFFULL |
125 | #define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */ | 160 | #define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */ |
126 | 161 | ||
162 | extern u32 get_ibs_caps(void); | ||
163 | |||
127 | #ifdef CONFIG_PERF_EVENTS | 164 | #ifdef CONFIG_PERF_EVENTS |
128 | extern void perf_events_lapic_init(void); | 165 | extern void perf_events_lapic_init(void); |
129 | 166 | ||
@@ -159,7 +196,19 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); | |||
159 | ); \ | 196 | ); \ |
160 | } | 197 | } |
161 | 198 | ||
199 | struct perf_guest_switch_msr { | ||
200 | unsigned msr; | ||
201 | u64 host, guest; | ||
202 | }; | ||
203 | |||
204 | extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); | ||
162 | #else | 205 | #else |
206 | static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr) | ||
207 | { | ||
208 | *nr = 0; | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
163 | static inline void perf_events_lapic_init(void) { } | 212 | static inline void perf_events_lapic_init(void) { } |
164 | #endif | 213 | #endif |
165 | 214 | ||
diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h index 3250e3d605d9..92f297069e87 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h | |||
@@ -23,7 +23,7 @@ void machine_real_restart(unsigned int type); | |||
23 | #define MRR_BIOS 0 | 23 | #define MRR_BIOS 0 |
24 | #define MRR_APM 1 | 24 | #define MRR_APM 1 |
25 | 25 | ||
26 | typedef void (*nmi_shootdown_cb)(int, struct die_args*); | 26 | typedef void (*nmi_shootdown_cb)(int, struct pt_regs*); |
27 | void nmi_shootdown_cpus(nmi_shootdown_cb callback); | 27 | void nmi_shootdown_cpus(nmi_shootdown_cb callback); |
28 | 28 | ||
29 | #endif /* _ASM_X86_REBOOT_H */ | 29 | #endif /* _ASM_X86_REBOOT_H */ |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 82f2912155a5..8baca3c4871c 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -19,7 +19,7 @@ endif | |||
19 | 19 | ||
20 | obj-y := process_$(BITS).o signal.o entry_$(BITS).o | 20 | obj-y := process_$(BITS).o signal.o entry_$(BITS).o |
21 | obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o | 21 | obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o |
22 | obj-y += time.o ioport.o ldt.o dumpstack.o | 22 | obj-y += time.o ioport.o ldt.o dumpstack.o nmi.o |
23 | obj-y += setup.o x86_init.o i8259.o irqinit.o jump_label.o | 23 | obj-y += setup.o x86_init.o i8259.o irqinit.o jump_label.o |
24 | obj-$(CONFIG_IRQ_WORK) += irq_work.o | 24 | obj-$(CONFIG_IRQ_WORK) += irq_work.o |
25 | obj-y += probe_roms.o | 25 | obj-y += probe_roms.o |
diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c index d5e57db0f7be..31cb9ae992b7 100644 --- a/arch/x86/kernel/apic/hw_nmi.c +++ b/arch/x86/kernel/apic/hw_nmi.c | |||
@@ -60,22 +60,10 @@ void arch_trigger_all_cpu_backtrace(void) | |||
60 | } | 60 | } |
61 | 61 | ||
62 | static int __kprobes | 62 | static int __kprobes |
63 | arch_trigger_all_cpu_backtrace_handler(struct notifier_block *self, | 63 | arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs) |
64 | unsigned long cmd, void *__args) | ||
65 | { | 64 | { |
66 | struct die_args *args = __args; | ||
67 | struct pt_regs *regs; | ||
68 | int cpu; | 65 | int cpu; |
69 | 66 | ||
70 | switch (cmd) { | ||
71 | case DIE_NMI: | ||
72 | break; | ||
73 | |||
74 | default: | ||
75 | return NOTIFY_DONE; | ||
76 | } | ||
77 | |||
78 | regs = args->regs; | ||
79 | cpu = smp_processor_id(); | 67 | cpu = smp_processor_id(); |
80 | 68 | ||
81 | if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { | 69 | if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { |
@@ -86,21 +74,16 @@ arch_trigger_all_cpu_backtrace_handler(struct notifier_block *self, | |||
86 | show_regs(regs); | 74 | show_regs(regs); |
87 | arch_spin_unlock(&lock); | 75 | arch_spin_unlock(&lock); |
88 | cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); | 76 | cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); |
89 | return NOTIFY_STOP; | 77 | return NMI_HANDLED; |
90 | } | 78 | } |
91 | 79 | ||
92 | return NOTIFY_DONE; | 80 | return NMI_DONE; |
93 | } | 81 | } |
94 | 82 | ||
95 | static __read_mostly struct notifier_block backtrace_notifier = { | ||
96 | .notifier_call = arch_trigger_all_cpu_backtrace_handler, | ||
97 | .next = NULL, | ||
98 | .priority = NMI_LOCAL_LOW_PRIOR, | ||
99 | }; | ||
100 | |||
101 | static int __init register_trigger_all_cpu_backtrace(void) | 83 | static int __init register_trigger_all_cpu_backtrace(void) |
102 | { | 84 | { |
103 | register_die_notifier(&backtrace_notifier); | 85 | register_nmi_handler(NMI_LOCAL, arch_trigger_all_cpu_backtrace_handler, |
86 | 0, "arch_bt"); | ||
104 | return 0; | 87 | return 0; |
105 | } | 88 | } |
106 | early_initcall(register_trigger_all_cpu_backtrace); | 89 | early_initcall(register_trigger_all_cpu_backtrace); |
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index 34b18594e724..75be00ecfff2 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c | |||
@@ -672,18 +672,11 @@ void __cpuinit uv_cpu_init(void) | |||
672 | /* | 672 | /* |
673 | * When NMI is received, print a stack trace. | 673 | * When NMI is received, print a stack trace. |
674 | */ | 674 | */ |
675 | int uv_handle_nmi(struct notifier_block *self, unsigned long reason, void *data) | 675 | int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) |
676 | { | 676 | { |
677 | unsigned long real_uv_nmi; | 677 | unsigned long real_uv_nmi; |
678 | int bid; | 678 | int bid; |
679 | 679 | ||
680 | if (reason != DIE_NMIUNKNOWN) | ||
681 | return NOTIFY_OK; | ||
682 | |||
683 | if (in_crash_kexec) | ||
684 | /* do nothing if entering the crash kernel */ | ||
685 | return NOTIFY_OK; | ||
686 | |||
687 | /* | 680 | /* |
688 | * Each blade has an MMR that indicates when an NMI has been sent | 681 | * Each blade has an MMR that indicates when an NMI has been sent |
689 | * to cpus on the blade. If an NMI is detected, atomically | 682 | * to cpus on the blade. If an NMI is detected, atomically |
@@ -704,7 +697,7 @@ int uv_handle_nmi(struct notifier_block *self, unsigned long reason, void *data) | |||
704 | } | 697 | } |
705 | 698 | ||
706 | if (likely(__get_cpu_var(cpu_last_nmi_count) == uv_blade_info[bid].nmi_count)) | 699 | if (likely(__get_cpu_var(cpu_last_nmi_count) == uv_blade_info[bid].nmi_count)) |
707 | return NOTIFY_DONE; | 700 | return NMI_DONE; |
708 | 701 | ||
709 | __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; | 702 | __get_cpu_var(cpu_last_nmi_count) = uv_blade_info[bid].nmi_count; |
710 | 703 | ||
@@ -717,17 +710,12 @@ int uv_handle_nmi(struct notifier_block *self, unsigned long reason, void *data) | |||
717 | dump_stack(); | 710 | dump_stack(); |
718 | spin_unlock(&uv_nmi_lock); | 711 | spin_unlock(&uv_nmi_lock); |
719 | 712 | ||
720 | return NOTIFY_STOP; | 713 | return NMI_HANDLED; |
721 | } | 714 | } |
722 | 715 | ||
723 | static struct notifier_block uv_dump_stack_nmi_nb = { | ||
724 | .notifier_call = uv_handle_nmi, | ||
725 | .priority = NMI_LOCAL_LOW_PRIOR - 1, | ||
726 | }; | ||
727 | |||
728 | void uv_register_nmi_notifier(void) | 716 | void uv_register_nmi_notifier(void) |
729 | { | 717 | { |
730 | if (register_die_notifier(&uv_dump_stack_nmi_nb)) | 718 | if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) |
731 | printk(KERN_WARNING "UV NMI handler failed to register\n"); | 719 | printk(KERN_WARNING "UV NMI handler failed to register\n"); |
732 | } | 720 | } |
733 | 721 | ||
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 6042981d0309..fe6eb197f848 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile | |||
@@ -28,10 +28,15 @@ obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o | |||
28 | 28 | ||
29 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o | 29 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o |
30 | 30 | ||
31 | ifdef CONFIG_PERF_EVENTS | ||
32 | obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd.o | ||
33 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_p4.o perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o | ||
34 | endif | ||
35 | |||
31 | obj-$(CONFIG_X86_MCE) += mcheck/ | 36 | obj-$(CONFIG_X86_MCE) += mcheck/ |
32 | obj-$(CONFIG_MTRR) += mtrr/ | 37 | obj-$(CONFIG_MTRR) += mtrr/ |
33 | 38 | ||
34 | obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o | 39 | obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o perf_event_amd_ibs.o |
35 | 40 | ||
36 | quiet_cmd_mkcapflags = MKCAP $@ | 41 | quiet_cmd_mkcapflags = MKCAP $@ |
37 | cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ | 42 | cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ |
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c index 0ed633c5048b..6199232161cf 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-inject.c +++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c | |||
@@ -78,27 +78,20 @@ static void raise_exception(struct mce *m, struct pt_regs *pregs) | |||
78 | 78 | ||
79 | static cpumask_var_t mce_inject_cpumask; | 79 | static cpumask_var_t mce_inject_cpumask; |
80 | 80 | ||
81 | static int mce_raise_notify(struct notifier_block *self, | 81 | static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs) |
82 | unsigned long val, void *data) | ||
83 | { | 82 | { |
84 | struct die_args *args = (struct die_args *)data; | ||
85 | int cpu = smp_processor_id(); | 83 | int cpu = smp_processor_id(); |
86 | struct mce *m = &__get_cpu_var(injectm); | 84 | struct mce *m = &__get_cpu_var(injectm); |
87 | if (val != DIE_NMI || !cpumask_test_cpu(cpu, mce_inject_cpumask)) | 85 | if (!cpumask_test_cpu(cpu, mce_inject_cpumask)) |
88 | return NOTIFY_DONE; | 86 | return NMI_DONE; |
89 | cpumask_clear_cpu(cpu, mce_inject_cpumask); | 87 | cpumask_clear_cpu(cpu, mce_inject_cpumask); |
90 | if (m->inject_flags & MCJ_EXCEPTION) | 88 | if (m->inject_flags & MCJ_EXCEPTION) |
91 | raise_exception(m, args->regs); | 89 | raise_exception(m, regs); |
92 | else if (m->status) | 90 | else if (m->status) |
93 | raise_poll(m); | 91 | raise_poll(m); |
94 | return NOTIFY_STOP; | 92 | return NMI_HANDLED; |
95 | } | 93 | } |
96 | 94 | ||
97 | static struct notifier_block mce_raise_nb = { | ||
98 | .notifier_call = mce_raise_notify, | ||
99 | .priority = NMI_LOCAL_NORMAL_PRIOR, | ||
100 | }; | ||
101 | |||
102 | /* Inject mce on current CPU */ | 95 | /* Inject mce on current CPU */ |
103 | static int raise_local(void) | 96 | static int raise_local(void) |
104 | { | 97 | { |
@@ -216,7 +209,8 @@ static int inject_init(void) | |||
216 | return -ENOMEM; | 209 | return -ENOMEM; |
217 | printk(KERN_INFO "Machine check injector initialized\n"); | 210 | printk(KERN_INFO "Machine check injector initialized\n"); |
218 | mce_chrdev_ops.write = mce_write; | 211 | mce_chrdev_ops.write = mce_write; |
219 | register_die_notifier(&mce_raise_nb); | 212 | register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, |
213 | "mce_notify"); | ||
220 | return 0; | 214 | return 0; |
221 | } | 215 | } |
222 | 216 | ||
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 08363b042122..fce51ad1f362 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
@@ -908,9 +908,6 @@ void do_machine_check(struct pt_regs *regs, long error_code) | |||
908 | 908 | ||
909 | percpu_inc(mce_exception_count); | 909 | percpu_inc(mce_exception_count); |
910 | 910 | ||
911 | if (notify_die(DIE_NMI, "machine check", regs, error_code, | ||
912 | 18, SIGKILL) == NOTIFY_STOP) | ||
913 | goto out; | ||
914 | if (!banks) | 911 | if (!banks) |
915 | goto out; | 912 | goto out; |
916 | 913 | ||
@@ -1140,6 +1137,15 @@ static void mce_start_timer(unsigned long data) | |||
1140 | add_timer_on(t, smp_processor_id()); | 1137 | add_timer_on(t, smp_processor_id()); |
1141 | } | 1138 | } |
1142 | 1139 | ||
1140 | /* Must not be called in IRQ context where del_timer_sync() can deadlock */ | ||
1141 | static void mce_timer_delete_all(void) | ||
1142 | { | ||
1143 | int cpu; | ||
1144 | |||
1145 | for_each_online_cpu(cpu) | ||
1146 | del_timer_sync(&per_cpu(mce_timer, cpu)); | ||
1147 | } | ||
1148 | |||
1143 | static void mce_do_trigger(struct work_struct *work) | 1149 | static void mce_do_trigger(struct work_struct *work) |
1144 | { | 1150 | { |
1145 | call_usermodehelper(mce_helper, mce_helper_argv, NULL, UMH_NO_WAIT); | 1151 | call_usermodehelper(mce_helper, mce_helper_argv, NULL, UMH_NO_WAIT); |
@@ -1750,7 +1756,6 @@ static struct syscore_ops mce_syscore_ops = { | |||
1750 | 1756 | ||
1751 | static void mce_cpu_restart(void *data) | 1757 | static void mce_cpu_restart(void *data) |
1752 | { | 1758 | { |
1753 | del_timer_sync(&__get_cpu_var(mce_timer)); | ||
1754 | if (!mce_available(__this_cpu_ptr(&cpu_info))) | 1759 | if (!mce_available(__this_cpu_ptr(&cpu_info))) |
1755 | return; | 1760 | return; |
1756 | __mcheck_cpu_init_generic(); | 1761 | __mcheck_cpu_init_generic(); |
@@ -1760,16 +1765,15 @@ static void mce_cpu_restart(void *data) | |||
1760 | /* Reinit MCEs after user configuration changes */ | 1765 | /* Reinit MCEs after user configuration changes */ |
1761 | static void mce_restart(void) | 1766 | static void mce_restart(void) |
1762 | { | 1767 | { |
1768 | mce_timer_delete_all(); | ||
1763 | on_each_cpu(mce_cpu_restart, NULL, 1); | 1769 | on_each_cpu(mce_cpu_restart, NULL, 1); |
1764 | } | 1770 | } |
1765 | 1771 | ||
1766 | /* Toggle features for corrected errors */ | 1772 | /* Toggle features for corrected errors */ |
1767 | static void mce_disable_ce(void *all) | 1773 | static void mce_disable_cmci(void *data) |
1768 | { | 1774 | { |
1769 | if (!mce_available(__this_cpu_ptr(&cpu_info))) | 1775 | if (!mce_available(__this_cpu_ptr(&cpu_info))) |
1770 | return; | 1776 | return; |
1771 | if (all) | ||
1772 | del_timer_sync(&__get_cpu_var(mce_timer)); | ||
1773 | cmci_clear(); | 1777 | cmci_clear(); |
1774 | } | 1778 | } |
1775 | 1779 | ||
@@ -1852,7 +1856,8 @@ static ssize_t set_ignore_ce(struct sys_device *s, | |||
1852 | if (mce_ignore_ce ^ !!new) { | 1856 | if (mce_ignore_ce ^ !!new) { |
1853 | if (new) { | 1857 | if (new) { |
1854 | /* disable ce features */ | 1858 | /* disable ce features */ |
1855 | on_each_cpu(mce_disable_ce, (void *)1, 1); | 1859 | mce_timer_delete_all(); |
1860 | on_each_cpu(mce_disable_cmci, NULL, 1); | ||
1856 | mce_ignore_ce = 1; | 1861 | mce_ignore_ce = 1; |
1857 | } else { | 1862 | } else { |
1858 | /* enable ce features */ | 1863 | /* enable ce features */ |
@@ -1875,7 +1880,7 @@ static ssize_t set_cmci_disabled(struct sys_device *s, | |||
1875 | if (mce_cmci_disabled ^ !!new) { | 1880 | if (mce_cmci_disabled ^ !!new) { |
1876 | if (new) { | 1881 | if (new) { |
1877 | /* disable cmci */ | 1882 | /* disable cmci */ |
1878 | on_each_cpu(mce_disable_ce, NULL, 1); | 1883 | on_each_cpu(mce_disable_cmci, NULL, 1); |
1879 | mce_cmci_disabled = 1; | 1884 | mce_cmci_disabled = 1; |
1880 | } else { | 1885 | } else { |
1881 | /* enable cmci */ | 1886 | /* enable cmci */ |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index cfa62ec090ec..640891014b2a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <asm/smp.h> | 32 | #include <asm/smp.h> |
33 | #include <asm/alternative.h> | 33 | #include <asm/alternative.h> |
34 | 34 | ||
35 | #include "perf_event.h" | ||
36 | |||
35 | #if 0 | 37 | #if 0 |
36 | #undef wrmsrl | 38 | #undef wrmsrl |
37 | #define wrmsrl(msr, val) \ | 39 | #define wrmsrl(msr, val) \ |
@@ -43,283 +45,17 @@ do { \ | |||
43 | } while (0) | 45 | } while (0) |
44 | #endif | 46 | #endif |
45 | 47 | ||
46 | /* | 48 | struct x86_pmu x86_pmu __read_mostly; |
47 | * | NHM/WSM | SNB | | ||
48 | * register ------------------------------- | ||
49 | * | HT | no HT | HT | no HT | | ||
50 | *----------------------------------------- | ||
51 | * offcore | core | core | cpu | core | | ||
52 | * lbr_sel | core | core | cpu | core | | ||
53 | * ld_lat | cpu | core | cpu | core | | ||
54 | *----------------------------------------- | ||
55 | * | ||
56 | * Given that there is a small number of shared regs, | ||
57 | * we can pre-allocate their slot in the per-cpu | ||
58 | * per-core reg tables. | ||
59 | */ | ||
60 | enum extra_reg_type { | ||
61 | EXTRA_REG_NONE = -1, /* not used */ | ||
62 | |||
63 | EXTRA_REG_RSP_0 = 0, /* offcore_response_0 */ | ||
64 | EXTRA_REG_RSP_1 = 1, /* offcore_response_1 */ | ||
65 | |||
66 | EXTRA_REG_MAX /* number of entries needed */ | ||
67 | }; | ||
68 | |||
69 | struct event_constraint { | ||
70 | union { | ||
71 | unsigned long idxmsk[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
72 | u64 idxmsk64; | ||
73 | }; | ||
74 | u64 code; | ||
75 | u64 cmask; | ||
76 | int weight; | ||
77 | }; | ||
78 | |||
79 | struct amd_nb { | ||
80 | int nb_id; /* NorthBridge id */ | ||
81 | int refcnt; /* reference count */ | ||
82 | struct perf_event *owners[X86_PMC_IDX_MAX]; | ||
83 | struct event_constraint event_constraints[X86_PMC_IDX_MAX]; | ||
84 | }; | ||
85 | |||
86 | struct intel_percore; | ||
87 | |||
88 | #define MAX_LBR_ENTRIES 16 | ||
89 | |||
90 | struct cpu_hw_events { | ||
91 | /* | ||
92 | * Generic x86 PMC bits | ||
93 | */ | ||
94 | struct perf_event *events[X86_PMC_IDX_MAX]; /* in counter order */ | ||
95 | unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
96 | unsigned long running[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
97 | int enabled; | ||
98 | |||
99 | int n_events; | ||
100 | int n_added; | ||
101 | int n_txn; | ||
102 | int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ | ||
103 | u64 tags[X86_PMC_IDX_MAX]; | ||
104 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ | ||
105 | |||
106 | unsigned int group_flag; | ||
107 | |||
108 | /* | ||
109 | * Intel DebugStore bits | ||
110 | */ | ||
111 | struct debug_store *ds; | ||
112 | u64 pebs_enabled; | ||
113 | |||
114 | /* | ||
115 | * Intel LBR bits | ||
116 | */ | ||
117 | int lbr_users; | ||
118 | void *lbr_context; | ||
119 | struct perf_branch_stack lbr_stack; | ||
120 | struct perf_branch_entry lbr_entries[MAX_LBR_ENTRIES]; | ||
121 | |||
122 | /* | ||
123 | * manage shared (per-core, per-cpu) registers | ||
124 | * used on Intel NHM/WSM/SNB | ||
125 | */ | ||
126 | struct intel_shared_regs *shared_regs; | ||
127 | |||
128 | /* | ||
129 | * AMD specific bits | ||
130 | */ | ||
131 | struct amd_nb *amd_nb; | ||
132 | }; | ||
133 | |||
134 | #define __EVENT_CONSTRAINT(c, n, m, w) {\ | ||
135 | { .idxmsk64 = (n) }, \ | ||
136 | .code = (c), \ | ||
137 | .cmask = (m), \ | ||
138 | .weight = (w), \ | ||
139 | } | ||
140 | |||
141 | #define EVENT_CONSTRAINT(c, n, m) \ | ||
142 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n)) | ||
143 | |||
144 | /* | ||
145 | * Constraint on the Event code. | ||
146 | */ | ||
147 | #define INTEL_EVENT_CONSTRAINT(c, n) \ | ||
148 | EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT) | ||
149 | |||
150 | /* | ||
151 | * Constraint on the Event code + UMask + fixed-mask | ||
152 | * | ||
153 | * filter mask to validate fixed counter events. | ||
154 | * the following filters disqualify for fixed counters: | ||
155 | * - inv | ||
156 | * - edge | ||
157 | * - cnt-mask | ||
158 | * The other filters are supported by fixed counters. | ||
159 | * The any-thread option is supported starting with v3. | ||
160 | */ | ||
161 | #define FIXED_EVENT_CONSTRAINT(c, n) \ | ||
162 | EVENT_CONSTRAINT(c, (1ULL << (32+n)), X86_RAW_EVENT_MASK) | ||
163 | |||
164 | /* | ||
165 | * Constraint on the Event code + UMask | ||
166 | */ | ||
167 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ | ||
168 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) | ||
169 | |||
170 | #define EVENT_CONSTRAINT_END \ | ||
171 | EVENT_CONSTRAINT(0, 0, 0) | ||
172 | |||
173 | #define for_each_event_constraint(e, c) \ | ||
174 | for ((e) = (c); (e)->weight; (e)++) | ||
175 | |||
176 | /* | ||
177 | * Per register state. | ||
178 | */ | ||
179 | struct er_account { | ||
180 | raw_spinlock_t lock; /* per-core: protect structure */ | ||
181 | u64 config; /* extra MSR config */ | ||
182 | u64 reg; /* extra MSR number */ | ||
183 | atomic_t ref; /* reference count */ | ||
184 | }; | ||
185 | |||
186 | /* | ||
187 | * Extra registers for specific events. | ||
188 | * | ||
189 | * Some events need large masks and require external MSRs. | ||
190 | * Those extra MSRs end up being shared for all events on | ||
191 | * a PMU and sometimes between PMU of sibling HT threads. | ||
192 | * In either case, the kernel needs to handle conflicting | ||
193 | * accesses to those extra, shared, regs. The data structure | ||
194 | * to manage those registers is stored in cpu_hw_event. | ||
195 | */ | ||
196 | struct extra_reg { | ||
197 | unsigned int event; | ||
198 | unsigned int msr; | ||
199 | u64 config_mask; | ||
200 | u64 valid_mask; | ||
201 | int idx; /* per_xxx->regs[] reg index */ | ||
202 | }; | ||
203 | |||
204 | #define EVENT_EXTRA_REG(e, ms, m, vm, i) { \ | ||
205 | .event = (e), \ | ||
206 | .msr = (ms), \ | ||
207 | .config_mask = (m), \ | ||
208 | .valid_mask = (vm), \ | ||
209 | .idx = EXTRA_REG_##i \ | ||
210 | } | ||
211 | |||
212 | #define INTEL_EVENT_EXTRA_REG(event, msr, vm, idx) \ | ||
213 | EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT, vm, idx) | ||
214 | |||
215 | #define EVENT_EXTRA_END EVENT_EXTRA_REG(0, 0, 0, 0, RSP_0) | ||
216 | |||
217 | union perf_capabilities { | ||
218 | struct { | ||
219 | u64 lbr_format : 6; | ||
220 | u64 pebs_trap : 1; | ||
221 | u64 pebs_arch_reg : 1; | ||
222 | u64 pebs_format : 4; | ||
223 | u64 smm_freeze : 1; | ||
224 | }; | ||
225 | u64 capabilities; | ||
226 | }; | ||
227 | |||
228 | /* | ||
229 | * struct x86_pmu - generic x86 pmu | ||
230 | */ | ||
231 | struct x86_pmu { | ||
232 | /* | ||
233 | * Generic x86 PMC bits | ||
234 | */ | ||
235 | const char *name; | ||
236 | int version; | ||
237 | int (*handle_irq)(struct pt_regs *); | ||
238 | void (*disable_all)(void); | ||
239 | void (*enable_all)(int added); | ||
240 | void (*enable)(struct perf_event *); | ||
241 | void (*disable)(struct perf_event *); | ||
242 | int (*hw_config)(struct perf_event *event); | ||
243 | int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); | ||
244 | unsigned eventsel; | ||
245 | unsigned perfctr; | ||
246 | u64 (*event_map)(int); | ||
247 | int max_events; | ||
248 | int num_counters; | ||
249 | int num_counters_fixed; | ||
250 | int cntval_bits; | ||
251 | u64 cntval_mask; | ||
252 | int apic; | ||
253 | u64 max_period; | ||
254 | struct event_constraint * | ||
255 | (*get_event_constraints)(struct cpu_hw_events *cpuc, | ||
256 | struct perf_event *event); | ||
257 | |||
258 | void (*put_event_constraints)(struct cpu_hw_events *cpuc, | ||
259 | struct perf_event *event); | ||
260 | struct event_constraint *event_constraints; | ||
261 | void (*quirks)(void); | ||
262 | int perfctr_second_write; | ||
263 | |||
264 | int (*cpu_prepare)(int cpu); | ||
265 | void (*cpu_starting)(int cpu); | ||
266 | void (*cpu_dying)(int cpu); | ||
267 | void (*cpu_dead)(int cpu); | ||
268 | |||
269 | /* | ||
270 | * Intel Arch Perfmon v2+ | ||
271 | */ | ||
272 | u64 intel_ctrl; | ||
273 | union perf_capabilities intel_cap; | ||
274 | 49 | ||
275 | /* | 50 | DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { |
276 | * Intel DebugStore bits | ||
277 | */ | ||
278 | int bts, pebs; | ||
279 | int bts_active, pebs_active; | ||
280 | int pebs_record_size; | ||
281 | void (*drain_pebs)(struct pt_regs *regs); | ||
282 | struct event_constraint *pebs_constraints; | ||
283 | |||
284 | /* | ||
285 | * Intel LBR | ||
286 | */ | ||
287 | unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ | ||
288 | int lbr_nr; /* hardware stack size */ | ||
289 | |||
290 | /* | ||
291 | * Extra registers for events | ||
292 | */ | ||
293 | struct extra_reg *extra_regs; | ||
294 | unsigned int er_flags; | ||
295 | }; | ||
296 | |||
297 | #define ERF_NO_HT_SHARING 1 | ||
298 | #define ERF_HAS_RSP_1 2 | ||
299 | |||
300 | static struct x86_pmu x86_pmu __read_mostly; | ||
301 | |||
302 | static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { | ||
303 | .enabled = 1, | 51 | .enabled = 1, |
304 | }; | 52 | }; |
305 | 53 | ||
306 | static int x86_perf_event_set_period(struct perf_event *event); | 54 | u64 __read_mostly hw_cache_event_ids |
307 | |||
308 | /* | ||
309 | * Generalized hw caching related hw_event table, filled | ||
310 | * in on a per model basis. A value of 0 means | ||
311 | * 'not supported', -1 means 'hw_event makes no sense on | ||
312 | * this CPU', any other value means the raw hw_event | ||
313 | * ID. | ||
314 | */ | ||
315 | |||
316 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
317 | |||
318 | static u64 __read_mostly hw_cache_event_ids | ||
319 | [PERF_COUNT_HW_CACHE_MAX] | 55 | [PERF_COUNT_HW_CACHE_MAX] |
320 | [PERF_COUNT_HW_CACHE_OP_MAX] | 56 | [PERF_COUNT_HW_CACHE_OP_MAX] |
321 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; | 57 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; |
322 | static u64 __read_mostly hw_cache_extra_regs | 58 | u64 __read_mostly hw_cache_extra_regs |
323 | [PERF_COUNT_HW_CACHE_MAX] | 59 | [PERF_COUNT_HW_CACHE_MAX] |
324 | [PERF_COUNT_HW_CACHE_OP_MAX] | 60 | [PERF_COUNT_HW_CACHE_OP_MAX] |
325 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; | 61 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; |
@@ -329,8 +65,7 @@ static u64 __read_mostly hw_cache_extra_regs | |||
329 | * Can only be executed on the CPU where the event is active. | 65 | * Can only be executed on the CPU where the event is active. |
330 | * Returns the delta events processed. | 66 | * Returns the delta events processed. |
331 | */ | 67 | */ |
332 | static u64 | 68 | u64 x86_perf_event_update(struct perf_event *event) |
333 | x86_perf_event_update(struct perf_event *event) | ||
334 | { | 69 | { |
335 | struct hw_perf_event *hwc = &event->hw; | 70 | struct hw_perf_event *hwc = &event->hw; |
336 | int shift = 64 - x86_pmu.cntval_bits; | 71 | int shift = 64 - x86_pmu.cntval_bits; |
@@ -373,30 +108,6 @@ again: | |||
373 | return new_raw_count; | 108 | return new_raw_count; |
374 | } | 109 | } |
375 | 110 | ||
376 | static inline int x86_pmu_addr_offset(int index) | ||
377 | { | ||
378 | int offset; | ||
379 | |||
380 | /* offset = X86_FEATURE_PERFCTR_CORE ? index << 1 : index */ | ||
381 | alternative_io(ASM_NOP2, | ||
382 | "shll $1, %%eax", | ||
383 | X86_FEATURE_PERFCTR_CORE, | ||
384 | "=a" (offset), | ||
385 | "a" (index)); | ||
386 | |||
387 | return offset; | ||
388 | } | ||
389 | |||
390 | static inline unsigned int x86_pmu_config_addr(int index) | ||
391 | { | ||
392 | return x86_pmu.eventsel + x86_pmu_addr_offset(index); | ||
393 | } | ||
394 | |||
395 | static inline unsigned int x86_pmu_event_addr(int index) | ||
396 | { | ||
397 | return x86_pmu.perfctr + x86_pmu_addr_offset(index); | ||
398 | } | ||
399 | |||
400 | /* | 111 | /* |
401 | * Find and validate any extra registers to set up. | 112 | * Find and validate any extra registers to set up. |
402 | */ | 113 | */ |
@@ -532,9 +243,6 @@ msr_fail: | |||
532 | return false; | 243 | return false; |
533 | } | 244 | } |
534 | 245 | ||
535 | static void reserve_ds_buffers(void); | ||
536 | static void release_ds_buffers(void); | ||
537 | |||
538 | static void hw_perf_event_destroy(struct perf_event *event) | 246 | static void hw_perf_event_destroy(struct perf_event *event) |
539 | { | 247 | { |
540 | if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { | 248 | if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { |
@@ -583,7 +291,7 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event) | |||
583 | return x86_pmu_extra_regs(val, event); | 291 | return x86_pmu_extra_regs(val, event); |
584 | } | 292 | } |
585 | 293 | ||
586 | static int x86_setup_perfctr(struct perf_event *event) | 294 | int x86_setup_perfctr(struct perf_event *event) |
587 | { | 295 | { |
588 | struct perf_event_attr *attr = &event->attr; | 296 | struct perf_event_attr *attr = &event->attr; |
589 | struct hw_perf_event *hwc = &event->hw; | 297 | struct hw_perf_event *hwc = &event->hw; |
@@ -647,7 +355,7 @@ static int x86_setup_perfctr(struct perf_event *event) | |||
647 | return 0; | 355 | return 0; |
648 | } | 356 | } |
649 | 357 | ||
650 | static int x86_pmu_hw_config(struct perf_event *event) | 358 | int x86_pmu_hw_config(struct perf_event *event) |
651 | { | 359 | { |
652 | if (event->attr.precise_ip) { | 360 | if (event->attr.precise_ip) { |
653 | int precise = 0; | 361 | int precise = 0; |
@@ -723,7 +431,7 @@ static int __x86_pmu_event_init(struct perf_event *event) | |||
723 | return x86_pmu.hw_config(event); | 431 | return x86_pmu.hw_config(event); |
724 | } | 432 | } |
725 | 433 | ||
726 | static void x86_pmu_disable_all(void) | 434 | void x86_pmu_disable_all(void) |
727 | { | 435 | { |
728 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 436 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
729 | int idx; | 437 | int idx; |
@@ -758,15 +466,7 @@ static void x86_pmu_disable(struct pmu *pmu) | |||
758 | x86_pmu.disable_all(); | 466 | x86_pmu.disable_all(); |
759 | } | 467 | } |
760 | 468 | ||
761 | static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, | 469 | void x86_pmu_enable_all(int added) |
762 | u64 enable_mask) | ||
763 | { | ||
764 | if (hwc->extra_reg.reg) | ||
765 | wrmsrl(hwc->extra_reg.reg, hwc->extra_reg.config); | ||
766 | wrmsrl(hwc->config_base, hwc->config | enable_mask); | ||
767 | } | ||
768 | |||
769 | static void x86_pmu_enable_all(int added) | ||
770 | { | 470 | { |
771 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 471 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
772 | int idx; | 472 | int idx; |
@@ -788,7 +488,7 @@ static inline int is_x86_event(struct perf_event *event) | |||
788 | return event->pmu == &pmu; | 488 | return event->pmu == &pmu; |
789 | } | 489 | } |
790 | 490 | ||
791 | static int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | 491 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) |
792 | { | 492 | { |
793 | struct event_constraint *c, *constraints[X86_PMC_IDX_MAX]; | 493 | struct event_constraint *c, *constraints[X86_PMC_IDX_MAX]; |
794 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 494 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
@@ -959,7 +659,6 @@ static inline int match_prev_assignment(struct hw_perf_event *hwc, | |||
959 | } | 659 | } |
960 | 660 | ||
961 | static void x86_pmu_start(struct perf_event *event, int flags); | 661 | static void x86_pmu_start(struct perf_event *event, int flags); |
962 | static void x86_pmu_stop(struct perf_event *event, int flags); | ||
963 | 662 | ||
964 | static void x86_pmu_enable(struct pmu *pmu) | 663 | static void x86_pmu_enable(struct pmu *pmu) |
965 | { | 664 | { |
@@ -1031,21 +730,13 @@ static void x86_pmu_enable(struct pmu *pmu) | |||
1031 | x86_pmu.enable_all(added); | 730 | x86_pmu.enable_all(added); |
1032 | } | 731 | } |
1033 | 732 | ||
1034 | static inline void x86_pmu_disable_event(struct perf_event *event) | ||
1035 | { | ||
1036 | struct hw_perf_event *hwc = &event->hw; | ||
1037 | |||
1038 | wrmsrl(hwc->config_base, hwc->config); | ||
1039 | } | ||
1040 | |||
1041 | static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); | 733 | static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); |
1042 | 734 | ||
1043 | /* | 735 | /* |
1044 | * Set the next IRQ period, based on the hwc->period_left value. | 736 | * Set the next IRQ period, based on the hwc->period_left value. |
1045 | * To be called with the event disabled in hw: | 737 | * To be called with the event disabled in hw: |
1046 | */ | 738 | */ |
1047 | static int | 739 | int x86_perf_event_set_period(struct perf_event *event) |
1048 | x86_perf_event_set_period(struct perf_event *event) | ||
1049 | { | 740 | { |
1050 | struct hw_perf_event *hwc = &event->hw; | 741 | struct hw_perf_event *hwc = &event->hw; |
1051 | s64 left = local64_read(&hwc->period_left); | 742 | s64 left = local64_read(&hwc->period_left); |
@@ -1105,7 +796,7 @@ x86_perf_event_set_period(struct perf_event *event) | |||
1105 | return ret; | 796 | return ret; |
1106 | } | 797 | } |
1107 | 798 | ||
1108 | static void x86_pmu_enable_event(struct perf_event *event) | 799 | void x86_pmu_enable_event(struct perf_event *event) |
1109 | { | 800 | { |
1110 | if (__this_cpu_read(cpu_hw_events.enabled)) | 801 | if (__this_cpu_read(cpu_hw_events.enabled)) |
1111 | __x86_pmu_enable_event(&event->hw, | 802 | __x86_pmu_enable_event(&event->hw, |
@@ -1244,7 +935,7 @@ void perf_event_print_debug(void) | |||
1244 | local_irq_restore(flags); | 935 | local_irq_restore(flags); |
1245 | } | 936 | } |
1246 | 937 | ||
1247 | static void x86_pmu_stop(struct perf_event *event, int flags) | 938 | void x86_pmu_stop(struct perf_event *event, int flags) |
1248 | { | 939 | { |
1249 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 940 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
1250 | struct hw_perf_event *hwc = &event->hw; | 941 | struct hw_perf_event *hwc = &event->hw; |
@@ -1297,7 +988,7 @@ static void x86_pmu_del(struct perf_event *event, int flags) | |||
1297 | perf_event_update_userpage(event); | 988 | perf_event_update_userpage(event); |
1298 | } | 989 | } |
1299 | 990 | ||
1300 | static int x86_pmu_handle_irq(struct pt_regs *regs) | 991 | int x86_pmu_handle_irq(struct pt_regs *regs) |
1301 | { | 992 | { |
1302 | struct perf_sample_data data; | 993 | struct perf_sample_data data; |
1303 | struct cpu_hw_events *cpuc; | 994 | struct cpu_hw_events *cpuc; |
@@ -1367,109 +1058,28 @@ void perf_events_lapic_init(void) | |||
1367 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1058 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
1368 | } | 1059 | } |
1369 | 1060 | ||
1370 | struct pmu_nmi_state { | ||
1371 | unsigned int marked; | ||
1372 | int handled; | ||
1373 | }; | ||
1374 | |||
1375 | static DEFINE_PER_CPU(struct pmu_nmi_state, pmu_nmi); | ||
1376 | |||
1377 | static int __kprobes | 1061 | static int __kprobes |
1378 | perf_event_nmi_handler(struct notifier_block *self, | 1062 | perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs) |
1379 | unsigned long cmd, void *__args) | ||
1380 | { | 1063 | { |
1381 | struct die_args *args = __args; | ||
1382 | unsigned int this_nmi; | ||
1383 | int handled; | ||
1384 | |||
1385 | if (!atomic_read(&active_events)) | 1064 | if (!atomic_read(&active_events)) |
1386 | return NOTIFY_DONE; | 1065 | return NMI_DONE; |
1387 | |||
1388 | switch (cmd) { | ||
1389 | case DIE_NMI: | ||
1390 | break; | ||
1391 | case DIE_NMIUNKNOWN: | ||
1392 | this_nmi = percpu_read(irq_stat.__nmi_count); | ||
1393 | if (this_nmi != __this_cpu_read(pmu_nmi.marked)) | ||
1394 | /* let the kernel handle the unknown nmi */ | ||
1395 | return NOTIFY_DONE; | ||
1396 | /* | ||
1397 | * This one is a PMU back-to-back nmi. Two events | ||
1398 | * trigger 'simultaneously' raising two back-to-back | ||
1399 | * NMIs. If the first NMI handles both, the latter | ||
1400 | * will be empty and daze the CPU. So, we drop it to | ||
1401 | * avoid false-positive 'unknown nmi' messages. | ||
1402 | */ | ||
1403 | return NOTIFY_STOP; | ||
1404 | default: | ||
1405 | return NOTIFY_DONE; | ||
1406 | } | ||
1407 | |||
1408 | handled = x86_pmu.handle_irq(args->regs); | ||
1409 | if (!handled) | ||
1410 | return NOTIFY_DONE; | ||
1411 | |||
1412 | this_nmi = percpu_read(irq_stat.__nmi_count); | ||
1413 | if ((handled > 1) || | ||
1414 | /* the next nmi could be a back-to-back nmi */ | ||
1415 | ((__this_cpu_read(pmu_nmi.marked) == this_nmi) && | ||
1416 | (__this_cpu_read(pmu_nmi.handled) > 1))) { | ||
1417 | /* | ||
1418 | * We could have two subsequent back-to-back nmis: The | ||
1419 | * first handles more than one counter, the 2nd | ||
1420 | * handles only one counter and the 3rd handles no | ||
1421 | * counter. | ||
1422 | * | ||
1423 | * This is the 2nd nmi because the previous was | ||
1424 | * handling more than one counter. We will mark the | ||
1425 | * next (3rd) and then drop it if unhandled. | ||
1426 | */ | ||
1427 | __this_cpu_write(pmu_nmi.marked, this_nmi + 1); | ||
1428 | __this_cpu_write(pmu_nmi.handled, handled); | ||
1429 | } | ||
1430 | 1066 | ||
1431 | return NOTIFY_STOP; | 1067 | return x86_pmu.handle_irq(regs); |
1432 | } | 1068 | } |
1433 | 1069 | ||
1434 | static __read_mostly struct notifier_block perf_event_nmi_notifier = { | 1070 | struct event_constraint emptyconstraint; |
1435 | .notifier_call = perf_event_nmi_handler, | 1071 | struct event_constraint unconstrained; |
1436 | .next = NULL, | ||
1437 | .priority = NMI_LOCAL_LOW_PRIOR, | ||
1438 | }; | ||
1439 | |||
1440 | static struct event_constraint unconstrained; | ||
1441 | static struct event_constraint emptyconstraint; | ||
1442 | |||
1443 | static struct event_constraint * | ||
1444 | x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | ||
1445 | { | ||
1446 | struct event_constraint *c; | ||
1447 | |||
1448 | if (x86_pmu.event_constraints) { | ||
1449 | for_each_event_constraint(c, x86_pmu.event_constraints) { | ||
1450 | if ((event->hw.config & c->cmask) == c->code) | ||
1451 | return c; | ||
1452 | } | ||
1453 | } | ||
1454 | |||
1455 | return &unconstrained; | ||
1456 | } | ||
1457 | |||
1458 | #include "perf_event_amd.c" | ||
1459 | #include "perf_event_p6.c" | ||
1460 | #include "perf_event_p4.c" | ||
1461 | #include "perf_event_intel_lbr.c" | ||
1462 | #include "perf_event_intel_ds.c" | ||
1463 | #include "perf_event_intel.c" | ||
1464 | 1072 | ||
1465 | static int __cpuinit | 1073 | static int __cpuinit |
1466 | x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | 1074 | x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) |
1467 | { | 1075 | { |
1468 | unsigned int cpu = (long)hcpu; | 1076 | unsigned int cpu = (long)hcpu; |
1077 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | ||
1469 | int ret = NOTIFY_OK; | 1078 | int ret = NOTIFY_OK; |
1470 | 1079 | ||
1471 | switch (action & ~CPU_TASKS_FROZEN) { | 1080 | switch (action & ~CPU_TASKS_FROZEN) { |
1472 | case CPU_UP_PREPARE: | 1081 | case CPU_UP_PREPARE: |
1082 | cpuc->kfree_on_online = NULL; | ||
1473 | if (x86_pmu.cpu_prepare) | 1083 | if (x86_pmu.cpu_prepare) |
1474 | ret = x86_pmu.cpu_prepare(cpu); | 1084 | ret = x86_pmu.cpu_prepare(cpu); |
1475 | break; | 1085 | break; |
@@ -1479,6 +1089,10 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | |||
1479 | x86_pmu.cpu_starting(cpu); | 1089 | x86_pmu.cpu_starting(cpu); |
1480 | break; | 1090 | break; |
1481 | 1091 | ||
1092 | case CPU_ONLINE: | ||
1093 | kfree(cpuc->kfree_on_online); | ||
1094 | break; | ||
1095 | |||
1482 | case CPU_DYING: | 1096 | case CPU_DYING: |
1483 | if (x86_pmu.cpu_dying) | 1097 | if (x86_pmu.cpu_dying) |
1484 | x86_pmu.cpu_dying(cpu); | 1098 | x86_pmu.cpu_dying(cpu); |
@@ -1557,7 +1171,7 @@ static int __init init_hw_perf_events(void) | |||
1557 | ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; | 1171 | ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; |
1558 | 1172 | ||
1559 | perf_events_lapic_init(); | 1173 | perf_events_lapic_init(); |
1560 | register_die_notifier(&perf_event_nmi_notifier); | 1174 | register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); |
1561 | 1175 | ||
1562 | unconstrained = (struct event_constraint) | 1176 | unconstrained = (struct event_constraint) |
1563 | __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, | 1177 | __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, |
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h new file mode 100644 index 000000000000..b9698d40ac4b --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event.h | |||
@@ -0,0 +1,505 @@ | |||
1 | /* | ||
2 | * Performance events x86 architecture header | ||
3 | * | ||
4 | * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de> | ||
5 | * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar | ||
6 | * Copyright (C) 2009 Jaswinder Singh Rajput | ||
7 | * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter | ||
8 | * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> | ||
9 | * Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com> | ||
10 | * Copyright (C) 2009 Google, Inc., Stephane Eranian | ||
11 | * | ||
12 | * For licencing details see kernel-base/COPYING | ||
13 | */ | ||
14 | |||
15 | #include <linux/perf_event.h> | ||
16 | |||
17 | /* | ||
18 | * | NHM/WSM | SNB | | ||
19 | * register ------------------------------- | ||
20 | * | HT | no HT | HT | no HT | | ||
21 | *----------------------------------------- | ||
22 | * offcore | core | core | cpu | core | | ||
23 | * lbr_sel | core | core | cpu | core | | ||
24 | * ld_lat | cpu | core | cpu | core | | ||
25 | *----------------------------------------- | ||
26 | * | ||
27 | * Given that there is a small number of shared regs, | ||
28 | * we can pre-allocate their slot in the per-cpu | ||
29 | * per-core reg tables. | ||
30 | */ | ||
31 | enum extra_reg_type { | ||
32 | EXTRA_REG_NONE = -1, /* not used */ | ||
33 | |||
34 | EXTRA_REG_RSP_0 = 0, /* offcore_response_0 */ | ||
35 | EXTRA_REG_RSP_1 = 1, /* offcore_response_1 */ | ||
36 | |||
37 | EXTRA_REG_MAX /* number of entries needed */ | ||
38 | }; | ||
39 | |||
40 | struct event_constraint { | ||
41 | union { | ||
42 | unsigned long idxmsk[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
43 | u64 idxmsk64; | ||
44 | }; | ||
45 | u64 code; | ||
46 | u64 cmask; | ||
47 | int weight; | ||
48 | }; | ||
49 | |||
50 | struct amd_nb { | ||
51 | int nb_id; /* NorthBridge id */ | ||
52 | int refcnt; /* reference count */ | ||
53 | struct perf_event *owners[X86_PMC_IDX_MAX]; | ||
54 | struct event_constraint event_constraints[X86_PMC_IDX_MAX]; | ||
55 | }; | ||
56 | |||
57 | /* The maximal number of PEBS events: */ | ||
58 | #define MAX_PEBS_EVENTS 4 | ||
59 | |||
60 | /* | ||
61 | * A debug store configuration. | ||
62 | * | ||
63 | * We only support architectures that use 64bit fields. | ||
64 | */ | ||
65 | struct debug_store { | ||
66 | u64 bts_buffer_base; | ||
67 | u64 bts_index; | ||
68 | u64 bts_absolute_maximum; | ||
69 | u64 bts_interrupt_threshold; | ||
70 | u64 pebs_buffer_base; | ||
71 | u64 pebs_index; | ||
72 | u64 pebs_absolute_maximum; | ||
73 | u64 pebs_interrupt_threshold; | ||
74 | u64 pebs_event_reset[MAX_PEBS_EVENTS]; | ||
75 | }; | ||
76 | |||
77 | /* | ||
78 | * Per register state. | ||
79 | */ | ||
80 | struct er_account { | ||
81 | raw_spinlock_t lock; /* per-core: protect structure */ | ||
82 | u64 config; /* extra MSR config */ | ||
83 | u64 reg; /* extra MSR number */ | ||
84 | atomic_t ref; /* reference count */ | ||
85 | }; | ||
86 | |||
87 | /* | ||
88 | * Per core/cpu state | ||
89 | * | ||
90 | * Used to coordinate shared registers between HT threads or | ||
91 | * among events on a single PMU. | ||
92 | */ | ||
93 | struct intel_shared_regs { | ||
94 | struct er_account regs[EXTRA_REG_MAX]; | ||
95 | int refcnt; /* per-core: #HT threads */ | ||
96 | unsigned core_id; /* per-core: core id */ | ||
97 | }; | ||
98 | |||
99 | #define MAX_LBR_ENTRIES 16 | ||
100 | |||
101 | struct cpu_hw_events { | ||
102 | /* | ||
103 | * Generic x86 PMC bits | ||
104 | */ | ||
105 | struct perf_event *events[X86_PMC_IDX_MAX]; /* in counter order */ | ||
106 | unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
107 | unsigned long running[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | ||
108 | int enabled; | ||
109 | |||
110 | int n_events; | ||
111 | int n_added; | ||
112 | int n_txn; | ||
113 | int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ | ||
114 | u64 tags[X86_PMC_IDX_MAX]; | ||
115 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ | ||
116 | |||
117 | unsigned int group_flag; | ||
118 | |||
119 | /* | ||
120 | * Intel DebugStore bits | ||
121 | */ | ||
122 | struct debug_store *ds; | ||
123 | u64 pebs_enabled; | ||
124 | |||
125 | /* | ||
126 | * Intel LBR bits | ||
127 | */ | ||
128 | int lbr_users; | ||
129 | void *lbr_context; | ||
130 | struct perf_branch_stack lbr_stack; | ||
131 | struct perf_branch_entry lbr_entries[MAX_LBR_ENTRIES]; | ||
132 | |||
133 | /* | ||
134 | * Intel host/guest exclude bits | ||
135 | */ | ||
136 | u64 intel_ctrl_guest_mask; | ||
137 | u64 intel_ctrl_host_mask; | ||
138 | struct perf_guest_switch_msr guest_switch_msrs[X86_PMC_IDX_MAX]; | ||
139 | |||
140 | /* | ||
141 | * manage shared (per-core, per-cpu) registers | ||
142 | * used on Intel NHM/WSM/SNB | ||
143 | */ | ||
144 | struct intel_shared_regs *shared_regs; | ||
145 | |||
146 | /* | ||
147 | * AMD specific bits | ||
148 | */ | ||
149 | struct amd_nb *amd_nb; | ||
150 | |||
151 | void *kfree_on_online; | ||
152 | }; | ||
153 | |||
154 | #define __EVENT_CONSTRAINT(c, n, m, w) {\ | ||
155 | { .idxmsk64 = (n) }, \ | ||
156 | .code = (c), \ | ||
157 | .cmask = (m), \ | ||
158 | .weight = (w), \ | ||
159 | } | ||
160 | |||
161 | #define EVENT_CONSTRAINT(c, n, m) \ | ||
162 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n)) | ||
163 | |||
164 | /* | ||
165 | * Constraint on the Event code. | ||
166 | */ | ||
167 | #define INTEL_EVENT_CONSTRAINT(c, n) \ | ||
168 | EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT) | ||
169 | |||
170 | /* | ||
171 | * Constraint on the Event code + UMask + fixed-mask | ||
172 | * | ||
173 | * filter mask to validate fixed counter events. | ||
174 | * the following filters disqualify for fixed counters: | ||
175 | * - inv | ||
176 | * - edge | ||
177 | * - cnt-mask | ||
178 | * The other filters are supported by fixed counters. | ||
179 | * The any-thread option is supported starting with v3. | ||
180 | */ | ||
181 | #define FIXED_EVENT_CONSTRAINT(c, n) \ | ||
182 | EVENT_CONSTRAINT(c, (1ULL << (32+n)), X86_RAW_EVENT_MASK) | ||
183 | |||
184 | /* | ||
185 | * Constraint on the Event code + UMask | ||
186 | */ | ||
187 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ | ||
188 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) | ||
189 | |||
190 | #define EVENT_CONSTRAINT_END \ | ||
191 | EVENT_CONSTRAINT(0, 0, 0) | ||
192 | |||
193 | #define for_each_event_constraint(e, c) \ | ||
194 | for ((e) = (c); (e)->weight; (e)++) | ||
195 | |||
196 | /* | ||
197 | * Extra registers for specific events. | ||
198 | * | ||
199 | * Some events need large masks and require external MSRs. | ||
200 | * Those extra MSRs end up being shared for all events on | ||
201 | * a PMU and sometimes between PMU of sibling HT threads. | ||
202 | * In either case, the kernel needs to handle conflicting | ||
203 | * accesses to those extra, shared, regs. The data structure | ||
204 | * to manage those registers is stored in cpu_hw_event. | ||
205 | */ | ||
206 | struct extra_reg { | ||
207 | unsigned int event; | ||
208 | unsigned int msr; | ||
209 | u64 config_mask; | ||
210 | u64 valid_mask; | ||
211 | int idx; /* per_xxx->regs[] reg index */ | ||
212 | }; | ||
213 | |||
214 | #define EVENT_EXTRA_REG(e, ms, m, vm, i) { \ | ||
215 | .event = (e), \ | ||
216 | .msr = (ms), \ | ||
217 | .config_mask = (m), \ | ||
218 | .valid_mask = (vm), \ | ||
219 | .idx = EXTRA_REG_##i \ | ||
220 | } | ||
221 | |||
222 | #define INTEL_EVENT_EXTRA_REG(event, msr, vm, idx) \ | ||
223 | EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT, vm, idx) | ||
224 | |||
225 | #define EVENT_EXTRA_END EVENT_EXTRA_REG(0, 0, 0, 0, RSP_0) | ||
226 | |||
227 | union perf_capabilities { | ||
228 | struct { | ||
229 | u64 lbr_format:6; | ||
230 | u64 pebs_trap:1; | ||
231 | u64 pebs_arch_reg:1; | ||
232 | u64 pebs_format:4; | ||
233 | u64 smm_freeze:1; | ||
234 | }; | ||
235 | u64 capabilities; | ||
236 | }; | ||
237 | |||
238 | /* | ||
239 | * struct x86_pmu - generic x86 pmu | ||
240 | */ | ||
241 | struct x86_pmu { | ||
242 | /* | ||
243 | * Generic x86 PMC bits | ||
244 | */ | ||
245 | const char *name; | ||
246 | int version; | ||
247 | int (*handle_irq)(struct pt_regs *); | ||
248 | void (*disable_all)(void); | ||
249 | void (*enable_all)(int added); | ||
250 | void (*enable)(struct perf_event *); | ||
251 | void (*disable)(struct perf_event *); | ||
252 | int (*hw_config)(struct perf_event *event); | ||
253 | int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); | ||
254 | unsigned eventsel; | ||
255 | unsigned perfctr; | ||
256 | u64 (*event_map)(int); | ||
257 | int max_events; | ||
258 | int num_counters; | ||
259 | int num_counters_fixed; | ||
260 | int cntval_bits; | ||
261 | u64 cntval_mask; | ||
262 | int apic; | ||
263 | u64 max_period; | ||
264 | struct event_constraint * | ||
265 | (*get_event_constraints)(struct cpu_hw_events *cpuc, | ||
266 | struct perf_event *event); | ||
267 | |||
268 | void (*put_event_constraints)(struct cpu_hw_events *cpuc, | ||
269 | struct perf_event *event); | ||
270 | struct event_constraint *event_constraints; | ||
271 | void (*quirks)(void); | ||
272 | int perfctr_second_write; | ||
273 | |||
274 | int (*cpu_prepare)(int cpu); | ||
275 | void (*cpu_starting)(int cpu); | ||
276 | void (*cpu_dying)(int cpu); | ||
277 | void (*cpu_dead)(int cpu); | ||
278 | |||
279 | /* | ||
280 | * Intel Arch Perfmon v2+ | ||
281 | */ | ||
282 | u64 intel_ctrl; | ||
283 | union perf_capabilities intel_cap; | ||
284 | |||
285 | /* | ||
286 | * Intel DebugStore bits | ||
287 | */ | ||
288 | int bts, pebs; | ||
289 | int bts_active, pebs_active; | ||
290 | int pebs_record_size; | ||
291 | void (*drain_pebs)(struct pt_regs *regs); | ||
292 | struct event_constraint *pebs_constraints; | ||
293 | |||
294 | /* | ||
295 | * Intel LBR | ||
296 | */ | ||
297 | unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ | ||
298 | int lbr_nr; /* hardware stack size */ | ||
299 | |||
300 | /* | ||
301 | * Extra registers for events | ||
302 | */ | ||
303 | struct extra_reg *extra_regs; | ||
304 | unsigned int er_flags; | ||
305 | |||
306 | /* | ||
307 | * Intel host/guest support (KVM) | ||
308 | */ | ||
309 | struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr); | ||
310 | }; | ||
311 | |||
312 | #define ERF_NO_HT_SHARING 1 | ||
313 | #define ERF_HAS_RSP_1 2 | ||
314 | |||
315 | extern struct x86_pmu x86_pmu __read_mostly; | ||
316 | |||
317 | DECLARE_PER_CPU(struct cpu_hw_events, cpu_hw_events); | ||
318 | |||
319 | int x86_perf_event_set_period(struct perf_event *event); | ||
320 | |||
321 | /* | ||
322 | * Generalized hw caching related hw_event table, filled | ||
323 | * in on a per model basis. A value of 0 means | ||
324 | * 'not supported', -1 means 'hw_event makes no sense on | ||
325 | * this CPU', any other value means the raw hw_event | ||
326 | * ID. | ||
327 | */ | ||
328 | |||
329 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
330 | |||
331 | extern u64 __read_mostly hw_cache_event_ids | ||
332 | [PERF_COUNT_HW_CACHE_MAX] | ||
333 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
334 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; | ||
335 | extern u64 __read_mostly hw_cache_extra_regs | ||
336 | [PERF_COUNT_HW_CACHE_MAX] | ||
337 | [PERF_COUNT_HW_CACHE_OP_MAX] | ||
338 | [PERF_COUNT_HW_CACHE_RESULT_MAX]; | ||
339 | |||
340 | u64 x86_perf_event_update(struct perf_event *event); | ||
341 | |||
342 | static inline int x86_pmu_addr_offset(int index) | ||
343 | { | ||
344 | int offset; | ||
345 | |||
346 | /* offset = X86_FEATURE_PERFCTR_CORE ? index << 1 : index */ | ||
347 | alternative_io(ASM_NOP2, | ||
348 | "shll $1, %%eax", | ||
349 | X86_FEATURE_PERFCTR_CORE, | ||
350 | "=a" (offset), | ||
351 | "a" (index)); | ||
352 | |||
353 | return offset; | ||
354 | } | ||
355 | |||
356 | static inline unsigned int x86_pmu_config_addr(int index) | ||
357 | { | ||
358 | return x86_pmu.eventsel + x86_pmu_addr_offset(index); | ||
359 | } | ||
360 | |||
361 | static inline unsigned int x86_pmu_event_addr(int index) | ||
362 | { | ||
363 | return x86_pmu.perfctr + x86_pmu_addr_offset(index); | ||
364 | } | ||
365 | |||
366 | int x86_setup_perfctr(struct perf_event *event); | ||
367 | |||
368 | int x86_pmu_hw_config(struct perf_event *event); | ||
369 | |||
370 | void x86_pmu_disable_all(void); | ||
371 | |||
372 | static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, | ||
373 | u64 enable_mask) | ||
374 | { | ||
375 | if (hwc->extra_reg.reg) | ||
376 | wrmsrl(hwc->extra_reg.reg, hwc->extra_reg.config); | ||
377 | wrmsrl(hwc->config_base, hwc->config | enable_mask); | ||
378 | } | ||
379 | |||
380 | void x86_pmu_enable_all(int added); | ||
381 | |||
382 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign); | ||
383 | |||
384 | void x86_pmu_stop(struct perf_event *event, int flags); | ||
385 | |||
386 | static inline void x86_pmu_disable_event(struct perf_event *event) | ||
387 | { | ||
388 | struct hw_perf_event *hwc = &event->hw; | ||
389 | |||
390 | wrmsrl(hwc->config_base, hwc->config); | ||
391 | } | ||
392 | |||
393 | void x86_pmu_enable_event(struct perf_event *event); | ||
394 | |||
395 | int x86_pmu_handle_irq(struct pt_regs *regs); | ||
396 | |||
397 | extern struct event_constraint emptyconstraint; | ||
398 | |||
399 | extern struct event_constraint unconstrained; | ||
400 | |||
401 | #ifdef CONFIG_CPU_SUP_AMD | ||
402 | |||
403 | int amd_pmu_init(void); | ||
404 | |||
405 | #else /* CONFIG_CPU_SUP_AMD */ | ||
406 | |||
407 | static inline int amd_pmu_init(void) | ||
408 | { | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | #endif /* CONFIG_CPU_SUP_AMD */ | ||
413 | |||
414 | #ifdef CONFIG_CPU_SUP_INTEL | ||
415 | |||
416 | int intel_pmu_save_and_restart(struct perf_event *event); | ||
417 | |||
418 | struct event_constraint * | ||
419 | x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event); | ||
420 | |||
421 | struct intel_shared_regs *allocate_shared_regs(int cpu); | ||
422 | |||
423 | int intel_pmu_init(void); | ||
424 | |||
425 | void init_debug_store_on_cpu(int cpu); | ||
426 | |||
427 | void fini_debug_store_on_cpu(int cpu); | ||
428 | |||
429 | void release_ds_buffers(void); | ||
430 | |||
431 | void reserve_ds_buffers(void); | ||
432 | |||
433 | extern struct event_constraint bts_constraint; | ||
434 | |||
435 | void intel_pmu_enable_bts(u64 config); | ||
436 | |||
437 | void intel_pmu_disable_bts(void); | ||
438 | |||
439 | int intel_pmu_drain_bts_buffer(void); | ||
440 | |||
441 | extern struct event_constraint intel_core2_pebs_event_constraints[]; | ||
442 | |||
443 | extern struct event_constraint intel_atom_pebs_event_constraints[]; | ||
444 | |||
445 | extern struct event_constraint intel_nehalem_pebs_event_constraints[]; | ||
446 | |||
447 | extern struct event_constraint intel_westmere_pebs_event_constraints[]; | ||
448 | |||
449 | extern struct event_constraint intel_snb_pebs_event_constraints[]; | ||
450 | |||
451 | struct event_constraint *intel_pebs_constraints(struct perf_event *event); | ||
452 | |||
453 | void intel_pmu_pebs_enable(struct perf_event *event); | ||
454 | |||
455 | void intel_pmu_pebs_disable(struct perf_event *event); | ||
456 | |||
457 | void intel_pmu_pebs_enable_all(void); | ||
458 | |||
459 | void intel_pmu_pebs_disable_all(void); | ||
460 | |||
461 | void intel_ds_init(void); | ||
462 | |||
463 | void intel_pmu_lbr_reset(void); | ||
464 | |||
465 | void intel_pmu_lbr_enable(struct perf_event *event); | ||
466 | |||
467 | void intel_pmu_lbr_disable(struct perf_event *event); | ||
468 | |||
469 | void intel_pmu_lbr_enable_all(void); | ||
470 | |||
471 | void intel_pmu_lbr_disable_all(void); | ||
472 | |||
473 | void intel_pmu_lbr_read(void); | ||
474 | |||
475 | void intel_pmu_lbr_init_core(void); | ||
476 | |||
477 | void intel_pmu_lbr_init_nhm(void); | ||
478 | |||
479 | void intel_pmu_lbr_init_atom(void); | ||
480 | |||
481 | int p4_pmu_init(void); | ||
482 | |||
483 | int p6_pmu_init(void); | ||
484 | |||
485 | #else /* CONFIG_CPU_SUP_INTEL */ | ||
486 | |||
487 | static inline void reserve_ds_buffers(void) | ||
488 | { | ||
489 | } | ||
490 | |||
491 | static inline void release_ds_buffers(void) | ||
492 | { | ||
493 | } | ||
494 | |||
495 | static inline int intel_pmu_init(void) | ||
496 | { | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static inline struct intel_shared_regs *allocate_shared_regs(int cpu) | ||
501 | { | ||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 941caa2e449b..aeefd45697a2 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c | |||
@@ -1,4 +1,10 @@ | |||
1 | #ifdef CONFIG_CPU_SUP_AMD | 1 | #include <linux/perf_event.h> |
2 | #include <linux/types.h> | ||
3 | #include <linux/init.h> | ||
4 | #include <linux/slab.h> | ||
5 | #include <asm/apicdef.h> | ||
6 | |||
7 | #include "perf_event.h" | ||
2 | 8 | ||
3 | static __initconst const u64 amd_hw_cache_event_ids | 9 | static __initconst const u64 amd_hw_cache_event_ids |
4 | [PERF_COUNT_HW_CACHE_MAX] | 10 | [PERF_COUNT_HW_CACHE_MAX] |
@@ -132,6 +138,19 @@ static int amd_pmu_hw_config(struct perf_event *event) | |||
132 | if (ret) | 138 | if (ret) |
133 | return ret; | 139 | return ret; |
134 | 140 | ||
141 | if (event->attr.exclude_host && event->attr.exclude_guest) | ||
142 | /* | ||
143 | * When HO == GO == 1 the hardware treats that as GO == HO == 0 | ||
144 | * and will count in both modes. We don't want to count in that | ||
145 | * case so we emulate no-counting by setting US = OS = 0. | ||
146 | */ | ||
147 | event->hw.config &= ~(ARCH_PERFMON_EVENTSEL_USR | | ||
148 | ARCH_PERFMON_EVENTSEL_OS); | ||
149 | else if (event->attr.exclude_host) | ||
150 | event->hw.config |= AMD_PERFMON_EVENTSEL_GUESTONLY; | ||
151 | else if (event->attr.exclude_guest) | ||
152 | event->hw.config |= AMD_PERFMON_EVENTSEL_HOSTONLY; | ||
153 | |||
135 | if (event->attr.type != PERF_TYPE_RAW) | 154 | if (event->attr.type != PERF_TYPE_RAW) |
136 | return 0; | 155 | return 0; |
137 | 156 | ||
@@ -350,7 +369,7 @@ static void amd_pmu_cpu_starting(int cpu) | |||
350 | continue; | 369 | continue; |
351 | 370 | ||
352 | if (nb->nb_id == nb_id) { | 371 | if (nb->nb_id == nb_id) { |
353 | kfree(cpuc->amd_nb); | 372 | cpuc->kfree_on_online = cpuc->amd_nb; |
354 | cpuc->amd_nb = nb; | 373 | cpuc->amd_nb = nb; |
355 | break; | 374 | break; |
356 | } | 375 | } |
@@ -392,7 +411,7 @@ static __initconst const struct x86_pmu amd_pmu = { | |||
392 | .perfctr = MSR_K7_PERFCTR0, | 411 | .perfctr = MSR_K7_PERFCTR0, |
393 | .event_map = amd_pmu_event_map, | 412 | .event_map = amd_pmu_event_map, |
394 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), | 413 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), |
395 | .num_counters = 4, | 414 | .num_counters = AMD64_NUM_COUNTERS, |
396 | .cntval_bits = 48, | 415 | .cntval_bits = 48, |
397 | .cntval_mask = (1ULL << 48) - 1, | 416 | .cntval_mask = (1ULL << 48) - 1, |
398 | .apic = 1, | 417 | .apic = 1, |
@@ -556,7 +575,7 @@ static __initconst const struct x86_pmu amd_pmu_f15h = { | |||
556 | .perfctr = MSR_F15H_PERF_CTR, | 575 | .perfctr = MSR_F15H_PERF_CTR, |
557 | .event_map = amd_pmu_event_map, | 576 | .event_map = amd_pmu_event_map, |
558 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), | 577 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), |
559 | .num_counters = 6, | 578 | .num_counters = AMD64_NUM_COUNTERS_F15H, |
560 | .cntval_bits = 48, | 579 | .cntval_bits = 48, |
561 | .cntval_mask = (1ULL << 48) - 1, | 580 | .cntval_mask = (1ULL << 48) - 1, |
562 | .apic = 1, | 581 | .apic = 1, |
@@ -573,7 +592,7 @@ static __initconst const struct x86_pmu amd_pmu_f15h = { | |||
573 | #endif | 592 | #endif |
574 | }; | 593 | }; |
575 | 594 | ||
576 | static __init int amd_pmu_init(void) | 595 | __init int amd_pmu_init(void) |
577 | { | 596 | { |
578 | /* Performance-monitoring supported from K7 and later: */ | 597 | /* Performance-monitoring supported from K7 and later: */ |
579 | if (boot_cpu_data.x86 < 6) | 598 | if (boot_cpu_data.x86 < 6) |
@@ -602,12 +621,3 @@ static __init int amd_pmu_init(void) | |||
602 | 621 | ||
603 | return 0; | 622 | return 0; |
604 | } | 623 | } |
605 | |||
606 | #else /* CONFIG_CPU_SUP_AMD */ | ||
607 | |||
608 | static int amd_pmu_init(void) | ||
609 | { | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | #endif | ||
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c new file mode 100644 index 000000000000..ab6343d21825 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * Performance events - AMD IBS | ||
3 | * | ||
4 | * Copyright (C) 2011 Advanced Micro Devices, Inc., Robert Richter | ||
5 | * | ||
6 | * For licencing details see kernel-base/COPYING | ||
7 | */ | ||
8 | |||
9 | #include <linux/perf_event.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/pci.h> | ||
12 | |||
13 | #include <asm/apic.h> | ||
14 | |||
15 | static u32 ibs_caps; | ||
16 | |||
17 | #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) | ||
18 | |||
19 | static struct pmu perf_ibs; | ||
20 | |||
21 | static int perf_ibs_init(struct perf_event *event) | ||
22 | { | ||
23 | if (perf_ibs.type != event->attr.type) | ||
24 | return -ENOENT; | ||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | static int perf_ibs_add(struct perf_event *event, int flags) | ||
29 | { | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static void perf_ibs_del(struct perf_event *event, int flags) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | static struct pmu perf_ibs = { | ||
38 | .event_init= perf_ibs_init, | ||
39 | .add= perf_ibs_add, | ||
40 | .del= perf_ibs_del, | ||
41 | }; | ||
42 | |||
43 | static __init int perf_event_ibs_init(void) | ||
44 | { | ||
45 | if (!ibs_caps) | ||
46 | return -ENODEV; /* ibs not supported by the cpu */ | ||
47 | |||
48 | perf_pmu_register(&perf_ibs, "ibs", -1); | ||
49 | printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | #else /* defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) */ | ||
55 | |||
56 | static __init int perf_event_ibs_init(void) { return 0; } | ||
57 | |||
58 | #endif | ||
59 | |||
60 | /* IBS - apic initialization, for perf and oprofile */ | ||
61 | |||
62 | static __init u32 __get_ibs_caps(void) | ||
63 | { | ||
64 | u32 caps; | ||
65 | unsigned int max_level; | ||
66 | |||
67 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
68 | return 0; | ||
69 | |||
70 | /* check IBS cpuid feature flags */ | ||
71 | max_level = cpuid_eax(0x80000000); | ||
72 | if (max_level < IBS_CPUID_FEATURES) | ||
73 | return IBS_CAPS_DEFAULT; | ||
74 | |||
75 | caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
76 | if (!(caps & IBS_CAPS_AVAIL)) | ||
77 | /* cpuid flags not valid */ | ||
78 | return IBS_CAPS_DEFAULT; | ||
79 | |||
80 | return caps; | ||
81 | } | ||
82 | |||
83 | u32 get_ibs_caps(void) | ||
84 | { | ||
85 | return ibs_caps; | ||
86 | } | ||
87 | |||
88 | EXPORT_SYMBOL(get_ibs_caps); | ||
89 | |||
90 | static inline int get_eilvt(int offset) | ||
91 | { | ||
92 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
93 | } | ||
94 | |||
95 | static inline int put_eilvt(int offset) | ||
96 | { | ||
97 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Check and reserve APIC extended interrupt LVT offset for IBS if available. | ||
102 | */ | ||
103 | static inline int ibs_eilvt_valid(void) | ||
104 | { | ||
105 | int offset; | ||
106 | u64 val; | ||
107 | int valid = 0; | ||
108 | |||
109 | preempt_disable(); | ||
110 | |||
111 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
112 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
113 | |||
114 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
115 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
116 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | if (!get_eilvt(offset)) { | ||
121 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
122 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
123 | goto out; | ||
124 | } | ||
125 | |||
126 | valid = 1; | ||
127 | out: | ||
128 | preempt_enable(); | ||
129 | |||
130 | return valid; | ||
131 | } | ||
132 | |||
133 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
134 | { | ||
135 | struct pci_dev *cpu_cfg; | ||
136 | int nodes; | ||
137 | u32 value = 0; | ||
138 | |||
139 | nodes = 0; | ||
140 | cpu_cfg = NULL; | ||
141 | do { | ||
142 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
143 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
144 | cpu_cfg); | ||
145 | if (!cpu_cfg) | ||
146 | break; | ||
147 | ++nodes; | ||
148 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
149 | | IBSCTL_LVT_OFFSET_VALID); | ||
150 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
151 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
152 | pci_dev_put(cpu_cfg); | ||
153 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
154 | "IBSCTL = 0x%08x\n", value); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | } while (1); | ||
158 | |||
159 | if (!nodes) { | ||
160 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
161 | return -ENODEV; | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * This runs only on the current cpu. We try to find an LVT offset and | ||
169 | * setup the local APIC. For this we must disable preemption. On | ||
170 | * success we initialize all nodes with this offset. This updates then | ||
171 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
172 | * the IBS interrupt vector is handled by perf_ibs_cpu_notifier that | ||
173 | * is using the new offset. | ||
174 | */ | ||
175 | static int force_ibs_eilvt_setup(void) | ||
176 | { | ||
177 | int offset; | ||
178 | int ret; | ||
179 | |||
180 | preempt_disable(); | ||
181 | /* find the next free available EILVT entry, skip offset 0 */ | ||
182 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
183 | if (get_eilvt(offset)) | ||
184 | break; | ||
185 | } | ||
186 | preempt_enable(); | ||
187 | |||
188 | if (offset == APIC_EILVT_NR_MAX) { | ||
189 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
190 | return -EBUSY; | ||
191 | } | ||
192 | |||
193 | ret = setup_ibs_ctl(offset); | ||
194 | if (ret) | ||
195 | goto out; | ||
196 | |||
197 | if (!ibs_eilvt_valid()) { | ||
198 | ret = -EFAULT; | ||
199 | goto out; | ||
200 | } | ||
201 | |||
202 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
203 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
204 | |||
205 | return 0; | ||
206 | out: | ||
207 | preempt_disable(); | ||
208 | put_eilvt(offset); | ||
209 | preempt_enable(); | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static inline int get_ibs_lvt_offset(void) | ||
214 | { | ||
215 | u64 val; | ||
216 | |||
217 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
218 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
219 | return -EINVAL; | ||
220 | |||
221 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
222 | } | ||
223 | |||
224 | static void setup_APIC_ibs(void *dummy) | ||
225 | { | ||
226 | int offset; | ||
227 | |||
228 | offset = get_ibs_lvt_offset(); | ||
229 | if (offset < 0) | ||
230 | goto failed; | ||
231 | |||
232 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
233 | return; | ||
234 | failed: | ||
235 | pr_warn("perf: IBS APIC setup failed on cpu #%d\n", | ||
236 | smp_processor_id()); | ||
237 | } | ||
238 | |||
239 | static void clear_APIC_ibs(void *dummy) | ||
240 | { | ||
241 | int offset; | ||
242 | |||
243 | offset = get_ibs_lvt_offset(); | ||
244 | if (offset >= 0) | ||
245 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
246 | } | ||
247 | |||
248 | static int __cpuinit | ||
249 | perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | ||
250 | { | ||
251 | switch (action & ~CPU_TASKS_FROZEN) { | ||
252 | case CPU_STARTING: | ||
253 | setup_APIC_ibs(NULL); | ||
254 | break; | ||
255 | case CPU_DYING: | ||
256 | clear_APIC_ibs(NULL); | ||
257 | break; | ||
258 | default: | ||
259 | break; | ||
260 | } | ||
261 | |||
262 | return NOTIFY_OK; | ||
263 | } | ||
264 | |||
265 | static __init int amd_ibs_init(void) | ||
266 | { | ||
267 | u32 caps; | ||
268 | int ret; | ||
269 | |||
270 | caps = __get_ibs_caps(); | ||
271 | if (!caps) | ||
272 | return -ENODEV; /* ibs not supported by the cpu */ | ||
273 | |||
274 | if (!ibs_eilvt_valid()) { | ||
275 | ret = force_ibs_eilvt_setup(); | ||
276 | if (ret) { | ||
277 | pr_err("Failed to setup IBS, %d\n", ret); | ||
278 | return ret; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | get_online_cpus(); | ||
283 | ibs_caps = caps; | ||
284 | /* make ibs_caps visible to other cpus: */ | ||
285 | smp_mb(); | ||
286 | perf_cpu_notifier(perf_ibs_cpu_notifier); | ||
287 | smp_call_function(setup_APIC_ibs, NULL, 1); | ||
288 | put_online_cpus(); | ||
289 | |||
290 | return perf_event_ibs_init(); | ||
291 | } | ||
292 | |||
293 | /* Since we need the pci subsystem to init ibs we can't do this earlier: */ | ||
294 | device_initcall(amd_ibs_init); | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index f88af2c2a561..e09ca20e86ee 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -1,16 +1,19 @@ | |||
1 | #ifdef CONFIG_CPU_SUP_INTEL | ||
2 | |||
3 | /* | 1 | /* |
4 | * Per core/cpu state | 2 | * Per core/cpu state |
5 | * | 3 | * |
6 | * Used to coordinate shared registers between HT threads or | 4 | * Used to coordinate shared registers between HT threads or |
7 | * among events on a single PMU. | 5 | * among events on a single PMU. |
8 | */ | 6 | */ |
9 | struct intel_shared_regs { | 7 | |
10 | struct er_account regs[EXTRA_REG_MAX]; | 8 | #include <linux/stddef.h> |
11 | int refcnt; /* per-core: #HT threads */ | 9 | #include <linux/types.h> |
12 | unsigned core_id; /* per-core: core id */ | 10 | #include <linux/init.h> |
13 | }; | 11 | #include <linux/slab.h> |
12 | |||
13 | #include <asm/hardirq.h> | ||
14 | #include <asm/apic.h> | ||
15 | |||
16 | #include "perf_event.h" | ||
14 | 17 | ||
15 | /* | 18 | /* |
16 | * Intel PerfMon, used on Core and later. | 19 | * Intel PerfMon, used on Core and later. |
@@ -746,7 +749,8 @@ static void intel_pmu_enable_all(int added) | |||
746 | 749 | ||
747 | intel_pmu_pebs_enable_all(); | 750 | intel_pmu_pebs_enable_all(); |
748 | intel_pmu_lbr_enable_all(); | 751 | intel_pmu_lbr_enable_all(); |
749 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); | 752 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, |
753 | x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask); | ||
750 | 754 | ||
751 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { | 755 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { |
752 | struct perf_event *event = | 756 | struct perf_event *event = |
@@ -869,6 +873,7 @@ static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) | |||
869 | static void intel_pmu_disable_event(struct perf_event *event) | 873 | static void intel_pmu_disable_event(struct perf_event *event) |
870 | { | 874 | { |
871 | struct hw_perf_event *hwc = &event->hw; | 875 | struct hw_perf_event *hwc = &event->hw; |
876 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
872 | 877 | ||
873 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { | 878 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { |
874 | intel_pmu_disable_bts(); | 879 | intel_pmu_disable_bts(); |
@@ -876,6 +881,9 @@ static void intel_pmu_disable_event(struct perf_event *event) | |||
876 | return; | 881 | return; |
877 | } | 882 | } |
878 | 883 | ||
884 | cpuc->intel_ctrl_guest_mask &= ~(1ull << hwc->idx); | ||
885 | cpuc->intel_ctrl_host_mask &= ~(1ull << hwc->idx); | ||
886 | |||
879 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 887 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
880 | intel_pmu_disable_fixed(hwc); | 888 | intel_pmu_disable_fixed(hwc); |
881 | return; | 889 | return; |
@@ -921,6 +929,7 @@ static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) | |||
921 | static void intel_pmu_enable_event(struct perf_event *event) | 929 | static void intel_pmu_enable_event(struct perf_event *event) |
922 | { | 930 | { |
923 | struct hw_perf_event *hwc = &event->hw; | 931 | struct hw_perf_event *hwc = &event->hw; |
932 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
924 | 933 | ||
925 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { | 934 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { |
926 | if (!__this_cpu_read(cpu_hw_events.enabled)) | 935 | if (!__this_cpu_read(cpu_hw_events.enabled)) |
@@ -930,6 +939,11 @@ static void intel_pmu_enable_event(struct perf_event *event) | |||
930 | return; | 939 | return; |
931 | } | 940 | } |
932 | 941 | ||
942 | if (event->attr.exclude_host) | ||
943 | cpuc->intel_ctrl_guest_mask |= (1ull << hwc->idx); | ||
944 | if (event->attr.exclude_guest) | ||
945 | cpuc->intel_ctrl_host_mask |= (1ull << hwc->idx); | ||
946 | |||
933 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 947 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
934 | intel_pmu_enable_fixed(hwc); | 948 | intel_pmu_enable_fixed(hwc); |
935 | return; | 949 | return; |
@@ -945,7 +959,7 @@ static void intel_pmu_enable_event(struct perf_event *event) | |||
945 | * Save and restart an expired event. Called by NMI contexts, | 959 | * Save and restart an expired event. Called by NMI contexts, |
946 | * so it has to be careful about preempting normal event ops: | 960 | * so it has to be careful about preempting normal event ops: |
947 | */ | 961 | */ |
948 | static int intel_pmu_save_and_restart(struct perf_event *event) | 962 | int intel_pmu_save_and_restart(struct perf_event *event) |
949 | { | 963 | { |
950 | x86_perf_event_update(event); | 964 | x86_perf_event_update(event); |
951 | return x86_perf_event_set_period(event); | 965 | return x86_perf_event_set_period(event); |
@@ -1197,6 +1211,21 @@ intel_shared_regs_constraints(struct cpu_hw_events *cpuc, | |||
1197 | return c; | 1211 | return c; |
1198 | } | 1212 | } |
1199 | 1213 | ||
1214 | struct event_constraint * | ||
1215 | x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | ||
1216 | { | ||
1217 | struct event_constraint *c; | ||
1218 | |||
1219 | if (x86_pmu.event_constraints) { | ||
1220 | for_each_event_constraint(c, x86_pmu.event_constraints) { | ||
1221 | if ((event->hw.config & c->cmask) == c->code) | ||
1222 | return c; | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | return &unconstrained; | ||
1227 | } | ||
1228 | |||
1200 | static struct event_constraint * | 1229 | static struct event_constraint * |
1201 | intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | 1230 | intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) |
1202 | { | 1231 | { |
@@ -1284,12 +1313,84 @@ static int intel_pmu_hw_config(struct perf_event *event) | |||
1284 | return 0; | 1313 | return 0; |
1285 | } | 1314 | } |
1286 | 1315 | ||
1316 | struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr) | ||
1317 | { | ||
1318 | if (x86_pmu.guest_get_msrs) | ||
1319 | return x86_pmu.guest_get_msrs(nr); | ||
1320 | *nr = 0; | ||
1321 | return NULL; | ||
1322 | } | ||
1323 | EXPORT_SYMBOL_GPL(perf_guest_get_msrs); | ||
1324 | |||
1325 | static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr) | ||
1326 | { | ||
1327 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1328 | struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; | ||
1329 | |||
1330 | arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL; | ||
1331 | arr[0].host = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask; | ||
1332 | arr[0].guest = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_host_mask; | ||
1333 | |||
1334 | *nr = 1; | ||
1335 | return arr; | ||
1336 | } | ||
1337 | |||
1338 | static struct perf_guest_switch_msr *core_guest_get_msrs(int *nr) | ||
1339 | { | ||
1340 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1341 | struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; | ||
1342 | int idx; | ||
1343 | |||
1344 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | ||
1345 | struct perf_event *event = cpuc->events[idx]; | ||
1346 | |||
1347 | arr[idx].msr = x86_pmu_config_addr(idx); | ||
1348 | arr[idx].host = arr[idx].guest = 0; | ||
1349 | |||
1350 | if (!test_bit(idx, cpuc->active_mask)) | ||
1351 | continue; | ||
1352 | |||
1353 | arr[idx].host = arr[idx].guest = | ||
1354 | event->hw.config | ARCH_PERFMON_EVENTSEL_ENABLE; | ||
1355 | |||
1356 | if (event->attr.exclude_host) | ||
1357 | arr[idx].host &= ~ARCH_PERFMON_EVENTSEL_ENABLE; | ||
1358 | else if (event->attr.exclude_guest) | ||
1359 | arr[idx].guest &= ~ARCH_PERFMON_EVENTSEL_ENABLE; | ||
1360 | } | ||
1361 | |||
1362 | *nr = x86_pmu.num_counters; | ||
1363 | return arr; | ||
1364 | } | ||
1365 | |||
1366 | static void core_pmu_enable_event(struct perf_event *event) | ||
1367 | { | ||
1368 | if (!event->attr.exclude_host) | ||
1369 | x86_pmu_enable_event(event); | ||
1370 | } | ||
1371 | |||
1372 | static void core_pmu_enable_all(int added) | ||
1373 | { | ||
1374 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1375 | int idx; | ||
1376 | |||
1377 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | ||
1378 | struct hw_perf_event *hwc = &cpuc->events[idx]->hw; | ||
1379 | |||
1380 | if (!test_bit(idx, cpuc->active_mask) || | ||
1381 | cpuc->events[idx]->attr.exclude_host) | ||
1382 | continue; | ||
1383 | |||
1384 | __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1287 | static __initconst const struct x86_pmu core_pmu = { | 1388 | static __initconst const struct x86_pmu core_pmu = { |
1288 | .name = "core", | 1389 | .name = "core", |
1289 | .handle_irq = x86_pmu_handle_irq, | 1390 | .handle_irq = x86_pmu_handle_irq, |
1290 | .disable_all = x86_pmu_disable_all, | 1391 | .disable_all = x86_pmu_disable_all, |
1291 | .enable_all = x86_pmu_enable_all, | 1392 | .enable_all = core_pmu_enable_all, |
1292 | .enable = x86_pmu_enable_event, | 1393 | .enable = core_pmu_enable_event, |
1293 | .disable = x86_pmu_disable_event, | 1394 | .disable = x86_pmu_disable_event, |
1294 | .hw_config = x86_pmu_hw_config, | 1395 | .hw_config = x86_pmu_hw_config, |
1295 | .schedule_events = x86_schedule_events, | 1396 | .schedule_events = x86_schedule_events, |
@@ -1307,9 +1408,10 @@ static __initconst const struct x86_pmu core_pmu = { | |||
1307 | .get_event_constraints = intel_get_event_constraints, | 1408 | .get_event_constraints = intel_get_event_constraints, |
1308 | .put_event_constraints = intel_put_event_constraints, | 1409 | .put_event_constraints = intel_put_event_constraints, |
1309 | .event_constraints = intel_core_event_constraints, | 1410 | .event_constraints = intel_core_event_constraints, |
1411 | .guest_get_msrs = core_guest_get_msrs, | ||
1310 | }; | 1412 | }; |
1311 | 1413 | ||
1312 | static struct intel_shared_regs *allocate_shared_regs(int cpu) | 1414 | struct intel_shared_regs *allocate_shared_regs(int cpu) |
1313 | { | 1415 | { |
1314 | struct intel_shared_regs *regs; | 1416 | struct intel_shared_regs *regs; |
1315 | int i; | 1417 | int i; |
@@ -1362,7 +1464,7 @@ static void intel_pmu_cpu_starting(int cpu) | |||
1362 | 1464 | ||
1363 | pc = per_cpu(cpu_hw_events, i).shared_regs; | 1465 | pc = per_cpu(cpu_hw_events, i).shared_regs; |
1364 | if (pc && pc->core_id == core_id) { | 1466 | if (pc && pc->core_id == core_id) { |
1365 | kfree(cpuc->shared_regs); | 1467 | cpuc->kfree_on_online = cpuc->shared_regs; |
1366 | cpuc->shared_regs = pc; | 1468 | cpuc->shared_regs = pc; |
1367 | break; | 1469 | break; |
1368 | } | 1470 | } |
@@ -1413,6 +1515,7 @@ static __initconst const struct x86_pmu intel_pmu = { | |||
1413 | .cpu_prepare = intel_pmu_cpu_prepare, | 1515 | .cpu_prepare = intel_pmu_cpu_prepare, |
1414 | .cpu_starting = intel_pmu_cpu_starting, | 1516 | .cpu_starting = intel_pmu_cpu_starting, |
1415 | .cpu_dying = intel_pmu_cpu_dying, | 1517 | .cpu_dying = intel_pmu_cpu_dying, |
1518 | .guest_get_msrs = intel_guest_get_msrs, | ||
1416 | }; | 1519 | }; |
1417 | 1520 | ||
1418 | static void intel_clovertown_quirks(void) | 1521 | static void intel_clovertown_quirks(void) |
@@ -1441,7 +1544,7 @@ static void intel_clovertown_quirks(void) | |||
1441 | x86_pmu.pebs_constraints = NULL; | 1544 | x86_pmu.pebs_constraints = NULL; |
1442 | } | 1545 | } |
1443 | 1546 | ||
1444 | static __init int intel_pmu_init(void) | 1547 | __init int intel_pmu_init(void) |
1445 | { | 1548 | { |
1446 | union cpuid10_edx edx; | 1549 | union cpuid10_edx edx; |
1447 | union cpuid10_eax eax; | 1550 | union cpuid10_eax eax; |
@@ -1597,7 +1700,7 @@ static __init int intel_pmu_init(void) | |||
1597 | intel_pmu_lbr_init_nhm(); | 1700 | intel_pmu_lbr_init_nhm(); |
1598 | 1701 | ||
1599 | x86_pmu.event_constraints = intel_snb_event_constraints; | 1702 | x86_pmu.event_constraints = intel_snb_event_constraints; |
1600 | x86_pmu.pebs_constraints = intel_snb_pebs_events; | 1703 | x86_pmu.pebs_constraints = intel_snb_pebs_event_constraints; |
1601 | x86_pmu.extra_regs = intel_snb_extra_regs; | 1704 | x86_pmu.extra_regs = intel_snb_extra_regs; |
1602 | /* all extra regs are per-cpu when HT is on */ | 1705 | /* all extra regs are per-cpu when HT is on */ |
1603 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 1706 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
@@ -1628,16 +1731,3 @@ static __init int intel_pmu_init(void) | |||
1628 | } | 1731 | } |
1629 | return 0; | 1732 | return 0; |
1630 | } | 1733 | } |
1631 | |||
1632 | #else /* CONFIG_CPU_SUP_INTEL */ | ||
1633 | |||
1634 | static int intel_pmu_init(void) | ||
1635 | { | ||
1636 | return 0; | ||
1637 | } | ||
1638 | |||
1639 | static struct intel_shared_regs *allocate_shared_regs(int cpu) | ||
1640 | { | ||
1641 | return NULL; | ||
1642 | } | ||
1643 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 1b1ef3addcfd..c0d238f49db8 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c | |||
@@ -1,7 +1,10 @@ | |||
1 | #ifdef CONFIG_CPU_SUP_INTEL | 1 | #include <linux/bitops.h> |
2 | #include <linux/types.h> | ||
3 | #include <linux/slab.h> | ||
2 | 4 | ||
3 | /* The maximal number of PEBS events: */ | 5 | #include <asm/perf_event.h> |
4 | #define MAX_PEBS_EVENTS 4 | 6 | |
7 | #include "perf_event.h" | ||
5 | 8 | ||
6 | /* The size of a BTS record in bytes: */ | 9 | /* The size of a BTS record in bytes: */ |
7 | #define BTS_RECORD_SIZE 24 | 10 | #define BTS_RECORD_SIZE 24 |
@@ -37,24 +40,7 @@ struct pebs_record_nhm { | |||
37 | u64 status, dla, dse, lat; | 40 | u64 status, dla, dse, lat; |
38 | }; | 41 | }; |
39 | 42 | ||
40 | /* | 43 | void init_debug_store_on_cpu(int cpu) |
41 | * A debug store configuration. | ||
42 | * | ||
43 | * We only support architectures that use 64bit fields. | ||
44 | */ | ||
45 | struct debug_store { | ||
46 | u64 bts_buffer_base; | ||
47 | u64 bts_index; | ||
48 | u64 bts_absolute_maximum; | ||
49 | u64 bts_interrupt_threshold; | ||
50 | u64 pebs_buffer_base; | ||
51 | u64 pebs_index; | ||
52 | u64 pebs_absolute_maximum; | ||
53 | u64 pebs_interrupt_threshold; | ||
54 | u64 pebs_event_reset[MAX_PEBS_EVENTS]; | ||
55 | }; | ||
56 | |||
57 | static void init_debug_store_on_cpu(int cpu) | ||
58 | { | 44 | { |
59 | struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; | 45 | struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; |
60 | 46 | ||
@@ -66,7 +52,7 @@ static void init_debug_store_on_cpu(int cpu) | |||
66 | (u32)((u64)(unsigned long)ds >> 32)); | 52 | (u32)((u64)(unsigned long)ds >> 32)); |
67 | } | 53 | } |
68 | 54 | ||
69 | static void fini_debug_store_on_cpu(int cpu) | 55 | void fini_debug_store_on_cpu(int cpu) |
70 | { | 56 | { |
71 | if (!per_cpu(cpu_hw_events, cpu).ds) | 57 | if (!per_cpu(cpu_hw_events, cpu).ds) |
72 | return; | 58 | return; |
@@ -175,7 +161,7 @@ static void release_ds_buffer(int cpu) | |||
175 | kfree(ds); | 161 | kfree(ds); |
176 | } | 162 | } |
177 | 163 | ||
178 | static void release_ds_buffers(void) | 164 | void release_ds_buffers(void) |
179 | { | 165 | { |
180 | int cpu; | 166 | int cpu; |
181 | 167 | ||
@@ -194,7 +180,7 @@ static void release_ds_buffers(void) | |||
194 | put_online_cpus(); | 180 | put_online_cpus(); |
195 | } | 181 | } |
196 | 182 | ||
197 | static void reserve_ds_buffers(void) | 183 | void reserve_ds_buffers(void) |
198 | { | 184 | { |
199 | int bts_err = 0, pebs_err = 0; | 185 | int bts_err = 0, pebs_err = 0; |
200 | int cpu; | 186 | int cpu; |
@@ -260,10 +246,10 @@ static void reserve_ds_buffers(void) | |||
260 | * BTS | 246 | * BTS |
261 | */ | 247 | */ |
262 | 248 | ||
263 | static struct event_constraint bts_constraint = | 249 | struct event_constraint bts_constraint = |
264 | EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); | 250 | EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); |
265 | 251 | ||
266 | static void intel_pmu_enable_bts(u64 config) | 252 | void intel_pmu_enable_bts(u64 config) |
267 | { | 253 | { |
268 | unsigned long debugctlmsr; | 254 | unsigned long debugctlmsr; |
269 | 255 | ||
@@ -282,7 +268,7 @@ static void intel_pmu_enable_bts(u64 config) | |||
282 | update_debugctlmsr(debugctlmsr); | 268 | update_debugctlmsr(debugctlmsr); |
283 | } | 269 | } |
284 | 270 | ||
285 | static void intel_pmu_disable_bts(void) | 271 | void intel_pmu_disable_bts(void) |
286 | { | 272 | { |
287 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 273 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
288 | unsigned long debugctlmsr; | 274 | unsigned long debugctlmsr; |
@@ -299,7 +285,7 @@ static void intel_pmu_disable_bts(void) | |||
299 | update_debugctlmsr(debugctlmsr); | 285 | update_debugctlmsr(debugctlmsr); |
300 | } | 286 | } |
301 | 287 | ||
302 | static int intel_pmu_drain_bts_buffer(void) | 288 | int intel_pmu_drain_bts_buffer(void) |
303 | { | 289 | { |
304 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 290 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
305 | struct debug_store *ds = cpuc->ds; | 291 | struct debug_store *ds = cpuc->ds; |
@@ -361,7 +347,7 @@ static int intel_pmu_drain_bts_buffer(void) | |||
361 | /* | 347 | /* |
362 | * PEBS | 348 | * PEBS |
363 | */ | 349 | */ |
364 | static struct event_constraint intel_core2_pebs_event_constraints[] = { | 350 | struct event_constraint intel_core2_pebs_event_constraints[] = { |
365 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ | 351 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ |
366 | INTEL_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ | 352 | INTEL_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ |
367 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ | 353 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ |
@@ -370,14 +356,14 @@ static struct event_constraint intel_core2_pebs_event_constraints[] = { | |||
370 | EVENT_CONSTRAINT_END | 356 | EVENT_CONSTRAINT_END |
371 | }; | 357 | }; |
372 | 358 | ||
373 | static struct event_constraint intel_atom_pebs_event_constraints[] = { | 359 | struct event_constraint intel_atom_pebs_event_constraints[] = { |
374 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ | 360 | INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ |
375 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */ | 361 | INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */ |
376 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ | 362 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ |
377 | EVENT_CONSTRAINT_END | 363 | EVENT_CONSTRAINT_END |
378 | }; | 364 | }; |
379 | 365 | ||
380 | static struct event_constraint intel_nehalem_pebs_event_constraints[] = { | 366 | struct event_constraint intel_nehalem_pebs_event_constraints[] = { |
381 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ | 367 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ |
382 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 368 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
383 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 369 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
@@ -392,7 +378,7 @@ static struct event_constraint intel_nehalem_pebs_event_constraints[] = { | |||
392 | EVENT_CONSTRAINT_END | 378 | EVENT_CONSTRAINT_END |
393 | }; | 379 | }; |
394 | 380 | ||
395 | static struct event_constraint intel_westmere_pebs_event_constraints[] = { | 381 | struct event_constraint intel_westmere_pebs_event_constraints[] = { |
396 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ | 382 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ |
397 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 383 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
398 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 384 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
@@ -407,7 +393,7 @@ static struct event_constraint intel_westmere_pebs_event_constraints[] = { | |||
407 | EVENT_CONSTRAINT_END | 393 | EVENT_CONSTRAINT_END |
408 | }; | 394 | }; |
409 | 395 | ||
410 | static struct event_constraint intel_snb_pebs_events[] = { | 396 | struct event_constraint intel_snb_pebs_event_constraints[] = { |
411 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ | 397 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ |
412 | INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */ | 398 | INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */ |
413 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ | 399 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ |
@@ -428,8 +414,7 @@ static struct event_constraint intel_snb_pebs_events[] = { | |||
428 | EVENT_CONSTRAINT_END | 414 | EVENT_CONSTRAINT_END |
429 | }; | 415 | }; |
430 | 416 | ||
431 | static struct event_constraint * | 417 | struct event_constraint *intel_pebs_constraints(struct perf_event *event) |
432 | intel_pebs_constraints(struct perf_event *event) | ||
433 | { | 418 | { |
434 | struct event_constraint *c; | 419 | struct event_constraint *c; |
435 | 420 | ||
@@ -446,7 +431,7 @@ intel_pebs_constraints(struct perf_event *event) | |||
446 | return &emptyconstraint; | 431 | return &emptyconstraint; |
447 | } | 432 | } |
448 | 433 | ||
449 | static void intel_pmu_pebs_enable(struct perf_event *event) | 434 | void intel_pmu_pebs_enable(struct perf_event *event) |
450 | { | 435 | { |
451 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 436 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
452 | struct hw_perf_event *hwc = &event->hw; | 437 | struct hw_perf_event *hwc = &event->hw; |
@@ -460,7 +445,7 @@ static void intel_pmu_pebs_enable(struct perf_event *event) | |||
460 | intel_pmu_lbr_enable(event); | 445 | intel_pmu_lbr_enable(event); |
461 | } | 446 | } |
462 | 447 | ||
463 | static void intel_pmu_pebs_disable(struct perf_event *event) | 448 | void intel_pmu_pebs_disable(struct perf_event *event) |
464 | { | 449 | { |
465 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 450 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
466 | struct hw_perf_event *hwc = &event->hw; | 451 | struct hw_perf_event *hwc = &event->hw; |
@@ -475,7 +460,7 @@ static void intel_pmu_pebs_disable(struct perf_event *event) | |||
475 | intel_pmu_lbr_disable(event); | 460 | intel_pmu_lbr_disable(event); |
476 | } | 461 | } |
477 | 462 | ||
478 | static void intel_pmu_pebs_enable_all(void) | 463 | void intel_pmu_pebs_enable_all(void) |
479 | { | 464 | { |
480 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 465 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
481 | 466 | ||
@@ -483,7 +468,7 @@ static void intel_pmu_pebs_enable_all(void) | |||
483 | wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); | 468 | wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); |
484 | } | 469 | } |
485 | 470 | ||
486 | static void intel_pmu_pebs_disable_all(void) | 471 | void intel_pmu_pebs_disable_all(void) |
487 | { | 472 | { |
488 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 473 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
489 | 474 | ||
@@ -576,8 +561,6 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) | |||
576 | return 0; | 561 | return 0; |
577 | } | 562 | } |
578 | 563 | ||
579 | static int intel_pmu_save_and_restart(struct perf_event *event); | ||
580 | |||
581 | static void __intel_pmu_pebs_event(struct perf_event *event, | 564 | static void __intel_pmu_pebs_event(struct perf_event *event, |
582 | struct pt_regs *iregs, void *__pebs) | 565 | struct pt_regs *iregs, void *__pebs) |
583 | { | 566 | { |
@@ -716,7 +699,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) | |||
716 | * BTS, PEBS probe and setup | 699 | * BTS, PEBS probe and setup |
717 | */ | 700 | */ |
718 | 701 | ||
719 | static void intel_ds_init(void) | 702 | void intel_ds_init(void) |
720 | { | 703 | { |
721 | /* | 704 | /* |
722 | * No support for 32bit formats | 705 | * No support for 32bit formats |
@@ -749,15 +732,3 @@ static void intel_ds_init(void) | |||
749 | } | 732 | } |
750 | } | 733 | } |
751 | } | 734 | } |
752 | |||
753 | #else /* CONFIG_CPU_SUP_INTEL */ | ||
754 | |||
755 | static void reserve_ds_buffers(void) | ||
756 | { | ||
757 | } | ||
758 | |||
759 | static void release_ds_buffers(void) | ||
760 | { | ||
761 | } | ||
762 | |||
763 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index d202c1bece1a..3fab3de3ce96 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c | |||
@@ -1,4 +1,10 @@ | |||
1 | #ifdef CONFIG_CPU_SUP_INTEL | 1 | #include <linux/perf_event.h> |
2 | #include <linux/types.h> | ||
3 | |||
4 | #include <asm/perf_event.h> | ||
5 | #include <asm/msr.h> | ||
6 | |||
7 | #include "perf_event.h" | ||
2 | 8 | ||
3 | enum { | 9 | enum { |
4 | LBR_FORMAT_32 = 0x00, | 10 | LBR_FORMAT_32 = 0x00, |
@@ -48,7 +54,7 @@ static void intel_pmu_lbr_reset_64(void) | |||
48 | } | 54 | } |
49 | } | 55 | } |
50 | 56 | ||
51 | static void intel_pmu_lbr_reset(void) | 57 | void intel_pmu_lbr_reset(void) |
52 | { | 58 | { |
53 | if (!x86_pmu.lbr_nr) | 59 | if (!x86_pmu.lbr_nr) |
54 | return; | 60 | return; |
@@ -59,7 +65,7 @@ static void intel_pmu_lbr_reset(void) | |||
59 | intel_pmu_lbr_reset_64(); | 65 | intel_pmu_lbr_reset_64(); |
60 | } | 66 | } |
61 | 67 | ||
62 | static void intel_pmu_lbr_enable(struct perf_event *event) | 68 | void intel_pmu_lbr_enable(struct perf_event *event) |
63 | { | 69 | { |
64 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 70 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
65 | 71 | ||
@@ -81,7 +87,7 @@ static void intel_pmu_lbr_enable(struct perf_event *event) | |||
81 | cpuc->lbr_users++; | 87 | cpuc->lbr_users++; |
82 | } | 88 | } |
83 | 89 | ||
84 | static void intel_pmu_lbr_disable(struct perf_event *event) | 90 | void intel_pmu_lbr_disable(struct perf_event *event) |
85 | { | 91 | { |
86 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 92 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
87 | 93 | ||
@@ -95,7 +101,7 @@ static void intel_pmu_lbr_disable(struct perf_event *event) | |||
95 | __intel_pmu_lbr_disable(); | 101 | __intel_pmu_lbr_disable(); |
96 | } | 102 | } |
97 | 103 | ||
98 | static void intel_pmu_lbr_enable_all(void) | 104 | void intel_pmu_lbr_enable_all(void) |
99 | { | 105 | { |
100 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 106 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
101 | 107 | ||
@@ -103,7 +109,7 @@ static void intel_pmu_lbr_enable_all(void) | |||
103 | __intel_pmu_lbr_enable(); | 109 | __intel_pmu_lbr_enable(); |
104 | } | 110 | } |
105 | 111 | ||
106 | static void intel_pmu_lbr_disable_all(void) | 112 | void intel_pmu_lbr_disable_all(void) |
107 | { | 113 | { |
108 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 114 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
109 | 115 | ||
@@ -178,7 +184,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) | |||
178 | cpuc->lbr_stack.nr = i; | 184 | cpuc->lbr_stack.nr = i; |
179 | } | 185 | } |
180 | 186 | ||
181 | static void intel_pmu_lbr_read(void) | 187 | void intel_pmu_lbr_read(void) |
182 | { | 188 | { |
183 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 189 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
184 | 190 | ||
@@ -191,7 +197,7 @@ static void intel_pmu_lbr_read(void) | |||
191 | intel_pmu_lbr_read_64(cpuc); | 197 | intel_pmu_lbr_read_64(cpuc); |
192 | } | 198 | } |
193 | 199 | ||
194 | static void intel_pmu_lbr_init_core(void) | 200 | void intel_pmu_lbr_init_core(void) |
195 | { | 201 | { |
196 | x86_pmu.lbr_nr = 4; | 202 | x86_pmu.lbr_nr = 4; |
197 | x86_pmu.lbr_tos = 0x01c9; | 203 | x86_pmu.lbr_tos = 0x01c9; |
@@ -199,7 +205,7 @@ static void intel_pmu_lbr_init_core(void) | |||
199 | x86_pmu.lbr_to = 0x60; | 205 | x86_pmu.lbr_to = 0x60; |
200 | } | 206 | } |
201 | 207 | ||
202 | static void intel_pmu_lbr_init_nhm(void) | 208 | void intel_pmu_lbr_init_nhm(void) |
203 | { | 209 | { |
204 | x86_pmu.lbr_nr = 16; | 210 | x86_pmu.lbr_nr = 16; |
205 | x86_pmu.lbr_tos = 0x01c9; | 211 | x86_pmu.lbr_tos = 0x01c9; |
@@ -207,12 +213,10 @@ static void intel_pmu_lbr_init_nhm(void) | |||
207 | x86_pmu.lbr_to = 0x6c0; | 213 | x86_pmu.lbr_to = 0x6c0; |
208 | } | 214 | } |
209 | 215 | ||
210 | static void intel_pmu_lbr_init_atom(void) | 216 | void intel_pmu_lbr_init_atom(void) |
211 | { | 217 | { |
212 | x86_pmu.lbr_nr = 8; | 218 | x86_pmu.lbr_nr = 8; |
213 | x86_pmu.lbr_tos = 0x01c9; | 219 | x86_pmu.lbr_tos = 0x01c9; |
214 | x86_pmu.lbr_from = 0x40; | 220 | x86_pmu.lbr_from = 0x40; |
215 | x86_pmu.lbr_to = 0x60; | 221 | x86_pmu.lbr_to = 0x60; |
216 | } | 222 | } |
217 | |||
218 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 7809d2bcb209..492bf1358a7c 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c | |||
@@ -7,9 +7,13 @@ | |||
7 | * For licencing details see kernel-base/COPYING | 7 | * For licencing details see kernel-base/COPYING |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #ifdef CONFIG_CPU_SUP_INTEL | 10 | #include <linux/perf_event.h> |
11 | 11 | ||
12 | #include <asm/perf_event_p4.h> | 12 | #include <asm/perf_event_p4.h> |
13 | #include <asm/hardirq.h> | ||
14 | #include <asm/apic.h> | ||
15 | |||
16 | #include "perf_event.h" | ||
13 | 17 | ||
14 | #define P4_CNTR_LIMIT 3 | 18 | #define P4_CNTR_LIMIT 3 |
15 | /* | 19 | /* |
@@ -1303,7 +1307,7 @@ static __initconst const struct x86_pmu p4_pmu = { | |||
1303 | .perfctr_second_write = 1, | 1307 | .perfctr_second_write = 1, |
1304 | }; | 1308 | }; |
1305 | 1309 | ||
1306 | static __init int p4_pmu_init(void) | 1310 | __init int p4_pmu_init(void) |
1307 | { | 1311 | { |
1308 | unsigned int low, high; | 1312 | unsigned int low, high; |
1309 | 1313 | ||
@@ -1326,5 +1330,3 @@ static __init int p4_pmu_init(void) | |||
1326 | 1330 | ||
1327 | return 0; | 1331 | return 0; |
1328 | } | 1332 | } |
1329 | |||
1330 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index 20c097e33860..c7181befecde 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c | |||
@@ -1,4 +1,7 @@ | |||
1 | #ifdef CONFIG_CPU_SUP_INTEL | 1 | #include <linux/perf_event.h> |
2 | #include <linux/types.h> | ||
3 | |||
4 | #include "perf_event.h" | ||
2 | 5 | ||
3 | /* | 6 | /* |
4 | * Not sure about some of these | 7 | * Not sure about some of these |
@@ -114,7 +117,7 @@ static __initconst const struct x86_pmu p6_pmu = { | |||
114 | .event_constraints = p6_event_constraints, | 117 | .event_constraints = p6_event_constraints, |
115 | }; | 118 | }; |
116 | 119 | ||
117 | static __init int p6_pmu_init(void) | 120 | __init int p6_pmu_init(void) |
118 | { | 121 | { |
119 | switch (boot_cpu_data.x86_model) { | 122 | switch (boot_cpu_data.x86_model) { |
120 | case 1: | 123 | case 1: |
@@ -138,5 +141,3 @@ static __init int p6_pmu_init(void) | |||
138 | 141 | ||
139 | return 0; | 142 | return 0; |
140 | } | 143 | } |
141 | |||
142 | #endif /* CONFIG_CPU_SUP_INTEL */ | ||
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 764c7c2b1811..13ad89971d47 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c | |||
@@ -32,15 +32,12 @@ int in_crash_kexec; | |||
32 | 32 | ||
33 | #if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) | 33 | #if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) |
34 | 34 | ||
35 | static void kdump_nmi_callback(int cpu, struct die_args *args) | 35 | static void kdump_nmi_callback(int cpu, struct pt_regs *regs) |
36 | { | 36 | { |
37 | struct pt_regs *regs; | ||
38 | #ifdef CONFIG_X86_32 | 37 | #ifdef CONFIG_X86_32 |
39 | struct pt_regs fixed_regs; | 38 | struct pt_regs fixed_regs; |
40 | #endif | 39 | #endif |
41 | 40 | ||
42 | regs = args->regs; | ||
43 | |||
44 | #ifdef CONFIG_X86_32 | 41 | #ifdef CONFIG_X86_32 |
45 | if (!user_mode_vm(regs)) { | 42 | if (!user_mode_vm(regs)) { |
46 | crash_fixup_ss_esp(&fixed_regs, regs); | 43 | crash_fixup_ss_esp(&fixed_regs, regs); |
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c index 3fee346ef545..cacdd46d184d 100644 --- a/arch/x86/kernel/jump_label.c +++ b/arch/x86/kernel/jump_label.c | |||
@@ -42,7 +42,7 @@ void arch_jump_label_transform(struct jump_entry *entry, | |||
42 | put_online_cpus(); | 42 | put_online_cpus(); |
43 | } | 43 | } |
44 | 44 | ||
45 | void arch_jump_label_text_poke_early(jump_label_t addr) | 45 | void __init_or_module arch_jump_label_text_poke_early(jump_label_t addr) |
46 | { | 46 | { |
47 | text_poke_early((void *)addr, ideal_nops[NOP_ATOMIC5], | 47 | text_poke_early((void *)addr, ideal_nops[NOP_ATOMIC5], |
48 | JUMP_LABEL_NOP_SIZE); | 48 | JUMP_LABEL_NOP_SIZE); |
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 00354d4919a9..faba5771acad 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c | |||
@@ -511,28 +511,37 @@ single_step_cont(struct pt_regs *regs, struct die_args *args) | |||
511 | 511 | ||
512 | static int was_in_debug_nmi[NR_CPUS]; | 512 | static int was_in_debug_nmi[NR_CPUS]; |
513 | 513 | ||
514 | static int __kgdb_notify(struct die_args *args, unsigned long cmd) | 514 | static int kgdb_nmi_handler(unsigned int cmd, struct pt_regs *regs) |
515 | { | 515 | { |
516 | struct pt_regs *regs = args->regs; | ||
517 | |||
518 | switch (cmd) { | 516 | switch (cmd) { |
519 | case DIE_NMI: | 517 | case NMI_LOCAL: |
520 | if (atomic_read(&kgdb_active) != -1) { | 518 | if (atomic_read(&kgdb_active) != -1) { |
521 | /* KGDB CPU roundup */ | 519 | /* KGDB CPU roundup */ |
522 | kgdb_nmicallback(raw_smp_processor_id(), regs); | 520 | kgdb_nmicallback(raw_smp_processor_id(), regs); |
523 | was_in_debug_nmi[raw_smp_processor_id()] = 1; | 521 | was_in_debug_nmi[raw_smp_processor_id()] = 1; |
524 | touch_nmi_watchdog(); | 522 | touch_nmi_watchdog(); |
525 | return NOTIFY_STOP; | 523 | return NMI_HANDLED; |
526 | } | 524 | } |
527 | return NOTIFY_DONE; | 525 | break; |
528 | 526 | ||
529 | case DIE_NMIUNKNOWN: | 527 | case NMI_UNKNOWN: |
530 | if (was_in_debug_nmi[raw_smp_processor_id()]) { | 528 | if (was_in_debug_nmi[raw_smp_processor_id()]) { |
531 | was_in_debug_nmi[raw_smp_processor_id()] = 0; | 529 | was_in_debug_nmi[raw_smp_processor_id()] = 0; |
532 | return NOTIFY_STOP; | 530 | return NMI_HANDLED; |
533 | } | 531 | } |
534 | return NOTIFY_DONE; | 532 | break; |
533 | default: | ||
534 | /* do nothing */ | ||
535 | break; | ||
536 | } | ||
537 | return NMI_DONE; | ||
538 | } | ||
539 | |||
540 | static int __kgdb_notify(struct die_args *args, unsigned long cmd) | ||
541 | { | ||
542 | struct pt_regs *regs = args->regs; | ||
535 | 543 | ||
544 | switch (cmd) { | ||
536 | case DIE_DEBUG: | 545 | case DIE_DEBUG: |
537 | if (atomic_read(&kgdb_cpu_doing_single_step) != -1) { | 546 | if (atomic_read(&kgdb_cpu_doing_single_step) != -1) { |
538 | if (user_mode(regs)) | 547 | if (user_mode(regs)) |
@@ -590,11 +599,6 @@ kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) | |||
590 | 599 | ||
591 | static struct notifier_block kgdb_notifier = { | 600 | static struct notifier_block kgdb_notifier = { |
592 | .notifier_call = kgdb_notify, | 601 | .notifier_call = kgdb_notify, |
593 | |||
594 | /* | ||
595 | * Lowest-prio notifier priority, we want to be notified last: | ||
596 | */ | ||
597 | .priority = NMI_LOCAL_LOW_PRIOR, | ||
598 | }; | 602 | }; |
599 | 603 | ||
600 | /** | 604 | /** |
@@ -605,7 +609,31 @@ static struct notifier_block kgdb_notifier = { | |||
605 | */ | 609 | */ |
606 | int kgdb_arch_init(void) | 610 | int kgdb_arch_init(void) |
607 | { | 611 | { |
608 | return register_die_notifier(&kgdb_notifier); | 612 | int retval; |
613 | |||
614 | retval = register_die_notifier(&kgdb_notifier); | ||
615 | if (retval) | ||
616 | goto out; | ||
617 | |||
618 | retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, | ||
619 | 0, "kgdb"); | ||
620 | if (retval) | ||
621 | goto out1; | ||
622 | |||
623 | retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, | ||
624 | 0, "kgdb"); | ||
625 | |||
626 | if (retval) | ||
627 | goto out2; | ||
628 | |||
629 | return retval; | ||
630 | |||
631 | out2: | ||
632 | unregister_nmi_handler(NMI_LOCAL, "kgdb"); | ||
633 | out1: | ||
634 | unregister_die_notifier(&kgdb_notifier); | ||
635 | out: | ||
636 | return retval; | ||
609 | } | 637 | } |
610 | 638 | ||
611 | static void kgdb_hw_overflow_handler(struct perf_event *event, | 639 | static void kgdb_hw_overflow_handler(struct perf_event *event, |
@@ -673,6 +701,8 @@ void kgdb_arch_exit(void) | |||
673 | breakinfo[i].pev = NULL; | 701 | breakinfo[i].pev = NULL; |
674 | } | 702 | } |
675 | } | 703 | } |
704 | unregister_nmi_handler(NMI_UNKNOWN, "kgdb"); | ||
705 | unregister_nmi_handler(NMI_LOCAL, "kgdb"); | ||
676 | unregister_die_notifier(&kgdb_notifier); | 706 | unregister_die_notifier(&kgdb_notifier); |
677 | } | 707 | } |
678 | 708 | ||
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 794bc95134cd..7da647d8b64c 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -75,10 +75,11 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | |||
75 | /* | 75 | /* |
76 | * Undefined/reserved opcodes, conditional jump, Opcode Extension | 76 | * Undefined/reserved opcodes, conditional jump, Opcode Extension |
77 | * Groups, and some special opcodes can not boost. | 77 | * Groups, and some special opcodes can not boost. |
78 | * This is non-const to keep gcc from statically optimizing it out, as | 78 | * This is non-const and volatile to keep gcc from statically |
79 | * variable_test_bit makes gcc think only *(unsigned long*) is used. | 79 | * optimizing it out, as variable_test_bit makes gcc think only |
80 | * *(unsigned long*) is used. | ||
80 | */ | 81 | */ |
81 | static u32 twobyte_is_boostable[256 / 32] = { | 82 | static volatile u32 twobyte_is_boostable[256 / 32] = { |
82 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ | 83 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
83 | /* ---------------------------------------------- */ | 84 | /* ---------------------------------------------- */ |
84 | W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* 00 */ | 85 | W(0x00, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0) | /* 00 */ |
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c new file mode 100644 index 000000000000..7ec5bd140b87 --- /dev/null +++ b/arch/x86/kernel/nmi.c | |||
@@ -0,0 +1,433 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
3 | * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs | ||
4 | * Copyright (C) 2011 Don Zickus Red Hat, Inc. | ||
5 | * | ||
6 | * Pentium III FXSR, SSE support | ||
7 | * Gareth Hughes <gareth@valinux.com>, May 2000 | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * Handle hardware traps and faults. | ||
12 | */ | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/kprobes.h> | ||
15 | #include <linux/kdebug.h> | ||
16 | #include <linux/nmi.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/hardirq.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include <linux/mca.h> | ||
22 | |||
23 | #if defined(CONFIG_EDAC) | ||
24 | #include <linux/edac.h> | ||
25 | #endif | ||
26 | |||
27 | #include <linux/atomic.h> | ||
28 | #include <asm/traps.h> | ||
29 | #include <asm/mach_traps.h> | ||
30 | #include <asm/nmi.h> | ||
31 | |||
32 | #define NMI_MAX_NAMELEN 16 | ||
33 | struct nmiaction { | ||
34 | struct list_head list; | ||
35 | nmi_handler_t handler; | ||
36 | unsigned int flags; | ||
37 | char *name; | ||
38 | }; | ||
39 | |||
40 | struct nmi_desc { | ||
41 | spinlock_t lock; | ||
42 | struct list_head head; | ||
43 | }; | ||
44 | |||
45 | static struct nmi_desc nmi_desc[NMI_MAX] = | ||
46 | { | ||
47 | { | ||
48 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[0].lock), | ||
49 | .head = LIST_HEAD_INIT(nmi_desc[0].head), | ||
50 | }, | ||
51 | { | ||
52 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock), | ||
53 | .head = LIST_HEAD_INIT(nmi_desc[1].head), | ||
54 | }, | ||
55 | |||
56 | }; | ||
57 | |||
58 | struct nmi_stats { | ||
59 | unsigned int normal; | ||
60 | unsigned int unknown; | ||
61 | unsigned int external; | ||
62 | unsigned int swallow; | ||
63 | }; | ||
64 | |||
65 | static DEFINE_PER_CPU(struct nmi_stats, nmi_stats); | ||
66 | |||
67 | static int ignore_nmis; | ||
68 | |||
69 | int unknown_nmi_panic; | ||
70 | /* | ||
71 | * Prevent NMI reason port (0x61) being accessed simultaneously, can | ||
72 | * only be used in NMI handler. | ||
73 | */ | ||
74 | static DEFINE_RAW_SPINLOCK(nmi_reason_lock); | ||
75 | |||
76 | static int __init setup_unknown_nmi_panic(char *str) | ||
77 | { | ||
78 | unknown_nmi_panic = 1; | ||
79 | return 1; | ||
80 | } | ||
81 | __setup("unknown_nmi_panic", setup_unknown_nmi_panic); | ||
82 | |||
83 | #define nmi_to_desc(type) (&nmi_desc[type]) | ||
84 | |||
85 | static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b) | ||
86 | { | ||
87 | struct nmi_desc *desc = nmi_to_desc(type); | ||
88 | struct nmiaction *a; | ||
89 | int handled=0; | ||
90 | |||
91 | rcu_read_lock(); | ||
92 | |||
93 | /* | ||
94 | * NMIs are edge-triggered, which means if you have enough | ||
95 | * of them concurrently, you can lose some because only one | ||
96 | * can be latched at any given time. Walk the whole list | ||
97 | * to handle those situations. | ||
98 | */ | ||
99 | list_for_each_entry_rcu(a, &desc->head, list) | ||
100 | handled += a->handler(type, regs); | ||
101 | |||
102 | rcu_read_unlock(); | ||
103 | |||
104 | /* return total number of NMI events handled */ | ||
105 | return handled; | ||
106 | } | ||
107 | |||
108 | static int __setup_nmi(unsigned int type, struct nmiaction *action) | ||
109 | { | ||
110 | struct nmi_desc *desc = nmi_to_desc(type); | ||
111 | unsigned long flags; | ||
112 | |||
113 | spin_lock_irqsave(&desc->lock, flags); | ||
114 | |||
115 | /* | ||
116 | * most handlers of type NMI_UNKNOWN never return because | ||
117 | * they just assume the NMI is theirs. Just a sanity check | ||
118 | * to manage expectations | ||
119 | */ | ||
120 | WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head)); | ||
121 | |||
122 | /* | ||
123 | * some handlers need to be executed first otherwise a fake | ||
124 | * event confuses some handlers (kdump uses this flag) | ||
125 | */ | ||
126 | if (action->flags & NMI_FLAG_FIRST) | ||
127 | list_add_rcu(&action->list, &desc->head); | ||
128 | else | ||
129 | list_add_tail_rcu(&action->list, &desc->head); | ||
130 | |||
131 | spin_unlock_irqrestore(&desc->lock, flags); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static struct nmiaction *__free_nmi(unsigned int type, const char *name) | ||
136 | { | ||
137 | struct nmi_desc *desc = nmi_to_desc(type); | ||
138 | struct nmiaction *n; | ||
139 | unsigned long flags; | ||
140 | |||
141 | spin_lock_irqsave(&desc->lock, flags); | ||
142 | |||
143 | list_for_each_entry_rcu(n, &desc->head, list) { | ||
144 | /* | ||
145 | * the name passed in to describe the nmi handler | ||
146 | * is used as the lookup key | ||
147 | */ | ||
148 | if (!strcmp(n->name, name)) { | ||
149 | WARN(in_nmi(), | ||
150 | "Trying to free NMI (%s) from NMI context!\n", n->name); | ||
151 | list_del_rcu(&n->list); | ||
152 | break; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | spin_unlock_irqrestore(&desc->lock, flags); | ||
157 | synchronize_rcu(); | ||
158 | return (n); | ||
159 | } | ||
160 | |||
161 | int register_nmi_handler(unsigned int type, nmi_handler_t handler, | ||
162 | unsigned long nmiflags, const char *devname) | ||
163 | { | ||
164 | struct nmiaction *action; | ||
165 | int retval = -ENOMEM; | ||
166 | |||
167 | if (!handler) | ||
168 | return -EINVAL; | ||
169 | |||
170 | action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL); | ||
171 | if (!action) | ||
172 | goto fail_action; | ||
173 | |||
174 | action->handler = handler; | ||
175 | action->flags = nmiflags; | ||
176 | action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL); | ||
177 | if (!action->name) | ||
178 | goto fail_action_name; | ||
179 | |||
180 | retval = __setup_nmi(type, action); | ||
181 | |||
182 | if (retval) | ||
183 | goto fail_setup_nmi; | ||
184 | |||
185 | return retval; | ||
186 | |||
187 | fail_setup_nmi: | ||
188 | kfree(action->name); | ||
189 | fail_action_name: | ||
190 | kfree(action); | ||
191 | fail_action: | ||
192 | |||
193 | return retval; | ||
194 | } | ||
195 | EXPORT_SYMBOL_GPL(register_nmi_handler); | ||
196 | |||
197 | void unregister_nmi_handler(unsigned int type, const char *name) | ||
198 | { | ||
199 | struct nmiaction *a; | ||
200 | |||
201 | a = __free_nmi(type, name); | ||
202 | if (a) { | ||
203 | kfree(a->name); | ||
204 | kfree(a); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | EXPORT_SYMBOL_GPL(unregister_nmi_handler); | ||
209 | |||
210 | static notrace __kprobes void | ||
211 | pci_serr_error(unsigned char reason, struct pt_regs *regs) | ||
212 | { | ||
213 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", | ||
214 | reason, smp_processor_id()); | ||
215 | |||
216 | /* | ||
217 | * On some machines, PCI SERR line is used to report memory | ||
218 | * errors. EDAC makes use of it. | ||
219 | */ | ||
220 | #if defined(CONFIG_EDAC) | ||
221 | if (edac_handler_set()) { | ||
222 | edac_atomic_assert_error(); | ||
223 | return; | ||
224 | } | ||
225 | #endif | ||
226 | |||
227 | if (panic_on_unrecovered_nmi) | ||
228 | panic("NMI: Not continuing"); | ||
229 | |||
230 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
231 | |||
232 | /* Clear and disable the PCI SERR error line. */ | ||
233 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR; | ||
234 | outb(reason, NMI_REASON_PORT); | ||
235 | } | ||
236 | |||
237 | static notrace __kprobes void | ||
238 | io_check_error(unsigned char reason, struct pt_regs *regs) | ||
239 | { | ||
240 | unsigned long i; | ||
241 | |||
242 | pr_emerg( | ||
243 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", | ||
244 | reason, smp_processor_id()); | ||
245 | show_registers(regs); | ||
246 | |||
247 | if (panic_on_io_nmi) | ||
248 | panic("NMI IOCK error: Not continuing"); | ||
249 | |||
250 | /* Re-enable the IOCK line, wait for a few seconds */ | ||
251 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_IOCHK; | ||
252 | outb(reason, NMI_REASON_PORT); | ||
253 | |||
254 | i = 20000; | ||
255 | while (--i) { | ||
256 | touch_nmi_watchdog(); | ||
257 | udelay(100); | ||
258 | } | ||
259 | |||
260 | reason &= ~NMI_REASON_CLEAR_IOCHK; | ||
261 | outb(reason, NMI_REASON_PORT); | ||
262 | } | ||
263 | |||
264 | static notrace __kprobes void | ||
265 | unknown_nmi_error(unsigned char reason, struct pt_regs *regs) | ||
266 | { | ||
267 | int handled; | ||
268 | |||
269 | /* | ||
270 | * Use 'false' as back-to-back NMIs are dealt with one level up. | ||
271 | * Of course this makes having multiple 'unknown' handlers useless | ||
272 | * as only the first one is ever run (unless it can actually determine | ||
273 | * if it caused the NMI) | ||
274 | */ | ||
275 | handled = nmi_handle(NMI_UNKNOWN, regs, false); | ||
276 | if (handled) { | ||
277 | __this_cpu_add(nmi_stats.unknown, handled); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | __this_cpu_add(nmi_stats.unknown, 1); | ||
282 | |||
283 | #ifdef CONFIG_MCA | ||
284 | /* | ||
285 | * Might actually be able to figure out what the guilty party | ||
286 | * is: | ||
287 | */ | ||
288 | if (MCA_bus) { | ||
289 | mca_handle_nmi(); | ||
290 | return; | ||
291 | } | ||
292 | #endif | ||
293 | pr_emerg("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", | ||
294 | reason, smp_processor_id()); | ||
295 | |||
296 | pr_emerg("Do you have a strange power saving mode enabled?\n"); | ||
297 | if (unknown_nmi_panic || panic_on_unrecovered_nmi) | ||
298 | panic("NMI: Not continuing"); | ||
299 | |||
300 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
301 | } | ||
302 | |||
303 | static DEFINE_PER_CPU(bool, swallow_nmi); | ||
304 | static DEFINE_PER_CPU(unsigned long, last_nmi_rip); | ||
305 | |||
306 | static notrace __kprobes void default_do_nmi(struct pt_regs *regs) | ||
307 | { | ||
308 | unsigned char reason = 0; | ||
309 | int handled; | ||
310 | bool b2b = false; | ||
311 | |||
312 | /* | ||
313 | * CPU-specific NMI must be processed before non-CPU-specific | ||
314 | * NMI, otherwise we may lose it, because the CPU-specific | ||
315 | * NMI can not be detected/processed on other CPUs. | ||
316 | */ | ||
317 | |||
318 | /* | ||
319 | * Back-to-back NMIs are interesting because they can either | ||
320 | * be two NMI or more than two NMIs (any thing over two is dropped | ||
321 | * due to NMI being edge-triggered). If this is the second half | ||
322 | * of the back-to-back NMI, assume we dropped things and process | ||
323 | * more handlers. Otherwise reset the 'swallow' NMI behaviour | ||
324 | */ | ||
325 | if (regs->ip == __this_cpu_read(last_nmi_rip)) | ||
326 | b2b = true; | ||
327 | else | ||
328 | __this_cpu_write(swallow_nmi, false); | ||
329 | |||
330 | __this_cpu_write(last_nmi_rip, regs->ip); | ||
331 | |||
332 | handled = nmi_handle(NMI_LOCAL, regs, b2b); | ||
333 | __this_cpu_add(nmi_stats.normal, handled); | ||
334 | if (handled) { | ||
335 | /* | ||
336 | * There are cases when a NMI handler handles multiple | ||
337 | * events in the current NMI. One of these events may | ||
338 | * be queued for in the next NMI. Because the event is | ||
339 | * already handled, the next NMI will result in an unknown | ||
340 | * NMI. Instead lets flag this for a potential NMI to | ||
341 | * swallow. | ||
342 | */ | ||
343 | if (handled > 1) | ||
344 | __this_cpu_write(swallow_nmi, true); | ||
345 | return; | ||
346 | } | ||
347 | |||
348 | /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ | ||
349 | raw_spin_lock(&nmi_reason_lock); | ||
350 | reason = get_nmi_reason(); | ||
351 | |||
352 | if (reason & NMI_REASON_MASK) { | ||
353 | if (reason & NMI_REASON_SERR) | ||
354 | pci_serr_error(reason, regs); | ||
355 | else if (reason & NMI_REASON_IOCHK) | ||
356 | io_check_error(reason, regs); | ||
357 | #ifdef CONFIG_X86_32 | ||
358 | /* | ||
359 | * Reassert NMI in case it became active | ||
360 | * meanwhile as it's edge-triggered: | ||
361 | */ | ||
362 | reassert_nmi(); | ||
363 | #endif | ||
364 | __this_cpu_add(nmi_stats.external, 1); | ||
365 | raw_spin_unlock(&nmi_reason_lock); | ||
366 | return; | ||
367 | } | ||
368 | raw_spin_unlock(&nmi_reason_lock); | ||
369 | |||
370 | /* | ||
371 | * Only one NMI can be latched at a time. To handle | ||
372 | * this we may process multiple nmi handlers at once to | ||
373 | * cover the case where an NMI is dropped. The downside | ||
374 | * to this approach is we may process an NMI prematurely, | ||
375 | * while its real NMI is sitting latched. This will cause | ||
376 | * an unknown NMI on the next run of the NMI processing. | ||
377 | * | ||
378 | * We tried to flag that condition above, by setting the | ||
379 | * swallow_nmi flag when we process more than one event. | ||
380 | * This condition is also only present on the second half | ||
381 | * of a back-to-back NMI, so we flag that condition too. | ||
382 | * | ||
383 | * If both are true, we assume we already processed this | ||
384 | * NMI previously and we swallow it. Otherwise we reset | ||
385 | * the logic. | ||
386 | * | ||
387 | * There are scenarios where we may accidentally swallow | ||
388 | * a 'real' unknown NMI. For example, while processing | ||
389 | * a perf NMI another perf NMI comes in along with a | ||
390 | * 'real' unknown NMI. These two NMIs get combined into | ||
391 | * one (as descibed above). When the next NMI gets | ||
392 | * processed, it will be flagged by perf as handled, but | ||
393 | * noone will know that there was a 'real' unknown NMI sent | ||
394 | * also. As a result it gets swallowed. Or if the first | ||
395 | * perf NMI returns two events handled then the second | ||
396 | * NMI will get eaten by the logic below, again losing a | ||
397 | * 'real' unknown NMI. But this is the best we can do | ||
398 | * for now. | ||
399 | */ | ||
400 | if (b2b && __this_cpu_read(swallow_nmi)) | ||
401 | __this_cpu_add(nmi_stats.swallow, 1); | ||
402 | else | ||
403 | unknown_nmi_error(reason, regs); | ||
404 | } | ||
405 | |||
406 | dotraplinkage notrace __kprobes void | ||
407 | do_nmi(struct pt_regs *regs, long error_code) | ||
408 | { | ||
409 | nmi_enter(); | ||
410 | |||
411 | inc_irq_stat(__nmi_count); | ||
412 | |||
413 | if (!ignore_nmis) | ||
414 | default_do_nmi(regs); | ||
415 | |||
416 | nmi_exit(); | ||
417 | } | ||
418 | |||
419 | void stop_nmi(void) | ||
420 | { | ||
421 | ignore_nmis++; | ||
422 | } | ||
423 | |||
424 | void restart_nmi(void) | ||
425 | { | ||
426 | ignore_nmis--; | ||
427 | } | ||
428 | |||
429 | /* reset the back-to-back NMI logic */ | ||
430 | void local_touch_nmi(void) | ||
431 | { | ||
432 | __this_cpu_write(last_nmi_rip, 0); | ||
433 | } | ||
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 2196c703c5e2..795b79f984c2 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include <asm/idle.h> | 57 | #include <asm/idle.h> |
58 | #include <asm/syscalls.h> | 58 | #include <asm/syscalls.h> |
59 | #include <asm/debugreg.h> | 59 | #include <asm/debugreg.h> |
60 | #include <asm/nmi.h> | ||
60 | 61 | ||
61 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | 62 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); |
62 | 63 | ||
@@ -107,6 +108,7 @@ void cpu_idle(void) | |||
107 | if (cpu_is_offline(cpu)) | 108 | if (cpu_is_offline(cpu)) |
108 | play_dead(); | 109 | play_dead(); |
109 | 110 | ||
111 | local_touch_nmi(); | ||
110 | local_irq_disable(); | 112 | local_irq_disable(); |
111 | /* Don't trace irqs off for idle */ | 113 | /* Don't trace irqs off for idle */ |
112 | stop_critical_timings(); | 114 | stop_critical_timings(); |
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index f693e44e1bf6..3bd7e6eebf31 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <asm/idle.h> | 51 | #include <asm/idle.h> |
52 | #include <asm/syscalls.h> | 52 | #include <asm/syscalls.h> |
53 | #include <asm/debugreg.h> | 53 | #include <asm/debugreg.h> |
54 | #include <asm/nmi.h> | ||
54 | 55 | ||
55 | asmlinkage extern void ret_from_fork(void); | 56 | asmlinkage extern void ret_from_fork(void); |
56 | 57 | ||
@@ -133,6 +134,7 @@ void cpu_idle(void) | |||
133 | * from here on, until they go to idle. | 134 | * from here on, until they go to idle. |
134 | * Otherwise, idle callbacks can misfire. | 135 | * Otherwise, idle callbacks can misfire. |
135 | */ | 136 | */ |
137 | local_touch_nmi(); | ||
136 | local_irq_disable(); | 138 | local_irq_disable(); |
137 | enter_idle(); | 139 | enter_idle(); |
138 | /* Don't trace irqs off for idle */ | 140 | /* Don't trace irqs off for idle */ |
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 9242436e9937..e334be1182b9 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c | |||
@@ -464,7 +464,7 @@ static inline void kb_wait(void) | |||
464 | } | 464 | } |
465 | } | 465 | } |
466 | 466 | ||
467 | static void vmxoff_nmi(int cpu, struct die_args *args) | 467 | static void vmxoff_nmi(int cpu, struct pt_regs *regs) |
468 | { | 468 | { |
469 | cpu_emergency_vmxoff(); | 469 | cpu_emergency_vmxoff(); |
470 | } | 470 | } |
@@ -736,14 +736,10 @@ static nmi_shootdown_cb shootdown_callback; | |||
736 | 736 | ||
737 | static atomic_t waiting_for_crash_ipi; | 737 | static atomic_t waiting_for_crash_ipi; |
738 | 738 | ||
739 | static int crash_nmi_callback(struct notifier_block *self, | 739 | static int crash_nmi_callback(unsigned int val, struct pt_regs *regs) |
740 | unsigned long val, void *data) | ||
741 | { | 740 | { |
742 | int cpu; | 741 | int cpu; |
743 | 742 | ||
744 | if (val != DIE_NMI) | ||
745 | return NOTIFY_OK; | ||
746 | |||
747 | cpu = raw_smp_processor_id(); | 743 | cpu = raw_smp_processor_id(); |
748 | 744 | ||
749 | /* Don't do anything if this handler is invoked on crashing cpu. | 745 | /* Don't do anything if this handler is invoked on crashing cpu. |
@@ -751,10 +747,10 @@ static int crash_nmi_callback(struct notifier_block *self, | |||
751 | * an NMI if system was initially booted with nmi_watchdog parameter. | 747 | * an NMI if system was initially booted with nmi_watchdog parameter. |
752 | */ | 748 | */ |
753 | if (cpu == crashing_cpu) | 749 | if (cpu == crashing_cpu) |
754 | return NOTIFY_STOP; | 750 | return NMI_HANDLED; |
755 | local_irq_disable(); | 751 | local_irq_disable(); |
756 | 752 | ||
757 | shootdown_callback(cpu, (struct die_args *)data); | 753 | shootdown_callback(cpu, regs); |
758 | 754 | ||
759 | atomic_dec(&waiting_for_crash_ipi); | 755 | atomic_dec(&waiting_for_crash_ipi); |
760 | /* Assume hlt works */ | 756 | /* Assume hlt works */ |
@@ -762,7 +758,7 @@ static int crash_nmi_callback(struct notifier_block *self, | |||
762 | for (;;) | 758 | for (;;) |
763 | cpu_relax(); | 759 | cpu_relax(); |
764 | 760 | ||
765 | return 1; | 761 | return NMI_HANDLED; |
766 | } | 762 | } |
767 | 763 | ||
768 | static void smp_send_nmi_allbutself(void) | 764 | static void smp_send_nmi_allbutself(void) |
@@ -770,12 +766,6 @@ static void smp_send_nmi_allbutself(void) | |||
770 | apic->send_IPI_allbutself(NMI_VECTOR); | 766 | apic->send_IPI_allbutself(NMI_VECTOR); |
771 | } | 767 | } |
772 | 768 | ||
773 | static struct notifier_block crash_nmi_nb = { | ||
774 | .notifier_call = crash_nmi_callback, | ||
775 | /* we want to be the first one called */ | ||
776 | .priority = NMI_LOCAL_HIGH_PRIOR+1, | ||
777 | }; | ||
778 | |||
779 | /* Halt all other CPUs, calling the specified function on each of them | 769 | /* Halt all other CPUs, calling the specified function on each of them |
780 | * | 770 | * |
781 | * This function can be used to halt all other CPUs on crash | 771 | * This function can be used to halt all other CPUs on crash |
@@ -794,7 +784,8 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback) | |||
794 | 784 | ||
795 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); | 785 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); |
796 | /* Would it be better to replace the trap vector here? */ | 786 | /* Would it be better to replace the trap vector here? */ |
797 | if (register_die_notifier(&crash_nmi_nb)) | 787 | if (register_nmi_handler(NMI_LOCAL, crash_nmi_callback, |
788 | NMI_FLAG_FIRST, "crash")) | ||
798 | return; /* return what? */ | 789 | return; /* return what? */ |
799 | /* Ensure the new callback function is set before sending | 790 | /* Ensure the new callback function is set before sending |
800 | * out the NMI | 791 | * out the NMI |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 6913369c234c..a8e3eb83466c 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -81,15 +81,6 @@ gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { 0, 0 } } }, }; | |||
81 | DECLARE_BITMAP(used_vectors, NR_VECTORS); | 81 | DECLARE_BITMAP(used_vectors, NR_VECTORS); |
82 | EXPORT_SYMBOL_GPL(used_vectors); | 82 | EXPORT_SYMBOL_GPL(used_vectors); |
83 | 83 | ||
84 | static int ignore_nmis; | ||
85 | |||
86 | int unknown_nmi_panic; | ||
87 | /* | ||
88 | * Prevent NMI reason port (0x61) being accessed simultaneously, can | ||
89 | * only be used in NMI handler. | ||
90 | */ | ||
91 | static DEFINE_RAW_SPINLOCK(nmi_reason_lock); | ||
92 | |||
93 | static inline void conditional_sti(struct pt_regs *regs) | 84 | static inline void conditional_sti(struct pt_regs *regs) |
94 | { | 85 | { |
95 | if (regs->flags & X86_EFLAGS_IF) | 86 | if (regs->flags & X86_EFLAGS_IF) |
@@ -307,152 +298,6 @@ gp_in_kernel: | |||
307 | die("general protection fault", regs, error_code); | 298 | die("general protection fault", regs, error_code); |
308 | } | 299 | } |
309 | 300 | ||
310 | static int __init setup_unknown_nmi_panic(char *str) | ||
311 | { | ||
312 | unknown_nmi_panic = 1; | ||
313 | return 1; | ||
314 | } | ||
315 | __setup("unknown_nmi_panic", setup_unknown_nmi_panic); | ||
316 | |||
317 | static notrace __kprobes void | ||
318 | pci_serr_error(unsigned char reason, struct pt_regs *regs) | ||
319 | { | ||
320 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", | ||
321 | reason, smp_processor_id()); | ||
322 | |||
323 | /* | ||
324 | * On some machines, PCI SERR line is used to report memory | ||
325 | * errors. EDAC makes use of it. | ||
326 | */ | ||
327 | #if defined(CONFIG_EDAC) | ||
328 | if (edac_handler_set()) { | ||
329 | edac_atomic_assert_error(); | ||
330 | return; | ||
331 | } | ||
332 | #endif | ||
333 | |||
334 | if (panic_on_unrecovered_nmi) | ||
335 | panic("NMI: Not continuing"); | ||
336 | |||
337 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
338 | |||
339 | /* Clear and disable the PCI SERR error line. */ | ||
340 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR; | ||
341 | outb(reason, NMI_REASON_PORT); | ||
342 | } | ||
343 | |||
344 | static notrace __kprobes void | ||
345 | io_check_error(unsigned char reason, struct pt_regs *regs) | ||
346 | { | ||
347 | unsigned long i; | ||
348 | |||
349 | pr_emerg( | ||
350 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", | ||
351 | reason, smp_processor_id()); | ||
352 | show_registers(regs); | ||
353 | |||
354 | if (panic_on_io_nmi) | ||
355 | panic("NMI IOCK error: Not continuing"); | ||
356 | |||
357 | /* Re-enable the IOCK line, wait for a few seconds */ | ||
358 | reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_IOCHK; | ||
359 | outb(reason, NMI_REASON_PORT); | ||
360 | |||
361 | i = 20000; | ||
362 | while (--i) { | ||
363 | touch_nmi_watchdog(); | ||
364 | udelay(100); | ||
365 | } | ||
366 | |||
367 | reason &= ~NMI_REASON_CLEAR_IOCHK; | ||
368 | outb(reason, NMI_REASON_PORT); | ||
369 | } | ||
370 | |||
371 | static notrace __kprobes void | ||
372 | unknown_nmi_error(unsigned char reason, struct pt_regs *regs) | ||
373 | { | ||
374 | if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == | ||
375 | NOTIFY_STOP) | ||
376 | return; | ||
377 | #ifdef CONFIG_MCA | ||
378 | /* | ||
379 | * Might actually be able to figure out what the guilty party | ||
380 | * is: | ||
381 | */ | ||
382 | if (MCA_bus) { | ||
383 | mca_handle_nmi(); | ||
384 | return; | ||
385 | } | ||
386 | #endif | ||
387 | pr_emerg("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", | ||
388 | reason, smp_processor_id()); | ||
389 | |||
390 | pr_emerg("Do you have a strange power saving mode enabled?\n"); | ||
391 | if (unknown_nmi_panic || panic_on_unrecovered_nmi) | ||
392 | panic("NMI: Not continuing"); | ||
393 | |||
394 | pr_emerg("Dazed and confused, but trying to continue\n"); | ||
395 | } | ||
396 | |||
397 | static notrace __kprobes void default_do_nmi(struct pt_regs *regs) | ||
398 | { | ||
399 | unsigned char reason = 0; | ||
400 | |||
401 | /* | ||
402 | * CPU-specific NMI must be processed before non-CPU-specific | ||
403 | * NMI, otherwise we may lose it, because the CPU-specific | ||
404 | * NMI can not be detected/processed on other CPUs. | ||
405 | */ | ||
406 | if (notify_die(DIE_NMI, "nmi", regs, 0, 2, SIGINT) == NOTIFY_STOP) | ||
407 | return; | ||
408 | |||
409 | /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ | ||
410 | raw_spin_lock(&nmi_reason_lock); | ||
411 | reason = get_nmi_reason(); | ||
412 | |||
413 | if (reason & NMI_REASON_MASK) { | ||
414 | if (reason & NMI_REASON_SERR) | ||
415 | pci_serr_error(reason, regs); | ||
416 | else if (reason & NMI_REASON_IOCHK) | ||
417 | io_check_error(reason, regs); | ||
418 | #ifdef CONFIG_X86_32 | ||
419 | /* | ||
420 | * Reassert NMI in case it became active | ||
421 | * meanwhile as it's edge-triggered: | ||
422 | */ | ||
423 | reassert_nmi(); | ||
424 | #endif | ||
425 | raw_spin_unlock(&nmi_reason_lock); | ||
426 | return; | ||
427 | } | ||
428 | raw_spin_unlock(&nmi_reason_lock); | ||
429 | |||
430 | unknown_nmi_error(reason, regs); | ||
431 | } | ||
432 | |||
433 | dotraplinkage notrace __kprobes void | ||
434 | do_nmi(struct pt_regs *regs, long error_code) | ||
435 | { | ||
436 | nmi_enter(); | ||
437 | |||
438 | inc_irq_stat(__nmi_count); | ||
439 | |||
440 | if (!ignore_nmis) | ||
441 | default_do_nmi(regs); | ||
442 | |||
443 | nmi_exit(); | ||
444 | } | ||
445 | |||
446 | void stop_nmi(void) | ||
447 | { | ||
448 | ignore_nmis++; | ||
449 | } | ||
450 | |||
451 | void restart_nmi(void) | ||
452 | { | ||
453 | ignore_nmis--; | ||
454 | } | ||
455 | |||
456 | /* May run on IST stack. */ | 301 | /* May run on IST stack. */ |
457 | dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) | 302 | dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) |
458 | { | 303 | { |
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 96646b3aeca8..75f9528e0372 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c | |||
@@ -61,26 +61,15 @@ u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, | |||
61 | } | 61 | } |
62 | 62 | ||
63 | 63 | ||
64 | static int profile_exceptions_notify(struct notifier_block *self, | 64 | static int profile_exceptions_notify(unsigned int val, struct pt_regs *regs) |
65 | unsigned long val, void *data) | ||
66 | { | 65 | { |
67 | struct die_args *args = (struct die_args *)data; | 66 | if (ctr_running) |
68 | int ret = NOTIFY_DONE; | 67 | model->check_ctrs(regs, &__get_cpu_var(cpu_msrs)); |
69 | 68 | else if (!nmi_enabled) | |
70 | switch (val) { | 69 | return NMI_DONE; |
71 | case DIE_NMI: | 70 | else |
72 | if (ctr_running) | 71 | model->stop(&__get_cpu_var(cpu_msrs)); |
73 | model->check_ctrs(args->regs, &__get_cpu_var(cpu_msrs)); | 72 | return NMI_HANDLED; |
74 | else if (!nmi_enabled) | ||
75 | break; | ||
76 | else | ||
77 | model->stop(&__get_cpu_var(cpu_msrs)); | ||
78 | ret = NOTIFY_STOP; | ||
79 | break; | ||
80 | default: | ||
81 | break; | ||
82 | } | ||
83 | return ret; | ||
84 | } | 73 | } |
85 | 74 | ||
86 | static void nmi_cpu_save_registers(struct op_msrs *msrs) | 75 | static void nmi_cpu_save_registers(struct op_msrs *msrs) |
@@ -363,12 +352,6 @@ static void nmi_cpu_setup(void *dummy) | |||
363 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 352 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
364 | } | 353 | } |
365 | 354 | ||
366 | static struct notifier_block profile_exceptions_nb = { | ||
367 | .notifier_call = profile_exceptions_notify, | ||
368 | .next = NULL, | ||
369 | .priority = NMI_LOCAL_LOW_PRIOR, | ||
370 | }; | ||
371 | |||
372 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) | 355 | static void nmi_cpu_restore_registers(struct op_msrs *msrs) |
373 | { | 356 | { |
374 | struct op_msr *counters = msrs->counters; | 357 | struct op_msr *counters = msrs->counters; |
@@ -402,8 +385,6 @@ static void nmi_cpu_shutdown(void *dummy) | |||
402 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); | 385 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); |
403 | apic_write(APIC_LVTERR, v); | 386 | apic_write(APIC_LVTERR, v); |
404 | nmi_cpu_restore_registers(msrs); | 387 | nmi_cpu_restore_registers(msrs); |
405 | if (model->cpu_down) | ||
406 | model->cpu_down(); | ||
407 | } | 388 | } |
408 | 389 | ||
409 | static void nmi_cpu_up(void *dummy) | 390 | static void nmi_cpu_up(void *dummy) |
@@ -508,7 +489,8 @@ static int nmi_setup(void) | |||
508 | ctr_running = 0; | 489 | ctr_running = 0; |
509 | /* make variables visible to the nmi handler: */ | 490 | /* make variables visible to the nmi handler: */ |
510 | smp_mb(); | 491 | smp_mb(); |
511 | err = register_die_notifier(&profile_exceptions_nb); | 492 | err = register_nmi_handler(NMI_LOCAL, profile_exceptions_notify, |
493 | 0, "oprofile"); | ||
512 | if (err) | 494 | if (err) |
513 | goto fail; | 495 | goto fail; |
514 | 496 | ||
@@ -538,7 +520,7 @@ static void nmi_shutdown(void) | |||
538 | put_online_cpus(); | 520 | put_online_cpus(); |
539 | /* make variables visible to the nmi handler: */ | 521 | /* make variables visible to the nmi handler: */ |
540 | smp_mb(); | 522 | smp_mb(); |
541 | unregister_die_notifier(&profile_exceptions_nb); | 523 | unregister_nmi_handler(NMI_LOCAL, "oprofile"); |
542 | msrs = &get_cpu_var(cpu_msrs); | 524 | msrs = &get_cpu_var(cpu_msrs); |
543 | model->shutdown(msrs); | 525 | model->shutdown(msrs); |
544 | free_msrs(); | 526 | free_msrs(); |
diff --git a/arch/x86/oprofile/nmi_timer_int.c b/arch/x86/oprofile/nmi_timer_int.c index 720bf5a53c51..7f8052cd6620 100644 --- a/arch/x86/oprofile/nmi_timer_int.c +++ b/arch/x86/oprofile/nmi_timer_int.c | |||
@@ -18,32 +18,16 @@ | |||
18 | #include <asm/apic.h> | 18 | #include <asm/apic.h> |
19 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
20 | 20 | ||
21 | static int profile_timer_exceptions_notify(struct notifier_block *self, | 21 | static int profile_timer_exceptions_notify(unsigned int val, struct pt_regs *regs) |
22 | unsigned long val, void *data) | ||
23 | { | 22 | { |
24 | struct die_args *args = (struct die_args *)data; | 23 | oprofile_add_sample(regs, 0); |
25 | int ret = NOTIFY_DONE; | 24 | return NMI_HANDLED; |
26 | |||
27 | switch (val) { | ||
28 | case DIE_NMI: | ||
29 | oprofile_add_sample(args->regs, 0); | ||
30 | ret = NOTIFY_STOP; | ||
31 | break; | ||
32 | default: | ||
33 | break; | ||
34 | } | ||
35 | return ret; | ||
36 | } | 25 | } |
37 | 26 | ||
38 | static struct notifier_block profile_timer_exceptions_nb = { | ||
39 | .notifier_call = profile_timer_exceptions_notify, | ||
40 | .next = NULL, | ||
41 | .priority = NMI_LOW_PRIOR, | ||
42 | }; | ||
43 | |||
44 | static int timer_start(void) | 27 | static int timer_start(void) |
45 | { | 28 | { |
46 | if (register_die_notifier(&profile_timer_exceptions_nb)) | 29 | if (register_nmi_handler(NMI_LOCAL, profile_timer_exceptions_notify, |
30 | 0, "oprofile-timer")) | ||
47 | return 1; | 31 | return 1; |
48 | return 0; | 32 | return 0; |
49 | } | 33 | } |
@@ -51,7 +35,7 @@ static int timer_start(void) | |||
51 | 35 | ||
52 | static void timer_stop(void) | 36 | static void timer_stop(void) |
53 | { | 37 | { |
54 | unregister_die_notifier(&profile_timer_exceptions_nb); | 38 | unregister_nmi_handler(NMI_LOCAL, "oprofile-timer"); |
55 | synchronize_sched(); /* Allow already-started NMIs to complete. */ | 39 | synchronize_sched(); /* Allow already-started NMIs to complete. */ |
56 | } | 40 | } |
57 | 41 | ||
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index 9cbb710dc94b..303f08637826 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -29,8 +29,6 @@ | |||
29 | #include "op_x86_model.h" | 29 | #include "op_x86_model.h" |
30 | #include "op_counter.h" | 30 | #include "op_counter.h" |
31 | 31 | ||
32 | #define NUM_COUNTERS 4 | ||
33 | #define NUM_COUNTERS_F15H 6 | ||
34 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 32 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
35 | #define NUM_VIRT_COUNTERS 32 | 33 | #define NUM_VIRT_COUNTERS 32 |
36 | #else | 34 | #else |
@@ -70,62 +68,12 @@ static struct ibs_config ibs_config; | |||
70 | static struct ibs_state ibs_state; | 68 | static struct ibs_state ibs_state; |
71 | 69 | ||
72 | /* | 70 | /* |
73 | * IBS cpuid feature detection | ||
74 | */ | ||
75 | |||
76 | #define IBS_CPUID_FEATURES 0x8000001b | ||
77 | |||
78 | /* | ||
79 | * Same bit mask as for IBS cpuid feature flags (Fn8000_001B_EAX), but | ||
80 | * bit 0 is used to indicate the existence of IBS. | ||
81 | */ | ||
82 | #define IBS_CAPS_AVAIL (1U<<0) | ||
83 | #define IBS_CAPS_FETCHSAM (1U<<1) | ||
84 | #define IBS_CAPS_OPSAM (1U<<2) | ||
85 | #define IBS_CAPS_RDWROPCNT (1U<<3) | ||
86 | #define IBS_CAPS_OPCNT (1U<<4) | ||
87 | #define IBS_CAPS_BRNTRGT (1U<<5) | ||
88 | #define IBS_CAPS_OPCNTEXT (1U<<6) | ||
89 | |||
90 | #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ | ||
91 | | IBS_CAPS_FETCHSAM \ | ||
92 | | IBS_CAPS_OPSAM) | ||
93 | |||
94 | /* | ||
95 | * IBS APIC setup | ||
96 | */ | ||
97 | #define IBSCTL 0x1cc | ||
98 | #define IBSCTL_LVT_OFFSET_VALID (1ULL<<8) | ||
99 | #define IBSCTL_LVT_OFFSET_MASK 0x0F | ||
100 | |||
101 | /* | ||
102 | * IBS randomization macros | 71 | * IBS randomization macros |
103 | */ | 72 | */ |
104 | #define IBS_RANDOM_BITS 12 | 73 | #define IBS_RANDOM_BITS 12 |
105 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) | 74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) |
106 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) | 75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) |
107 | 76 | ||
108 | static u32 get_ibs_caps(void) | ||
109 | { | ||
110 | u32 ibs_caps; | ||
111 | unsigned int max_level; | ||
112 | |||
113 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
114 | return 0; | ||
115 | |||
116 | /* check IBS cpuid feature flags */ | ||
117 | max_level = cpuid_eax(0x80000000); | ||
118 | if (max_level < IBS_CPUID_FEATURES) | ||
119 | return IBS_CAPS_DEFAULT; | ||
120 | |||
121 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
122 | if (!(ibs_caps & IBS_CAPS_AVAIL)) | ||
123 | /* cpuid flags not valid */ | ||
124 | return IBS_CAPS_DEFAULT; | ||
125 | |||
126 | return ibs_caps; | ||
127 | } | ||
128 | |||
129 | /* | 77 | /* |
130 | * 16-bit Linear Feedback Shift Register (LFSR) | 78 | * 16-bit Linear Feedback Shift Register (LFSR) |
131 | * | 79 | * |
@@ -316,81 +264,6 @@ static void op_amd_stop_ibs(void) | |||
316 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 264 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
317 | } | 265 | } |
318 | 266 | ||
319 | static inline int get_eilvt(int offset) | ||
320 | { | ||
321 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
322 | } | ||
323 | |||
324 | static inline int put_eilvt(int offset) | ||
325 | { | ||
326 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
327 | } | ||
328 | |||
329 | static inline int ibs_eilvt_valid(void) | ||
330 | { | ||
331 | int offset; | ||
332 | u64 val; | ||
333 | int valid = 0; | ||
334 | |||
335 | preempt_disable(); | ||
336 | |||
337 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
338 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
339 | |||
340 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
341 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
342 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | if (!get_eilvt(offset)) { | ||
347 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
348 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
349 | goto out; | ||
350 | } | ||
351 | |||
352 | valid = 1; | ||
353 | out: | ||
354 | preempt_enable(); | ||
355 | |||
356 | return valid; | ||
357 | } | ||
358 | |||
359 | static inline int get_ibs_offset(void) | ||
360 | { | ||
361 | u64 val; | ||
362 | |||
363 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
364 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
365 | return -EINVAL; | ||
366 | |||
367 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
368 | } | ||
369 | |||
370 | static void setup_APIC_ibs(void) | ||
371 | { | ||
372 | int offset; | ||
373 | |||
374 | offset = get_ibs_offset(); | ||
375 | if (offset < 0) | ||
376 | goto failed; | ||
377 | |||
378 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
379 | return; | ||
380 | failed: | ||
381 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
382 | smp_processor_id()); | ||
383 | } | ||
384 | |||
385 | static void clear_APIC_ibs(void) | ||
386 | { | ||
387 | int offset; | ||
388 | |||
389 | offset = get_ibs_offset(); | ||
390 | if (offset >= 0) | ||
391 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
392 | } | ||
393 | |||
394 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 267 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
395 | 268 | ||
396 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 269 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
@@ -439,7 +312,7 @@ static int op_amd_fill_in_addresses(struct op_msrs * const msrs) | |||
439 | goto fail; | 312 | goto fail; |
440 | } | 313 | } |
441 | /* both registers must be reserved */ | 314 | /* both registers must be reserved */ |
442 | if (num_counters == NUM_COUNTERS_F15H) { | 315 | if (num_counters == AMD64_NUM_COUNTERS_F15H) { |
443 | msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); | 316 | msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); |
444 | msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); | 317 | msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); |
445 | } else { | 318 | } else { |
@@ -504,15 +377,6 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
504 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | 377 | val |= op_x86_get_ctrl(model, &counter_config[virt]); |
505 | wrmsrl(msrs->controls[i].addr, val); | 378 | wrmsrl(msrs->controls[i].addr, val); |
506 | } | 379 | } |
507 | |||
508 | if (ibs_caps) | ||
509 | setup_APIC_ibs(); | ||
510 | } | ||
511 | |||
512 | static void op_amd_cpu_shutdown(void) | ||
513 | { | ||
514 | if (ibs_caps) | ||
515 | clear_APIC_ibs(); | ||
516 | } | 380 | } |
517 | 381 | ||
518 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 382 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
@@ -575,86 +439,6 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
575 | op_amd_stop_ibs(); | 439 | op_amd_stop_ibs(); |
576 | } | 440 | } |
577 | 441 | ||
578 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
579 | { | ||
580 | struct pci_dev *cpu_cfg; | ||
581 | int nodes; | ||
582 | u32 value = 0; | ||
583 | |||
584 | nodes = 0; | ||
585 | cpu_cfg = NULL; | ||
586 | do { | ||
587 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
588 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
589 | cpu_cfg); | ||
590 | if (!cpu_cfg) | ||
591 | break; | ||
592 | ++nodes; | ||
593 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
594 | | IBSCTL_LVT_OFFSET_VALID); | ||
595 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
596 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
597 | pci_dev_put(cpu_cfg); | ||
598 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
599 | "IBSCTL = 0x%08x\n", value); | ||
600 | return -EINVAL; | ||
601 | } | ||
602 | } while (1); | ||
603 | |||
604 | if (!nodes) { | ||
605 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
606 | return -ENODEV; | ||
607 | } | ||
608 | |||
609 | return 0; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * This runs only on the current cpu. We try to find an LVT offset and | ||
614 | * setup the local APIC. For this we must disable preemption. On | ||
615 | * success we initialize all nodes with this offset. This updates then | ||
616 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
617 | * the IBS interrupt vector is called from op_amd_setup_ctrs()/op_- | ||
618 | * amd_cpu_shutdown() using the new offset. | ||
619 | */ | ||
620 | static int force_ibs_eilvt_setup(void) | ||
621 | { | ||
622 | int offset; | ||
623 | int ret; | ||
624 | |||
625 | preempt_disable(); | ||
626 | /* find the next free available EILVT entry, skip offset 0 */ | ||
627 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
628 | if (get_eilvt(offset)) | ||
629 | break; | ||
630 | } | ||
631 | preempt_enable(); | ||
632 | |||
633 | if (offset == APIC_EILVT_NR_MAX) { | ||
634 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
635 | return -EBUSY; | ||
636 | } | ||
637 | |||
638 | ret = setup_ibs_ctl(offset); | ||
639 | if (ret) | ||
640 | goto out; | ||
641 | |||
642 | if (!ibs_eilvt_valid()) { | ||
643 | ret = -EFAULT; | ||
644 | goto out; | ||
645 | } | ||
646 | |||
647 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
648 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
649 | |||
650 | return 0; | ||
651 | out: | ||
652 | preempt_disable(); | ||
653 | put_eilvt(offset); | ||
654 | preempt_enable(); | ||
655 | return ret; | ||
656 | } | ||
657 | |||
658 | /* | 442 | /* |
659 | * check and reserve APIC extended interrupt LVT offset for IBS if | 443 | * check and reserve APIC extended interrupt LVT offset for IBS if |
660 | * available | 444 | * available |
@@ -667,17 +451,6 @@ static void init_ibs(void) | |||
667 | if (!ibs_caps) | 451 | if (!ibs_caps) |
668 | return; | 452 | return; |
669 | 453 | ||
670 | if (ibs_eilvt_valid()) | ||
671 | goto out; | ||
672 | |||
673 | if (!force_ibs_eilvt_setup()) | ||
674 | goto out; | ||
675 | |||
676 | /* Failed to setup ibs */ | ||
677 | ibs_caps = 0; | ||
678 | return; | ||
679 | |||
680 | out: | ||
681 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); | 454 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); |
682 | } | 455 | } |
683 | 456 | ||
@@ -741,9 +514,9 @@ static int op_amd_init(struct oprofile_operations *ops) | |||
741 | ops->create_files = setup_ibs_files; | 514 | ops->create_files = setup_ibs_files; |
742 | 515 | ||
743 | if (boot_cpu_data.x86 == 0x15) { | 516 | if (boot_cpu_data.x86 == 0x15) { |
744 | num_counters = NUM_COUNTERS_F15H; | 517 | num_counters = AMD64_NUM_COUNTERS_F15H; |
745 | } else { | 518 | } else { |
746 | num_counters = NUM_COUNTERS; | 519 | num_counters = AMD64_NUM_COUNTERS; |
747 | } | 520 | } |
748 | 521 | ||
749 | op_amd_spec.num_counters = num_counters; | 522 | op_amd_spec.num_counters = num_counters; |
@@ -760,7 +533,6 @@ struct op_x86_model_spec op_amd_spec = { | |||
760 | .init = op_amd_init, | 533 | .init = op_amd_init, |
761 | .fill_in_addresses = &op_amd_fill_in_addresses, | 534 | .fill_in_addresses = &op_amd_fill_in_addresses, |
762 | .setup_ctrs = &op_amd_setup_ctrs, | 535 | .setup_ctrs = &op_amd_setup_ctrs, |
763 | .cpu_down = &op_amd_cpu_shutdown, | ||
764 | .check_ctrs = &op_amd_check_ctrs, | 536 | .check_ctrs = &op_amd_check_ctrs, |
765 | .start = &op_amd_start, | 537 | .start = &op_amd_start, |
766 | .stop = &op_amd_stop, | 538 | .stop = &op_amd_stop, |
diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c index 94b745045e45..d90528ea5412 100644 --- a/arch/x86/oprofile/op_model_ppro.c +++ b/arch/x86/oprofile/op_model_ppro.c | |||
@@ -28,7 +28,7 @@ static int counter_width = 32; | |||
28 | 28 | ||
29 | #define MSR_PPRO_EVENTSEL_RESERVED ((0xFFFFFFFFULL<<32)|(1ULL<<21)) | 29 | #define MSR_PPRO_EVENTSEL_RESERVED ((0xFFFFFFFFULL<<32)|(1ULL<<21)) |
30 | 30 | ||
31 | static u64 *reset_value; | 31 | static u64 reset_value[OP_MAX_COUNTER]; |
32 | 32 | ||
33 | static void ppro_shutdown(struct op_msrs const * const msrs) | 33 | static void ppro_shutdown(struct op_msrs const * const msrs) |
34 | { | 34 | { |
@@ -40,10 +40,6 @@ static void ppro_shutdown(struct op_msrs const * const msrs) | |||
40 | release_perfctr_nmi(MSR_P6_PERFCTR0 + i); | 40 | release_perfctr_nmi(MSR_P6_PERFCTR0 + i); |
41 | release_evntsel_nmi(MSR_P6_EVNTSEL0 + i); | 41 | release_evntsel_nmi(MSR_P6_EVNTSEL0 + i); |
42 | } | 42 | } |
43 | if (reset_value) { | ||
44 | kfree(reset_value); | ||
45 | reset_value = NULL; | ||
46 | } | ||
47 | } | 43 | } |
48 | 44 | ||
49 | static int ppro_fill_in_addresses(struct op_msrs * const msrs) | 45 | static int ppro_fill_in_addresses(struct op_msrs * const msrs) |
@@ -79,13 +75,6 @@ static void ppro_setup_ctrs(struct op_x86_model_spec const *model, | |||
79 | u64 val; | 75 | u64 val; |
80 | int i; | 76 | int i; |
81 | 77 | ||
82 | if (!reset_value) { | ||
83 | reset_value = kzalloc(sizeof(reset_value[0]) * num_counters, | ||
84 | GFP_ATOMIC); | ||
85 | if (!reset_value) | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | if (cpu_has_arch_perfmon) { | 78 | if (cpu_has_arch_perfmon) { |
90 | union cpuid10_eax eax; | 79 | union cpuid10_eax eax; |
91 | eax.full = cpuid_eax(0xa); | 80 | eax.full = cpuid_eax(0xa); |
@@ -141,13 +130,6 @@ static int ppro_check_ctrs(struct pt_regs * const regs, | |||
141 | u64 val; | 130 | u64 val; |
142 | int i; | 131 | int i; |
143 | 132 | ||
144 | /* | ||
145 | * This can happen if perf counters are in use when | ||
146 | * we steal the die notifier NMI. | ||
147 | */ | ||
148 | if (unlikely(!reset_value)) | ||
149 | goto out; | ||
150 | |||
151 | for (i = 0; i < num_counters; ++i) { | 133 | for (i = 0; i < num_counters; ++i) { |
152 | if (!reset_value[i]) | 134 | if (!reset_value[i]) |
153 | continue; | 135 | continue; |
@@ -158,7 +140,6 @@ static int ppro_check_ctrs(struct pt_regs * const regs, | |||
158 | wrmsrl(msrs->counters[i].addr, -reset_value[i]); | 140 | wrmsrl(msrs->counters[i].addr, -reset_value[i]); |
159 | } | 141 | } |
160 | 142 | ||
161 | out: | ||
162 | /* Only P6 based Pentium M need to re-unmask the apic vector but it | 143 | /* Only P6 based Pentium M need to re-unmask the apic vector but it |
163 | * doesn't hurt other P6 variant */ | 144 | * doesn't hurt other P6 variant */ |
164 | apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); | 145 | apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); |
@@ -179,8 +160,6 @@ static void ppro_start(struct op_msrs const * const msrs) | |||
179 | u64 val; | 160 | u64 val; |
180 | int i; | 161 | int i; |
181 | 162 | ||
182 | if (!reset_value) | ||
183 | return; | ||
184 | for (i = 0; i < num_counters; ++i) { | 163 | for (i = 0; i < num_counters; ++i) { |
185 | if (reset_value[i]) { | 164 | if (reset_value[i]) { |
186 | rdmsrl(msrs->controls[i].addr, val); | 165 | rdmsrl(msrs->controls[i].addr, val); |
@@ -196,8 +175,6 @@ static void ppro_stop(struct op_msrs const * const msrs) | |||
196 | u64 val; | 175 | u64 val; |
197 | int i; | 176 | int i; |
198 | 177 | ||
199 | if (!reset_value) | ||
200 | return; | ||
201 | for (i = 0; i < num_counters; ++i) { | 178 | for (i = 0; i < num_counters; ++i) { |
202 | if (!reset_value[i]) | 179 | if (!reset_value[i]) |
203 | continue; | 180 | continue; |
@@ -242,7 +219,7 @@ static void arch_perfmon_setup_counters(void) | |||
242 | eax.split.bit_width = 40; | 219 | eax.split.bit_width = 40; |
243 | } | 220 | } |
244 | 221 | ||
245 | num_counters = eax.split.num_counters; | 222 | num_counters = min((int)eax.split.num_counters, OP_MAX_COUNTER); |
246 | 223 | ||
247 | op_arch_perfmon_spec.num_counters = num_counters; | 224 | op_arch_perfmon_spec.num_counters = num_counters; |
248 | op_arch_perfmon_spec.num_controls = num_counters; | 225 | op_arch_perfmon_spec.num_controls = num_counters; |
diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index 89017fa1fd63..71e8a67337e2 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h | |||
@@ -43,7 +43,6 @@ struct op_x86_model_spec { | |||
43 | int (*fill_in_addresses)(struct op_msrs * const msrs); | 43 | int (*fill_in_addresses)(struct op_msrs * const msrs); |
44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, | 44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, |
45 | struct op_msrs const * const msrs); | 45 | struct op_msrs const * const msrs); |
46 | void (*cpu_down)(void); | ||
47 | int (*check_ctrs)(struct pt_regs * const regs, | 46 | int (*check_ctrs)(struct pt_regs * const regs, |
48 | struct op_msrs const * const msrs); | 47 | struct op_msrs const * const msrs); |
49 | void (*start)(struct op_msrs const * const msrs); | 48 | void (*start)(struct op_msrs const * const msrs); |
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0784f99a4665..b8e08cb67a18 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <acpi/hed.h> | 50 | #include <acpi/hed.h> |
51 | #include <asm/mce.h> | 51 | #include <asm/mce.h> |
52 | #include <asm/tlbflush.h> | 52 | #include <asm/tlbflush.h> |
53 | #include <asm/nmi.h> | ||
53 | 54 | ||
54 | #include "apei-internal.h" | 55 | #include "apei-internal.h" |
55 | 56 | ||
@@ -749,15 +750,11 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
749 | } | 750 | } |
750 | } | 751 | } |
751 | 752 | ||
752 | static int ghes_notify_nmi(struct notifier_block *this, | 753 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) |
753 | unsigned long cmd, void *data) | ||
754 | { | 754 | { |
755 | struct ghes *ghes, *ghes_global = NULL; | 755 | struct ghes *ghes, *ghes_global = NULL; |
756 | int sev, sev_global = -1; | 756 | int sev, sev_global = -1; |
757 | int ret = NOTIFY_DONE; | 757 | int ret = NMI_DONE; |
758 | |||
759 | if (cmd != DIE_NMI) | ||
760 | return ret; | ||
761 | 758 | ||
762 | raw_spin_lock(&ghes_nmi_lock); | 759 | raw_spin_lock(&ghes_nmi_lock); |
763 | list_for_each_entry_rcu(ghes, &ghes_nmi, list) { | 760 | list_for_each_entry_rcu(ghes, &ghes_nmi, list) { |
@@ -770,10 +767,10 @@ static int ghes_notify_nmi(struct notifier_block *this, | |||
770 | sev_global = sev; | 767 | sev_global = sev; |
771 | ghes_global = ghes; | 768 | ghes_global = ghes; |
772 | } | 769 | } |
773 | ret = NOTIFY_STOP; | 770 | ret = NMI_HANDLED; |
774 | } | 771 | } |
775 | 772 | ||
776 | if (ret == NOTIFY_DONE) | 773 | if (ret == NMI_DONE) |
777 | goto out; | 774 | goto out; |
778 | 775 | ||
779 | if (sev_global >= GHES_SEV_PANIC) { | 776 | if (sev_global >= GHES_SEV_PANIC) { |
@@ -825,10 +822,6 @@ static struct notifier_block ghes_notifier_sci = { | |||
825 | .notifier_call = ghes_notify_sci, | 822 | .notifier_call = ghes_notify_sci, |
826 | }; | 823 | }; |
827 | 824 | ||
828 | static struct notifier_block ghes_notifier_nmi = { | ||
829 | .notifier_call = ghes_notify_nmi, | ||
830 | }; | ||
831 | |||
832 | static unsigned long ghes_esource_prealloc_size( | 825 | static unsigned long ghes_esource_prealloc_size( |
833 | const struct acpi_hest_generic *generic) | 826 | const struct acpi_hest_generic *generic) |
834 | { | 827 | { |
@@ -918,7 +911,8 @@ static int __devinit ghes_probe(struct platform_device *ghes_dev) | |||
918 | ghes_estatus_pool_expand(len); | 911 | ghes_estatus_pool_expand(len); |
919 | mutex_lock(&ghes_list_mutex); | 912 | mutex_lock(&ghes_list_mutex); |
920 | if (list_empty(&ghes_nmi)) | 913 | if (list_empty(&ghes_nmi)) |
921 | register_die_notifier(&ghes_notifier_nmi); | 914 | register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, |
915 | "ghes"); | ||
922 | list_add_rcu(&ghes->list, &ghes_nmi); | 916 | list_add_rcu(&ghes->list, &ghes_nmi); |
923 | mutex_unlock(&ghes_list_mutex); | 917 | mutex_unlock(&ghes_list_mutex); |
924 | break; | 918 | break; |
@@ -964,7 +958,7 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev) | |||
964 | mutex_lock(&ghes_list_mutex); | 958 | mutex_lock(&ghes_list_mutex); |
965 | list_del_rcu(&ghes->list); | 959 | list_del_rcu(&ghes->list); |
966 | if (list_empty(&ghes_nmi)) | 960 | if (list_empty(&ghes_nmi)) |
967 | unregister_die_notifier(&ghes_notifier_nmi); | 961 | unregister_nmi_handler(NMI_LOCAL, "ghes"); |
968 | mutex_unlock(&ghes_list_mutex); | 962 | mutex_unlock(&ghes_list_mutex); |
969 | /* | 963 | /* |
970 | * To synchronize with NMI handler, ghes can only be | 964 | * To synchronize with NMI handler, ghes can only be |
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 3302586655c4..c2917ffad2c2 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c | |||
@@ -65,6 +65,7 @@ | |||
65 | * mechanism for it at that time. | 65 | * mechanism for it at that time. |
66 | */ | 66 | */ |
67 | #include <asm/kdebug.h> | 67 | #include <asm/kdebug.h> |
68 | #include <asm/nmi.h> | ||
68 | #define HAVE_DIE_NMI | 69 | #define HAVE_DIE_NMI |
69 | #endif | 70 | #endif |
70 | 71 | ||
@@ -1077,17 +1078,8 @@ static void ipmi_unregister_watchdog(int ipmi_intf) | |||
1077 | 1078 | ||
1078 | #ifdef HAVE_DIE_NMI | 1079 | #ifdef HAVE_DIE_NMI |
1079 | static int | 1080 | static int |
1080 | ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) | 1081 | ipmi_nmi(unsigned int val, struct pt_regs *regs) |
1081 | { | 1082 | { |
1082 | struct die_args *args = data; | ||
1083 | |||
1084 | if (val != DIE_NMIUNKNOWN) | ||
1085 | return NOTIFY_OK; | ||
1086 | |||
1087 | /* Hack, if it's a memory or I/O error, ignore it. */ | ||
1088 | if (args->err & 0xc0) | ||
1089 | return NOTIFY_OK; | ||
1090 | |||
1091 | /* | 1083 | /* |
1092 | * If we get here, it's an NMI that's not a memory or I/O | 1084 | * If we get here, it's an NMI that's not a memory or I/O |
1093 | * error. We can't truly tell if it's from IPMI or not | 1085 | * error. We can't truly tell if it's from IPMI or not |
@@ -1097,15 +1089,15 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) | |||
1097 | 1089 | ||
1098 | if (testing_nmi) { | 1090 | if (testing_nmi) { |
1099 | testing_nmi = 2; | 1091 | testing_nmi = 2; |
1100 | return NOTIFY_STOP; | 1092 | return NMI_HANDLED; |
1101 | } | 1093 | } |
1102 | 1094 | ||
1103 | /* If we are not expecting a timeout, ignore it. */ | 1095 | /* If we are not expecting a timeout, ignore it. */ |
1104 | if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) | 1096 | if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) |
1105 | return NOTIFY_OK; | 1097 | return NMI_DONE; |
1106 | 1098 | ||
1107 | if (preaction_val != WDOG_PRETIMEOUT_NMI) | 1099 | if (preaction_val != WDOG_PRETIMEOUT_NMI) |
1108 | return NOTIFY_OK; | 1100 | return NMI_DONE; |
1109 | 1101 | ||
1110 | /* | 1102 | /* |
1111 | * If no one else handled the NMI, we assume it was the IPMI | 1103 | * If no one else handled the NMI, we assume it was the IPMI |
@@ -1120,12 +1112,8 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) | |||
1120 | panic(PFX "pre-timeout"); | 1112 | panic(PFX "pre-timeout"); |
1121 | } | 1113 | } |
1122 | 1114 | ||
1123 | return NOTIFY_STOP; | 1115 | return NMI_HANDLED; |
1124 | } | 1116 | } |
1125 | |||
1126 | static struct notifier_block ipmi_nmi_handler = { | ||
1127 | .notifier_call = ipmi_nmi | ||
1128 | }; | ||
1129 | #endif | 1117 | #endif |
1130 | 1118 | ||
1131 | static int wdog_reboot_handler(struct notifier_block *this, | 1119 | static int wdog_reboot_handler(struct notifier_block *this, |
@@ -1290,7 +1278,8 @@ static void check_parms(void) | |||
1290 | } | 1278 | } |
1291 | } | 1279 | } |
1292 | if (do_nmi && !nmi_handler_registered) { | 1280 | if (do_nmi && !nmi_handler_registered) { |
1293 | rv = register_die_notifier(&ipmi_nmi_handler); | 1281 | rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, |
1282 | "ipmi"); | ||
1294 | if (rv) { | 1283 | if (rv) { |
1295 | printk(KERN_WARNING PFX | 1284 | printk(KERN_WARNING PFX |
1296 | "Can't register nmi handler\n"); | 1285 | "Can't register nmi handler\n"); |
@@ -1298,7 +1287,7 @@ static void check_parms(void) | |||
1298 | } else | 1287 | } else |
1299 | nmi_handler_registered = 1; | 1288 | nmi_handler_registered = 1; |
1300 | } else if (!do_nmi && nmi_handler_registered) { | 1289 | } else if (!do_nmi && nmi_handler_registered) { |
1301 | unregister_die_notifier(&ipmi_nmi_handler); | 1290 | unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); |
1302 | nmi_handler_registered = 0; | 1291 | nmi_handler_registered = 0; |
1303 | } | 1292 | } |
1304 | #endif | 1293 | #endif |
@@ -1336,7 +1325,7 @@ static int __init ipmi_wdog_init(void) | |||
1336 | if (rv) { | 1325 | if (rv) { |
1337 | #ifdef HAVE_DIE_NMI | 1326 | #ifdef HAVE_DIE_NMI |
1338 | if (nmi_handler_registered) | 1327 | if (nmi_handler_registered) |
1339 | unregister_die_notifier(&ipmi_nmi_handler); | 1328 | unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); |
1340 | #endif | 1329 | #endif |
1341 | atomic_notifier_chain_unregister(&panic_notifier_list, | 1330 | atomic_notifier_chain_unregister(&panic_notifier_list, |
1342 | &wdog_panic_notifier); | 1331 | &wdog_panic_notifier); |
@@ -1357,7 +1346,7 @@ static void __exit ipmi_wdog_exit(void) | |||
1357 | 1346 | ||
1358 | #ifdef HAVE_DIE_NMI | 1347 | #ifdef HAVE_DIE_NMI |
1359 | if (nmi_handler_registered) | 1348 | if (nmi_handler_registered) |
1360 | unregister_die_notifier(&ipmi_nmi_handler); | 1349 | unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); |
1361 | #endif | 1350 | #endif |
1362 | 1351 | ||
1363 | atomic_notifier_chain_unregister(&panic_notifier_list, | 1352 | atomic_notifier_chain_unregister(&panic_notifier_list, |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 809cbda03d7a..3774c9b8dac9 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/notifier.h> | 35 | #include <linux/notifier.h> |
36 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | 37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
38 | #include <asm/nmi.h> | ||
38 | 39 | ||
39 | #define HPWDT_VERSION "1.3.0" | 40 | #define HPWDT_VERSION "1.3.0" |
40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | 41 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) |
@@ -477,15 +478,11 @@ static int hpwdt_time_left(void) | |||
477 | /* | 478 | /* |
478 | * NMI Handler | 479 | * NMI Handler |
479 | */ | 480 | */ |
480 | static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | 481 | static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) |
481 | void *data) | ||
482 | { | 482 | { |
483 | unsigned long rom_pl; | 483 | unsigned long rom_pl; |
484 | static int die_nmi_called; | 484 | static int die_nmi_called; |
485 | 485 | ||
486 | if (ulReason != DIE_NMIUNKNOWN) | ||
487 | goto out; | ||
488 | |||
489 | if (!hpwdt_nmi_decoding) | 486 | if (!hpwdt_nmi_decoding) |
490 | goto out; | 487 | goto out; |
491 | 488 | ||
@@ -508,7 +505,7 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | |||
508 | "Management Log for details.\n"); | 505 | "Management Log for details.\n"); |
509 | 506 | ||
510 | out: | 507 | out: |
511 | return NOTIFY_OK; | 508 | return NMI_DONE; |
512 | } | 509 | } |
513 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | 510 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
514 | 511 | ||
@@ -648,13 +645,6 @@ static struct miscdevice hpwdt_miscdev = { | |||
648 | .fops = &hpwdt_fops, | 645 | .fops = &hpwdt_fops, |
649 | }; | 646 | }; |
650 | 647 | ||
651 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
652 | static struct notifier_block die_notifier = { | ||
653 | .notifier_call = hpwdt_pretimeout, | ||
654 | .priority = 0, | ||
655 | }; | ||
656 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
657 | |||
658 | /* | 648 | /* |
659 | * Init & Exit | 649 | * Init & Exit |
660 | */ | 650 | */ |
@@ -740,10 +730,9 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | |||
740 | * die notify list to handle a critical NMI. The default is to | 730 | * die notify list to handle a critical NMI. The default is to |
741 | * be last so other users of the NMI signal can function. | 731 | * be last so other users of the NMI signal can function. |
742 | */ | 732 | */ |
743 | if (priority) | 733 | retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, |
744 | die_notifier.priority = 0x7FFFFFFF; | 734 | (priority) ? NMI_FLAG_FIRST : 0, |
745 | 735 | "hpwdt"); | |
746 | retval = register_die_notifier(&die_notifier); | ||
747 | if (retval != 0) { | 736 | if (retval != 0) { |
748 | dev_warn(&dev->dev, | 737 | dev_warn(&dev->dev, |
749 | "Unable to register a die notifier (err=%d).\n", | 738 | "Unable to register a die notifier (err=%d).\n", |
@@ -763,7 +752,7 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | |||
763 | 752 | ||
764 | static void hpwdt_exit_nmi_decoding(void) | 753 | static void hpwdt_exit_nmi_decoding(void) |
765 | { | 754 | { |
766 | unregister_die_notifier(&die_notifier); | 755 | unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); |
767 | if (cru_rom_addr) | 756 | if (cru_rom_addr) |
768 | iounmap(cru_rom_addr); | 757 | iounmap(cru_rom_addr); |
769 | } | 758 | } |
diff --git a/include/linux/module.h b/include/linux/module.h index 1c30087a2d81..863921637d9f 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -580,9 +580,6 @@ int unregister_module_notifier(struct notifier_block * nb); | |||
580 | 580 | ||
581 | extern void print_modules(void); | 581 | extern void print_modules(void); |
582 | 582 | ||
583 | extern void module_update_tracepoints(void); | ||
584 | extern int module_get_iter_tracepoints(struct tracepoint_iter *iter); | ||
585 | |||
586 | #else /* !CONFIG_MODULES... */ | 583 | #else /* !CONFIG_MODULES... */ |
587 | #define EXPORT_SYMBOL(sym) | 584 | #define EXPORT_SYMBOL(sym) |
588 | #define EXPORT_SYMBOL_GPL(sym) | 585 | #define EXPORT_SYMBOL_GPL(sym) |
@@ -698,15 +695,6 @@ static inline int unregister_module_notifier(struct notifier_block * nb) | |||
698 | static inline void print_modules(void) | 695 | static inline void print_modules(void) |
699 | { | 696 | { |
700 | } | 697 | } |
701 | |||
702 | static inline void module_update_tracepoints(void) | ||
703 | { | ||
704 | } | ||
705 | |||
706 | static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter) | ||
707 | { | ||
708 | return 0; | ||
709 | } | ||
710 | #endif /* CONFIG_MODULES */ | 698 | #endif /* CONFIG_MODULES */ |
711 | 699 | ||
712 | #ifdef CONFIG_SYSFS | 700 | #ifdef CONFIG_SYSFS |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c816075c01ce..1e9ebe5e0091 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -220,7 +220,10 @@ struct perf_event_attr { | |||
220 | mmap_data : 1, /* non-exec mmap data */ | 220 | mmap_data : 1, /* non-exec mmap data */ |
221 | sample_id_all : 1, /* sample_type all events */ | 221 | sample_id_all : 1, /* sample_type all events */ |
222 | 222 | ||
223 | __reserved_1 : 45; | 223 | exclude_host : 1, /* don't count in host */ |
224 | exclude_guest : 1, /* don't count in guest */ | ||
225 | |||
226 | __reserved_1 : 43; | ||
224 | 227 | ||
225 | union { | 228 | union { |
226 | __u32 wakeup_events; /* wakeup every n events */ | 229 | __u32 wakeup_events; /* wakeup every n events */ |
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index b891de96000f..67be0376d8e3 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h | |||
@@ -154,6 +154,8 @@ void ring_buffer_record_enable(struct ring_buffer *buffer); | |||
154 | void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu); | 154 | void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu); |
155 | void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu); | 155 | void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu); |
156 | 156 | ||
157 | unsigned long ring_buffer_oldest_event_ts(struct ring_buffer *buffer, int cpu); | ||
158 | unsigned long ring_buffer_bytes_cpu(struct ring_buffer *buffer, int cpu); | ||
157 | unsigned long ring_buffer_entries(struct ring_buffer *buffer); | 159 | unsigned long ring_buffer_entries(struct ring_buffer *buffer); |
158 | unsigned long ring_buffer_overruns(struct ring_buffer *buffer); | 160 | unsigned long ring_buffer_overruns(struct ring_buffer *buffer); |
159 | unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu); | 161 | unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu); |
diff --git a/include/linux/trace_clock.h b/include/linux/trace_clock.h index 7a8130384087..4eb490237d4c 100644 --- a/include/linux/trace_clock.h +++ b/include/linux/trace_clock.h | |||
@@ -15,5 +15,6 @@ | |||
15 | extern u64 notrace trace_clock_local(void); | 15 | extern u64 notrace trace_clock_local(void); |
16 | extern u64 notrace trace_clock(void); | 16 | extern u64 notrace trace_clock(void); |
17 | extern u64 notrace trace_clock_global(void); | 17 | extern u64 notrace trace_clock_global(void); |
18 | extern u64 notrace trace_clock_counter(void); | ||
18 | 19 | ||
19 | #endif /* _LINUX_TRACE_CLOCK_H */ | 20 | #endif /* _LINUX_TRACE_CLOCK_H */ |
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index d530a4460a0b..df0a779c1bbd 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h | |||
@@ -54,8 +54,18 @@ extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe, | |||
54 | void *data); | 54 | void *data); |
55 | extern void tracepoint_probe_update_all(void); | 55 | extern void tracepoint_probe_update_all(void); |
56 | 56 | ||
57 | #ifdef CONFIG_MODULES | ||
58 | struct tp_module { | ||
59 | struct list_head list; | ||
60 | unsigned int num_tracepoints; | ||
61 | struct tracepoint * const *tracepoints_ptrs; | ||
62 | }; | ||
63 | #endif /* CONFIG_MODULES */ | ||
64 | |||
57 | struct tracepoint_iter { | 65 | struct tracepoint_iter { |
58 | struct module *module; | 66 | #ifdef CONFIG_MODULES |
67 | struct tp_module *module; | ||
68 | #endif /* CONFIG_MODULES */ | ||
59 | struct tracepoint * const *tracepoint; | 69 | struct tracepoint * const *tracepoint; |
60 | }; | 70 | }; |
61 | 71 | ||
@@ -63,8 +73,6 @@ extern void tracepoint_iter_start(struct tracepoint_iter *iter); | |||
63 | extern void tracepoint_iter_next(struct tracepoint_iter *iter); | 73 | extern void tracepoint_iter_next(struct tracepoint_iter *iter); |
64 | extern void tracepoint_iter_stop(struct tracepoint_iter *iter); | 74 | extern void tracepoint_iter_stop(struct tracepoint_iter *iter); |
65 | extern void tracepoint_iter_reset(struct tracepoint_iter *iter); | 75 | extern void tracepoint_iter_reset(struct tracepoint_iter *iter); |
66 | extern int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, | ||
67 | struct tracepoint * const *begin, struct tracepoint * const *end); | ||
68 | 76 | ||
69 | /* | 77 | /* |
70 | * tracepoint_synchronize_unregister must be called between the last tracepoint | 78 | * tracepoint_synchronize_unregister must be called between the last tracepoint |
@@ -78,17 +86,6 @@ static inline void tracepoint_synchronize_unregister(void) | |||
78 | 86 | ||
79 | #define PARAMS(args...) args | 87 | #define PARAMS(args...) args |
80 | 88 | ||
81 | #ifdef CONFIG_TRACEPOINTS | ||
82 | extern | ||
83 | void tracepoint_update_probe_range(struct tracepoint * const *begin, | ||
84 | struct tracepoint * const *end); | ||
85 | #else | ||
86 | static inline | ||
87 | void tracepoint_update_probe_range(struct tracepoint * const *begin, | ||
88 | struct tracepoint * const *end) | ||
89 | { } | ||
90 | #endif /* CONFIG_TRACEPOINTS */ | ||
91 | |||
92 | #endif /* _LINUX_TRACEPOINT_H */ | 89 | #endif /* _LINUX_TRACEPOINT_H */ |
93 | 90 | ||
94 | /* | 91 | /* |
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 533c49f48047..769724944fc6 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h | |||
@@ -711,6 +711,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call | |||
711 | #undef __perf_count | 711 | #undef __perf_count |
712 | #define __perf_count(c) __count = (c) | 712 | #define __perf_count(c) __count = (c) |
713 | 713 | ||
714 | #undef TP_perf_assign | ||
715 | #define TP_perf_assign(args...) args | ||
716 | |||
714 | #undef DECLARE_EVENT_CLASS | 717 | #undef DECLARE_EVENT_CLASS |
715 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ | 718 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ |
716 | static notrace void \ | 719 | static notrace void \ |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 0f857782d06f..d1a1bee35228 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/hardirq.h> | 29 | #include <linux/hardirq.h> |
30 | #include <linux/rculist.h> | 30 | #include <linux/rculist.h> |
31 | #include <linux/uaccess.h> | 31 | #include <linux/uaccess.h> |
32 | #include <linux/suspend.h> | ||
32 | #include <linux/syscalls.h> | 33 | #include <linux/syscalls.h> |
33 | #include <linux/anon_inodes.h> | 34 | #include <linux/anon_inodes.h> |
34 | #include <linux/kernel_stat.h> | 35 | #include <linux/kernel_stat.h> |
@@ -5758,6 +5759,7 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
5758 | pmu = idr_find(&pmu_idr, event->attr.type); | 5759 | pmu = idr_find(&pmu_idr, event->attr.type); |
5759 | rcu_read_unlock(); | 5760 | rcu_read_unlock(); |
5760 | if (pmu) { | 5761 | if (pmu) { |
5762 | event->pmu = pmu; | ||
5761 | ret = pmu->event_init(event); | 5763 | ret = pmu->event_init(event); |
5762 | if (ret) | 5764 | if (ret) |
5763 | pmu = ERR_PTR(ret); | 5765 | pmu = ERR_PTR(ret); |
@@ -5765,6 +5767,7 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
5765 | } | 5767 | } |
5766 | 5768 | ||
5767 | list_for_each_entry_rcu(pmu, &pmus, entry) { | 5769 | list_for_each_entry_rcu(pmu, &pmus, entry) { |
5770 | event->pmu = pmu; | ||
5768 | ret = pmu->event_init(event); | 5771 | ret = pmu->event_init(event); |
5769 | if (!ret) | 5772 | if (!ret) |
5770 | goto unlock; | 5773 | goto unlock; |
@@ -5891,8 +5894,6 @@ done: | |||
5891 | return ERR_PTR(err); | 5894 | return ERR_PTR(err); |
5892 | } | 5895 | } |
5893 | 5896 | ||
5894 | event->pmu = pmu; | ||
5895 | |||
5896 | if (!event->parent) { | 5897 | if (!event->parent) { |
5897 | if (event->attach_state & PERF_ATTACH_TASK) | 5898 | if (event->attach_state & PERF_ATTACH_TASK) |
5898 | jump_label_inc(&perf_sched_events); | 5899 | jump_label_inc(&perf_sched_events); |
@@ -6852,7 +6853,7 @@ static void __cpuinit perf_event_init_cpu(int cpu) | |||
6852 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); | 6853 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); |
6853 | 6854 | ||
6854 | mutex_lock(&swhash->hlist_mutex); | 6855 | mutex_lock(&swhash->hlist_mutex); |
6855 | if (swhash->hlist_refcount > 0) { | 6856 | if (swhash->hlist_refcount > 0 && !swhash->swevent_hlist) { |
6856 | struct swevent_hlist *hlist; | 6857 | struct swevent_hlist *hlist; |
6857 | 6858 | ||
6858 | hlist = kzalloc_node(sizeof(*hlist), GFP_KERNEL, cpu_to_node(cpu)); | 6859 | hlist = kzalloc_node(sizeof(*hlist), GFP_KERNEL, cpu_to_node(cpu)); |
@@ -6941,7 +6942,14 @@ perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) | |||
6941 | { | 6942 | { |
6942 | unsigned int cpu = (long)hcpu; | 6943 | unsigned int cpu = (long)hcpu; |
6943 | 6944 | ||
6944 | switch (action & ~CPU_TASKS_FROZEN) { | 6945 | /* |
6946 | * Ignore suspend/resume action, the perf_pm_notifier will | ||
6947 | * take care of that. | ||
6948 | */ | ||
6949 | if (action & CPU_TASKS_FROZEN) | ||
6950 | return NOTIFY_OK; | ||
6951 | |||
6952 | switch (action) { | ||
6945 | 6953 | ||
6946 | case CPU_UP_PREPARE: | 6954 | case CPU_UP_PREPARE: |
6947 | case CPU_DOWN_FAILED: | 6955 | case CPU_DOWN_FAILED: |
@@ -6960,6 +6968,90 @@ perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) | |||
6960 | return NOTIFY_OK; | 6968 | return NOTIFY_OK; |
6961 | } | 6969 | } |
6962 | 6970 | ||
6971 | static void perf_pm_resume_cpu(void *unused) | ||
6972 | { | ||
6973 | struct perf_cpu_context *cpuctx; | ||
6974 | struct perf_event_context *ctx; | ||
6975 | struct pmu *pmu; | ||
6976 | int idx; | ||
6977 | |||
6978 | idx = srcu_read_lock(&pmus_srcu); | ||
6979 | list_for_each_entry_rcu(pmu, &pmus, entry) { | ||
6980 | cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | ||
6981 | ctx = cpuctx->task_ctx; | ||
6982 | |||
6983 | perf_ctx_lock(cpuctx, ctx); | ||
6984 | perf_pmu_disable(cpuctx->ctx.pmu); | ||
6985 | |||
6986 | cpu_ctx_sched_out(cpuctx, EVENT_ALL); | ||
6987 | if (ctx) | ||
6988 | ctx_sched_out(ctx, cpuctx, EVENT_ALL); | ||
6989 | |||
6990 | perf_pmu_enable(cpuctx->ctx.pmu); | ||
6991 | perf_ctx_unlock(cpuctx, ctx); | ||
6992 | } | ||
6993 | srcu_read_unlock(&pmus_srcu, idx); | ||
6994 | } | ||
6995 | |||
6996 | static void perf_pm_suspend_cpu(void *unused) | ||
6997 | { | ||
6998 | struct perf_cpu_context *cpuctx; | ||
6999 | struct perf_event_context *ctx; | ||
7000 | struct pmu *pmu; | ||
7001 | int idx; | ||
7002 | |||
7003 | idx = srcu_read_lock(&pmus_srcu); | ||
7004 | list_for_each_entry_rcu(pmu, &pmus, entry) { | ||
7005 | cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | ||
7006 | ctx = cpuctx->task_ctx; | ||
7007 | |||
7008 | perf_ctx_lock(cpuctx, ctx); | ||
7009 | perf_pmu_disable(cpuctx->ctx.pmu); | ||
7010 | |||
7011 | perf_event_sched_in(cpuctx, ctx, current); | ||
7012 | |||
7013 | perf_pmu_enable(cpuctx->ctx.pmu); | ||
7014 | perf_ctx_unlock(cpuctx, ctx); | ||
7015 | } | ||
7016 | srcu_read_unlock(&pmus_srcu, idx); | ||
7017 | } | ||
7018 | |||
7019 | static int perf_resume(void) | ||
7020 | { | ||
7021 | get_online_cpus(); | ||
7022 | smp_call_function(perf_pm_resume_cpu, NULL, 1); | ||
7023 | put_online_cpus(); | ||
7024 | |||
7025 | return NOTIFY_OK; | ||
7026 | } | ||
7027 | |||
7028 | static int perf_suspend(void) | ||
7029 | { | ||
7030 | get_online_cpus(); | ||
7031 | smp_call_function(perf_pm_suspend_cpu, NULL, 1); | ||
7032 | put_online_cpus(); | ||
7033 | |||
7034 | return NOTIFY_OK; | ||
7035 | } | ||
7036 | |||
7037 | static int perf_pm(struct notifier_block *self, unsigned long action, void *ptr) | ||
7038 | { | ||
7039 | switch (action) { | ||
7040 | case PM_POST_HIBERNATION: | ||
7041 | case PM_POST_SUSPEND: | ||
7042 | return perf_resume(); | ||
7043 | case PM_HIBERNATION_PREPARE: | ||
7044 | case PM_SUSPEND_PREPARE: | ||
7045 | return perf_suspend(); | ||
7046 | default: | ||
7047 | return NOTIFY_DONE; | ||
7048 | } | ||
7049 | } | ||
7050 | |||
7051 | static struct notifier_block perf_pm_notifier = { | ||
7052 | .notifier_call = perf_pm, | ||
7053 | }; | ||
7054 | |||
6963 | void __init perf_event_init(void) | 7055 | void __init perf_event_init(void) |
6964 | { | 7056 | { |
6965 | int ret; | 7057 | int ret; |
@@ -6974,6 +7066,7 @@ void __init perf_event_init(void) | |||
6974 | perf_tp_register(); | 7066 | perf_tp_register(); |
6975 | perf_cpu_notifier(perf_cpu_notify); | 7067 | perf_cpu_notifier(perf_cpu_notify); |
6976 | register_reboot_notifier(&perf_reboot_notifier); | 7068 | register_reboot_notifier(&perf_reboot_notifier); |
7069 | register_pm_notifier(&perf_pm_notifier); | ||
6977 | 7070 | ||
6978 | ret = init_hw_breakpoint(); | 7071 | ret = init_hw_breakpoint(); |
6979 | WARN(ret, "hw_breakpoint initialization failed with: %d", ret); | 7072 | WARN(ret, "hw_breakpoint initialization failed with: %d", ret); |
diff --git a/kernel/module.c b/kernel/module.c index 04379f92f843..93342d992f34 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -3487,50 +3487,3 @@ void module_layout(struct module *mod, | |||
3487 | } | 3487 | } |
3488 | EXPORT_SYMBOL(module_layout); | 3488 | EXPORT_SYMBOL(module_layout); |
3489 | #endif | 3489 | #endif |
3490 | |||
3491 | #ifdef CONFIG_TRACEPOINTS | ||
3492 | void module_update_tracepoints(void) | ||
3493 | { | ||
3494 | struct module *mod; | ||
3495 | |||
3496 | mutex_lock(&module_mutex); | ||
3497 | list_for_each_entry(mod, &modules, list) | ||
3498 | if (!mod->taints) | ||
3499 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | ||
3500 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
3501 | mutex_unlock(&module_mutex); | ||
3502 | } | ||
3503 | |||
3504 | /* | ||
3505 | * Returns 0 if current not found. | ||
3506 | * Returns 1 if current found. | ||
3507 | */ | ||
3508 | int module_get_iter_tracepoints(struct tracepoint_iter *iter) | ||
3509 | { | ||
3510 | struct module *iter_mod; | ||
3511 | int found = 0; | ||
3512 | |||
3513 | mutex_lock(&module_mutex); | ||
3514 | list_for_each_entry(iter_mod, &modules, list) { | ||
3515 | if (!iter_mod->taints) { | ||
3516 | /* | ||
3517 | * Sorted module list | ||
3518 | */ | ||
3519 | if (iter_mod < iter->module) | ||
3520 | continue; | ||
3521 | else if (iter_mod > iter->module) | ||
3522 | iter->tracepoint = NULL; | ||
3523 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
3524 | iter_mod->tracepoints_ptrs, | ||
3525 | iter_mod->tracepoints_ptrs | ||
3526 | + iter_mod->num_tracepoints); | ||
3527 | if (found) { | ||
3528 | iter->module = iter_mod; | ||
3529 | break; | ||
3530 | } | ||
3531 | } | ||
3532 | } | ||
3533 | mutex_unlock(&module_mutex); | ||
3534 | return found; | ||
3535 | } | ||
3536 | #endif | ||
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index f49405f842f4..5f39a07fe5ea 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -15,6 +15,8 @@ ifdef CONFIG_TRACING_BRANCHES | |||
15 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING | 15 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING |
16 | endif | 16 | endif |
17 | 17 | ||
18 | CFLAGS_trace_events_filter.o := -I$(src) | ||
19 | |||
18 | # | 20 | # |
19 | # Make the trace clocks available generally: it's infrastructure | 21 | # Make the trace clocks available generally: it's infrastructure |
20 | # relied on by ptrace for example: | 22 | # relied on by ptrace for example: |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c3e4575e7829..077d85387908 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -3863,6 +3863,14 @@ void ftrace_kill(void) | |||
3863 | } | 3863 | } |
3864 | 3864 | ||
3865 | /** | 3865 | /** |
3866 | * Test if ftrace is dead or not. | ||
3867 | */ | ||
3868 | int ftrace_is_dead(void) | ||
3869 | { | ||
3870 | return ftrace_disabled; | ||
3871 | } | ||
3872 | |||
3873 | /** | ||
3866 | * register_ftrace_function - register a function for profiling | 3874 | * register_ftrace_function - register a function for profiling |
3867 | * @ops - ops structure that holds the function for profiling. | 3875 | * @ops - ops structure that holds the function for profiling. |
3868 | * | 3876 | * |
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index f2f821acc597..f5b7b5c1195b 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c | |||
@@ -488,12 +488,14 @@ struct ring_buffer_per_cpu { | |||
488 | struct buffer_page *reader_page; | 488 | struct buffer_page *reader_page; |
489 | unsigned long lost_events; | 489 | unsigned long lost_events; |
490 | unsigned long last_overrun; | 490 | unsigned long last_overrun; |
491 | local_t entries_bytes; | ||
491 | local_t commit_overrun; | 492 | local_t commit_overrun; |
492 | local_t overrun; | 493 | local_t overrun; |
493 | local_t entries; | 494 | local_t entries; |
494 | local_t committing; | 495 | local_t committing; |
495 | local_t commits; | 496 | local_t commits; |
496 | unsigned long read; | 497 | unsigned long read; |
498 | unsigned long read_bytes; | ||
497 | u64 write_stamp; | 499 | u64 write_stamp; |
498 | u64 read_stamp; | 500 | u64 read_stamp; |
499 | }; | 501 | }; |
@@ -1708,6 +1710,7 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer, | |||
1708 | * the counters. | 1710 | * the counters. |
1709 | */ | 1711 | */ |
1710 | local_add(entries, &cpu_buffer->overrun); | 1712 | local_add(entries, &cpu_buffer->overrun); |
1713 | local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes); | ||
1711 | 1714 | ||
1712 | /* | 1715 | /* |
1713 | * The entries will be zeroed out when we move the | 1716 | * The entries will be zeroed out when we move the |
@@ -1863,6 +1866,9 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, | |||
1863 | event = __rb_page_index(tail_page, tail); | 1866 | event = __rb_page_index(tail_page, tail); |
1864 | kmemcheck_annotate_bitfield(event, bitfield); | 1867 | kmemcheck_annotate_bitfield(event, bitfield); |
1865 | 1868 | ||
1869 | /* account for padding bytes */ | ||
1870 | local_add(BUF_PAGE_SIZE - tail, &cpu_buffer->entries_bytes); | ||
1871 | |||
1866 | /* | 1872 | /* |
1867 | * Save the original length to the meta data. | 1873 | * Save the original length to the meta data. |
1868 | * This will be used by the reader to add lost event | 1874 | * This will be used by the reader to add lost event |
@@ -2054,6 +2060,9 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, | |||
2054 | if (!tail) | 2060 | if (!tail) |
2055 | tail_page->page->time_stamp = ts; | 2061 | tail_page->page->time_stamp = ts; |
2056 | 2062 | ||
2063 | /* account for these added bytes */ | ||
2064 | local_add(length, &cpu_buffer->entries_bytes); | ||
2065 | |||
2057 | return event; | 2066 | return event; |
2058 | } | 2067 | } |
2059 | 2068 | ||
@@ -2076,6 +2085,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, | |||
2076 | if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) { | 2085 | if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) { |
2077 | unsigned long write_mask = | 2086 | unsigned long write_mask = |
2078 | local_read(&bpage->write) & ~RB_WRITE_MASK; | 2087 | local_read(&bpage->write) & ~RB_WRITE_MASK; |
2088 | unsigned long event_length = rb_event_length(event); | ||
2079 | /* | 2089 | /* |
2080 | * This is on the tail page. It is possible that | 2090 | * This is on the tail page. It is possible that |
2081 | * a write could come in and move the tail page | 2091 | * a write could come in and move the tail page |
@@ -2085,8 +2095,11 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, | |||
2085 | old_index += write_mask; | 2095 | old_index += write_mask; |
2086 | new_index += write_mask; | 2096 | new_index += write_mask; |
2087 | index = local_cmpxchg(&bpage->write, old_index, new_index); | 2097 | index = local_cmpxchg(&bpage->write, old_index, new_index); |
2088 | if (index == old_index) | 2098 | if (index == old_index) { |
2099 | /* update counters */ | ||
2100 | local_sub(event_length, &cpu_buffer->entries_bytes); | ||
2089 | return 1; | 2101 | return 1; |
2102 | } | ||
2090 | } | 2103 | } |
2091 | 2104 | ||
2092 | /* could not discard */ | 2105 | /* could not discard */ |
@@ -2661,6 +2674,58 @@ rb_num_of_entries(struct ring_buffer_per_cpu *cpu_buffer) | |||
2661 | } | 2674 | } |
2662 | 2675 | ||
2663 | /** | 2676 | /** |
2677 | * ring_buffer_oldest_event_ts - get the oldest event timestamp from the buffer | ||
2678 | * @buffer: The ring buffer | ||
2679 | * @cpu: The per CPU buffer to read from. | ||
2680 | */ | ||
2681 | unsigned long ring_buffer_oldest_event_ts(struct ring_buffer *buffer, int cpu) | ||
2682 | { | ||
2683 | unsigned long flags; | ||
2684 | struct ring_buffer_per_cpu *cpu_buffer; | ||
2685 | struct buffer_page *bpage; | ||
2686 | unsigned long ret; | ||
2687 | |||
2688 | if (!cpumask_test_cpu(cpu, buffer->cpumask)) | ||
2689 | return 0; | ||
2690 | |||
2691 | cpu_buffer = buffer->buffers[cpu]; | ||
2692 | raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); | ||
2693 | /* | ||
2694 | * if the tail is on reader_page, oldest time stamp is on the reader | ||
2695 | * page | ||
2696 | */ | ||
2697 | if (cpu_buffer->tail_page == cpu_buffer->reader_page) | ||
2698 | bpage = cpu_buffer->reader_page; | ||
2699 | else | ||
2700 | bpage = rb_set_head_page(cpu_buffer); | ||
2701 | ret = bpage->page->time_stamp; | ||
2702 | raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); | ||
2703 | |||
2704 | return ret; | ||
2705 | } | ||
2706 | EXPORT_SYMBOL_GPL(ring_buffer_oldest_event_ts); | ||
2707 | |||
2708 | /** | ||
2709 | * ring_buffer_bytes_cpu - get the number of bytes consumed in a cpu buffer | ||
2710 | * @buffer: The ring buffer | ||
2711 | * @cpu: The per CPU buffer to read from. | ||
2712 | */ | ||
2713 | unsigned long ring_buffer_bytes_cpu(struct ring_buffer *buffer, int cpu) | ||
2714 | { | ||
2715 | struct ring_buffer_per_cpu *cpu_buffer; | ||
2716 | unsigned long ret; | ||
2717 | |||
2718 | if (!cpumask_test_cpu(cpu, buffer->cpumask)) | ||
2719 | return 0; | ||
2720 | |||
2721 | cpu_buffer = buffer->buffers[cpu]; | ||
2722 | ret = local_read(&cpu_buffer->entries_bytes) - cpu_buffer->read_bytes; | ||
2723 | |||
2724 | return ret; | ||
2725 | } | ||
2726 | EXPORT_SYMBOL_GPL(ring_buffer_bytes_cpu); | ||
2727 | |||
2728 | /** | ||
2664 | * ring_buffer_entries_cpu - get the number of entries in a cpu buffer | 2729 | * ring_buffer_entries_cpu - get the number of entries in a cpu buffer |
2665 | * @buffer: The ring buffer | 2730 | * @buffer: The ring buffer |
2666 | * @cpu: The per CPU buffer to get the entries from. | 2731 | * @cpu: The per CPU buffer to get the entries from. |
@@ -3527,11 +3592,13 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) | |||
3527 | cpu_buffer->reader_page->read = 0; | 3592 | cpu_buffer->reader_page->read = 0; |
3528 | 3593 | ||
3529 | local_set(&cpu_buffer->commit_overrun, 0); | 3594 | local_set(&cpu_buffer->commit_overrun, 0); |
3595 | local_set(&cpu_buffer->entries_bytes, 0); | ||
3530 | local_set(&cpu_buffer->overrun, 0); | 3596 | local_set(&cpu_buffer->overrun, 0); |
3531 | local_set(&cpu_buffer->entries, 0); | 3597 | local_set(&cpu_buffer->entries, 0); |
3532 | local_set(&cpu_buffer->committing, 0); | 3598 | local_set(&cpu_buffer->committing, 0); |
3533 | local_set(&cpu_buffer->commits, 0); | 3599 | local_set(&cpu_buffer->commits, 0); |
3534 | cpu_buffer->read = 0; | 3600 | cpu_buffer->read = 0; |
3601 | cpu_buffer->read_bytes = 0; | ||
3535 | 3602 | ||
3536 | cpu_buffer->write_stamp = 0; | 3603 | cpu_buffer->write_stamp = 0; |
3537 | cpu_buffer->read_stamp = 0; | 3604 | cpu_buffer->read_stamp = 0; |
@@ -3918,6 +3985,7 @@ int ring_buffer_read_page(struct ring_buffer *buffer, | |||
3918 | } else { | 3985 | } else { |
3919 | /* update the entry counter */ | 3986 | /* update the entry counter */ |
3920 | cpu_buffer->read += rb_page_entries(reader); | 3987 | cpu_buffer->read += rb_page_entries(reader); |
3988 | cpu_buffer->read_bytes += BUF_PAGE_SIZE; | ||
3921 | 3989 | ||
3922 | /* swap the pages */ | 3990 | /* swap the pages */ |
3923 | rb_init_page(bpage); | 3991 | rb_init_page(bpage); |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0c8bdeeb358b..f2bd275bb60f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -435,6 +435,7 @@ static struct { | |||
435 | } trace_clocks[] = { | 435 | } trace_clocks[] = { |
436 | { trace_clock_local, "local" }, | 436 | { trace_clock_local, "local" }, |
437 | { trace_clock_global, "global" }, | 437 | { trace_clock_global, "global" }, |
438 | { trace_clock_counter, "counter" }, | ||
438 | }; | 439 | }; |
439 | 440 | ||
440 | int trace_clock_id; | 441 | int trace_clock_id; |
@@ -2159,6 +2160,14 @@ void trace_default_header(struct seq_file *m) | |||
2159 | } | 2160 | } |
2160 | } | 2161 | } |
2161 | 2162 | ||
2163 | static void test_ftrace_alive(struct seq_file *m) | ||
2164 | { | ||
2165 | if (!ftrace_is_dead()) | ||
2166 | return; | ||
2167 | seq_printf(m, "# WARNING: FUNCTION TRACING IS CORRUPTED\n"); | ||
2168 | seq_printf(m, "# MAY BE MISSING FUNCTION EVENTS\n"); | ||
2169 | } | ||
2170 | |||
2162 | static int s_show(struct seq_file *m, void *v) | 2171 | static int s_show(struct seq_file *m, void *v) |
2163 | { | 2172 | { |
2164 | struct trace_iterator *iter = v; | 2173 | struct trace_iterator *iter = v; |
@@ -2168,6 +2177,7 @@ static int s_show(struct seq_file *m, void *v) | |||
2168 | if (iter->tr) { | 2177 | if (iter->tr) { |
2169 | seq_printf(m, "# tracer: %s\n", iter->trace->name); | 2178 | seq_printf(m, "# tracer: %s\n", iter->trace->name); |
2170 | seq_puts(m, "#\n"); | 2179 | seq_puts(m, "#\n"); |
2180 | test_ftrace_alive(m); | ||
2171 | } | 2181 | } |
2172 | if (iter->trace && iter->trace->print_header) | 2182 | if (iter->trace && iter->trace->print_header) |
2173 | iter->trace->print_header(m); | 2183 | iter->trace->print_header(m); |
@@ -2710,9 +2720,9 @@ static const char readme_msg[] = | |||
2710 | "# cat /sys/kernel/debug/tracing/trace_options\n" | 2720 | "# cat /sys/kernel/debug/tracing/trace_options\n" |
2711 | "noprint-parent nosym-offset nosym-addr noverbose\n" | 2721 | "noprint-parent nosym-offset nosym-addr noverbose\n" |
2712 | "# echo print-parent > /sys/kernel/debug/tracing/trace_options\n" | 2722 | "# echo print-parent > /sys/kernel/debug/tracing/trace_options\n" |
2713 | "# echo 1 > /sys/kernel/debug/tracing/tracing_enabled\n" | 2723 | "# echo 1 > /sys/kernel/debug/tracing/tracing_on\n" |
2714 | "# cat /sys/kernel/debug/tracing/trace > /tmp/trace.txt\n" | 2724 | "# cat /sys/kernel/debug/tracing/trace > /tmp/trace.txt\n" |
2715 | "# echo 0 > /sys/kernel/debug/tracing/tracing_enabled\n" | 2725 | "# echo 0 > /sys/kernel/debug/tracing/tracing_on\n" |
2716 | ; | 2726 | ; |
2717 | 2727 | ||
2718 | static ssize_t | 2728 | static ssize_t |
@@ -3569,6 +3579,30 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, | |||
3569 | } | 3579 | } |
3570 | 3580 | ||
3571 | static ssize_t | 3581 | static ssize_t |
3582 | tracing_total_entries_read(struct file *filp, char __user *ubuf, | ||
3583 | size_t cnt, loff_t *ppos) | ||
3584 | { | ||
3585 | struct trace_array *tr = filp->private_data; | ||
3586 | char buf[64]; | ||
3587 | int r, cpu; | ||
3588 | unsigned long size = 0, expanded_size = 0; | ||
3589 | |||
3590 | mutex_lock(&trace_types_lock); | ||
3591 | for_each_tracing_cpu(cpu) { | ||
3592 | size += tr->entries >> 10; | ||
3593 | if (!ring_buffer_expanded) | ||
3594 | expanded_size += trace_buf_size >> 10; | ||
3595 | } | ||
3596 | if (ring_buffer_expanded) | ||
3597 | r = sprintf(buf, "%lu\n", size); | ||
3598 | else | ||
3599 | r = sprintf(buf, "%lu (expanded: %lu)\n", size, expanded_size); | ||
3600 | mutex_unlock(&trace_types_lock); | ||
3601 | |||
3602 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
3603 | } | ||
3604 | |||
3605 | static ssize_t | ||
3572 | tracing_free_buffer_write(struct file *filp, const char __user *ubuf, | 3606 | tracing_free_buffer_write(struct file *filp, const char __user *ubuf, |
3573 | size_t cnt, loff_t *ppos) | 3607 | size_t cnt, loff_t *ppos) |
3574 | { | 3608 | { |
@@ -3594,22 +3628,24 @@ tracing_free_buffer_release(struct inode *inode, struct file *filp) | |||
3594 | return 0; | 3628 | return 0; |
3595 | } | 3629 | } |
3596 | 3630 | ||
3597 | static int mark_printk(const char *fmt, ...) | ||
3598 | { | ||
3599 | int ret; | ||
3600 | va_list args; | ||
3601 | va_start(args, fmt); | ||
3602 | ret = trace_vprintk(0, fmt, args); | ||
3603 | va_end(args); | ||
3604 | return ret; | ||
3605 | } | ||
3606 | |||
3607 | static ssize_t | 3631 | static ssize_t |
3608 | tracing_mark_write(struct file *filp, const char __user *ubuf, | 3632 | tracing_mark_write(struct file *filp, const char __user *ubuf, |
3609 | size_t cnt, loff_t *fpos) | 3633 | size_t cnt, loff_t *fpos) |
3610 | { | 3634 | { |
3611 | char *buf; | 3635 | unsigned long addr = (unsigned long)ubuf; |
3612 | size_t written; | 3636 | struct ring_buffer_event *event; |
3637 | struct ring_buffer *buffer; | ||
3638 | struct print_entry *entry; | ||
3639 | unsigned long irq_flags; | ||
3640 | struct page *pages[2]; | ||
3641 | int nr_pages = 1; | ||
3642 | ssize_t written; | ||
3643 | void *page1; | ||
3644 | void *page2; | ||
3645 | int offset; | ||
3646 | int size; | ||
3647 | int len; | ||
3648 | int ret; | ||
3613 | 3649 | ||
3614 | if (tracing_disabled) | 3650 | if (tracing_disabled) |
3615 | return -EINVAL; | 3651 | return -EINVAL; |
@@ -3617,28 +3653,81 @@ tracing_mark_write(struct file *filp, const char __user *ubuf, | |||
3617 | if (cnt > TRACE_BUF_SIZE) | 3653 | if (cnt > TRACE_BUF_SIZE) |
3618 | cnt = TRACE_BUF_SIZE; | 3654 | cnt = TRACE_BUF_SIZE; |
3619 | 3655 | ||
3620 | buf = kmalloc(cnt + 2, GFP_KERNEL); | 3656 | /* |
3621 | if (buf == NULL) | 3657 | * Userspace is injecting traces into the kernel trace buffer. |
3622 | return -ENOMEM; | 3658 | * We want to be as non intrusive as possible. |
3659 | * To do so, we do not want to allocate any special buffers | ||
3660 | * or take any locks, but instead write the userspace data | ||
3661 | * straight into the ring buffer. | ||
3662 | * | ||
3663 | * First we need to pin the userspace buffer into memory, | ||
3664 | * which, most likely it is, because it just referenced it. | ||
3665 | * But there's no guarantee that it is. By using get_user_pages_fast() | ||
3666 | * and kmap_atomic/kunmap_atomic() we can get access to the | ||
3667 | * pages directly. We then write the data directly into the | ||
3668 | * ring buffer. | ||
3669 | */ | ||
3670 | BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE); | ||
3623 | 3671 | ||
3624 | if (copy_from_user(buf, ubuf, cnt)) { | 3672 | /* check if we cross pages */ |
3625 | kfree(buf); | 3673 | if ((addr & PAGE_MASK) != ((addr + cnt) & PAGE_MASK)) |
3626 | return -EFAULT; | 3674 | nr_pages = 2; |
3675 | |||
3676 | offset = addr & (PAGE_SIZE - 1); | ||
3677 | addr &= PAGE_MASK; | ||
3678 | |||
3679 | ret = get_user_pages_fast(addr, nr_pages, 0, pages); | ||
3680 | if (ret < nr_pages) { | ||
3681 | while (--ret >= 0) | ||
3682 | put_page(pages[ret]); | ||
3683 | written = -EFAULT; | ||
3684 | goto out; | ||
3685 | } | ||
3686 | |||
3687 | page1 = kmap_atomic(pages[0]); | ||
3688 | if (nr_pages == 2) | ||
3689 | page2 = kmap_atomic(pages[1]); | ||
3690 | |||
3691 | local_save_flags(irq_flags); | ||
3692 | size = sizeof(*entry) + cnt + 2; /* possible \n added */ | ||
3693 | buffer = global_trace.buffer; | ||
3694 | event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, | ||
3695 | irq_flags, preempt_count()); | ||
3696 | if (!event) { | ||
3697 | /* Ring buffer disabled, return as if not open for write */ | ||
3698 | written = -EBADF; | ||
3699 | goto out_unlock; | ||
3627 | } | 3700 | } |
3628 | if (buf[cnt-1] != '\n') { | 3701 | |
3629 | buf[cnt] = '\n'; | 3702 | entry = ring_buffer_event_data(event); |
3630 | buf[cnt+1] = '\0'; | 3703 | entry->ip = _THIS_IP_; |
3704 | |||
3705 | if (nr_pages == 2) { | ||
3706 | len = PAGE_SIZE - offset; | ||
3707 | memcpy(&entry->buf, page1 + offset, len); | ||
3708 | memcpy(&entry->buf[len], page2, cnt - len); | ||
3631 | } else | 3709 | } else |
3632 | buf[cnt] = '\0'; | 3710 | memcpy(&entry->buf, page1 + offset, cnt); |
3633 | 3711 | ||
3634 | written = mark_printk("%s", buf); | 3712 | if (entry->buf[cnt - 1] != '\n') { |
3635 | kfree(buf); | 3713 | entry->buf[cnt] = '\n'; |
3636 | *fpos += written; | 3714 | entry->buf[cnt + 1] = '\0'; |
3715 | } else | ||
3716 | entry->buf[cnt] = '\0'; | ||
3717 | |||
3718 | ring_buffer_unlock_commit(buffer, event); | ||
3637 | 3719 | ||
3638 | /* don't tell userspace we wrote more - it might confuse them */ | 3720 | written = cnt; |
3639 | if (written > cnt) | ||
3640 | written = cnt; | ||
3641 | 3721 | ||
3722 | *fpos += written; | ||
3723 | |||
3724 | out_unlock: | ||
3725 | if (nr_pages == 2) | ||
3726 | kunmap_atomic(page2); | ||
3727 | kunmap_atomic(page1); | ||
3728 | while (nr_pages > 0) | ||
3729 | put_page(pages[--nr_pages]); | ||
3730 | out: | ||
3642 | return written; | 3731 | return written; |
3643 | } | 3732 | } |
3644 | 3733 | ||
@@ -3739,6 +3828,12 @@ static const struct file_operations tracing_entries_fops = { | |||
3739 | .llseek = generic_file_llseek, | 3828 | .llseek = generic_file_llseek, |
3740 | }; | 3829 | }; |
3741 | 3830 | ||
3831 | static const struct file_operations tracing_total_entries_fops = { | ||
3832 | .open = tracing_open_generic, | ||
3833 | .read = tracing_total_entries_read, | ||
3834 | .llseek = generic_file_llseek, | ||
3835 | }; | ||
3836 | |||
3742 | static const struct file_operations tracing_free_buffer_fops = { | 3837 | static const struct file_operations tracing_free_buffer_fops = { |
3743 | .write = tracing_free_buffer_write, | 3838 | .write = tracing_free_buffer_write, |
3744 | .release = tracing_free_buffer_release, | 3839 | .release = tracing_free_buffer_release, |
@@ -3808,8 +3903,6 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, | |||
3808 | if (info->read < PAGE_SIZE) | 3903 | if (info->read < PAGE_SIZE) |
3809 | goto read; | 3904 | goto read; |
3810 | 3905 | ||
3811 | info->read = 0; | ||
3812 | |||
3813 | trace_access_lock(info->cpu); | 3906 | trace_access_lock(info->cpu); |
3814 | ret = ring_buffer_read_page(info->tr->buffer, | 3907 | ret = ring_buffer_read_page(info->tr->buffer, |
3815 | &info->spare, | 3908 | &info->spare, |
@@ -3819,6 +3912,8 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, | |||
3819 | if (ret < 0) | 3912 | if (ret < 0) |
3820 | return 0; | 3913 | return 0; |
3821 | 3914 | ||
3915 | info->read = 0; | ||
3916 | |||
3822 | read: | 3917 | read: |
3823 | size = PAGE_SIZE - info->read; | 3918 | size = PAGE_SIZE - info->read; |
3824 | if (size > count) | 3919 | if (size > count) |
@@ -4026,6 +4121,8 @@ tracing_stats_read(struct file *filp, char __user *ubuf, | |||
4026 | struct trace_array *tr = &global_trace; | 4121 | struct trace_array *tr = &global_trace; |
4027 | struct trace_seq *s; | 4122 | struct trace_seq *s; |
4028 | unsigned long cnt; | 4123 | unsigned long cnt; |
4124 | unsigned long long t; | ||
4125 | unsigned long usec_rem; | ||
4029 | 4126 | ||
4030 | s = kmalloc(sizeof(*s), GFP_KERNEL); | 4127 | s = kmalloc(sizeof(*s), GFP_KERNEL); |
4031 | if (!s) | 4128 | if (!s) |
@@ -4042,6 +4139,17 @@ tracing_stats_read(struct file *filp, char __user *ubuf, | |||
4042 | cnt = ring_buffer_commit_overrun_cpu(tr->buffer, cpu); | 4139 | cnt = ring_buffer_commit_overrun_cpu(tr->buffer, cpu); |
4043 | trace_seq_printf(s, "commit overrun: %ld\n", cnt); | 4140 | trace_seq_printf(s, "commit overrun: %ld\n", cnt); |
4044 | 4141 | ||
4142 | cnt = ring_buffer_bytes_cpu(tr->buffer, cpu); | ||
4143 | trace_seq_printf(s, "bytes: %ld\n", cnt); | ||
4144 | |||
4145 | t = ns2usecs(ring_buffer_oldest_event_ts(tr->buffer, cpu)); | ||
4146 | usec_rem = do_div(t, USEC_PER_SEC); | ||
4147 | trace_seq_printf(s, "oldest event ts: %5llu.%06lu\n", t, usec_rem); | ||
4148 | |||
4149 | t = ns2usecs(ring_buffer_time_stamp(tr->buffer, cpu)); | ||
4150 | usec_rem = do_div(t, USEC_PER_SEC); | ||
4151 | trace_seq_printf(s, "now ts: %5llu.%06lu\n", t, usec_rem); | ||
4152 | |||
4045 | count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->len); | 4153 | count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->len); |
4046 | 4154 | ||
4047 | kfree(s); | 4155 | kfree(s); |
@@ -4450,6 +4558,9 @@ static __init int tracer_init_debugfs(void) | |||
4450 | trace_create_file("buffer_size_kb", 0644, d_tracer, | 4558 | trace_create_file("buffer_size_kb", 0644, d_tracer, |
4451 | &global_trace, &tracing_entries_fops); | 4559 | &global_trace, &tracing_entries_fops); |
4452 | 4560 | ||
4561 | trace_create_file("buffer_total_size_kb", 0444, d_tracer, | ||
4562 | &global_trace, &tracing_total_entries_fops); | ||
4563 | |||
4453 | trace_create_file("free_buffer", 0644, d_tracer, | 4564 | trace_create_file("free_buffer", 0644, d_tracer, |
4454 | &global_trace, &tracing_free_buffer_fops); | 4565 | &global_trace, &tracing_free_buffer_fops); |
4455 | 4566 | ||
@@ -4566,6 +4677,12 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | |||
4566 | 4677 | ||
4567 | tracing_off(); | 4678 | tracing_off(); |
4568 | 4679 | ||
4680 | /* Did function tracer already get disabled? */ | ||
4681 | if (ftrace_is_dead()) { | ||
4682 | printk("# WARNING: FUNCTION TRACING IS CORRUPTED\n"); | ||
4683 | printk("# MAY BE MISSING FUNCTION EVENTS\n"); | ||
4684 | } | ||
4685 | |||
4569 | if (disable_tracing) | 4686 | if (disable_tracing) |
4570 | ftrace_kill(); | 4687 | ftrace_kill(); |
4571 | 4688 | ||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 616846bcfee5..092e1f8d18dc 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -579,11 +579,13 @@ static inline int ftrace_trace_task(struct task_struct *task) | |||
579 | 579 | ||
580 | return test_tsk_trace_trace(task); | 580 | return test_tsk_trace_trace(task); |
581 | } | 581 | } |
582 | extern int ftrace_is_dead(void); | ||
582 | #else | 583 | #else |
583 | static inline int ftrace_trace_task(struct task_struct *task) | 584 | static inline int ftrace_trace_task(struct task_struct *task) |
584 | { | 585 | { |
585 | return 1; | 586 | return 1; |
586 | } | 587 | } |
588 | static inline int ftrace_is_dead(void) { return 0; } | ||
587 | #endif | 589 | #endif |
588 | 590 | ||
589 | /* | 591 | /* |
@@ -761,16 +763,10 @@ struct filter_pred { | |||
761 | filter_pred_fn_t fn; | 763 | filter_pred_fn_t fn; |
762 | u64 val; | 764 | u64 val; |
763 | struct regex regex; | 765 | struct regex regex; |
764 | /* | 766 | unsigned short *ops; |
765 | * Leaf nodes use field_name, ops is used by AND and OR | 767 | #ifdef CONFIG_FTRACE_STARTUP_TEST |
766 | * nodes. The field_name is always freed when freeing a pred. | 768 | struct ftrace_event_field *field; |
767 | * We can overload field_name for ops and have it freed | 769 | #endif |
768 | * as well. | ||
769 | */ | ||
770 | union { | ||
771 | char *field_name; | ||
772 | unsigned short *ops; | ||
773 | }; | ||
774 | int offset; | 770 | int offset; |
775 | int not; | 771 | int not; |
776 | int op; | 772 | int op; |
diff --git a/kernel/trace/trace_clock.c b/kernel/trace/trace_clock.c index 6302747a1398..394783531cbb 100644 --- a/kernel/trace/trace_clock.c +++ b/kernel/trace/trace_clock.c | |||
@@ -113,3 +113,15 @@ u64 notrace trace_clock_global(void) | |||
113 | 113 | ||
114 | return now; | 114 | return now; |
115 | } | 115 | } |
116 | |||
117 | static atomic64_t trace_counter; | ||
118 | |||
119 | /* | ||
120 | * trace_clock_counter(): simply an atomic counter. | ||
121 | * Use the trace_counter "counter" for cases where you do not care | ||
122 | * about timings, but are interested in strict ordering. | ||
123 | */ | ||
124 | u64 notrace trace_clock_counter(void) | ||
125 | { | ||
126 | return atomic64_add_return(1, &trace_counter); | ||
127 | } | ||
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 256764ecccd6..816d3d074979 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -381,6 +381,63 @@ get_pred_parent(struct filter_pred *pred, struct filter_pred *preds, | |||
381 | return pred; | 381 | return pred; |
382 | } | 382 | } |
383 | 383 | ||
384 | enum walk_return { | ||
385 | WALK_PRED_ABORT, | ||
386 | WALK_PRED_PARENT, | ||
387 | WALK_PRED_DEFAULT, | ||
388 | }; | ||
389 | |||
390 | typedef int (*filter_pred_walkcb_t) (enum move_type move, | ||
391 | struct filter_pred *pred, | ||
392 | int *err, void *data); | ||
393 | |||
394 | static int walk_pred_tree(struct filter_pred *preds, | ||
395 | struct filter_pred *root, | ||
396 | filter_pred_walkcb_t cb, void *data) | ||
397 | { | ||
398 | struct filter_pred *pred = root; | ||
399 | enum move_type move = MOVE_DOWN; | ||
400 | int done = 0; | ||
401 | |||
402 | if (!preds) | ||
403 | return -EINVAL; | ||
404 | |||
405 | do { | ||
406 | int err = 0, ret; | ||
407 | |||
408 | ret = cb(move, pred, &err, data); | ||
409 | if (ret == WALK_PRED_ABORT) | ||
410 | return err; | ||
411 | if (ret == WALK_PRED_PARENT) | ||
412 | goto get_parent; | ||
413 | |||
414 | switch (move) { | ||
415 | case MOVE_DOWN: | ||
416 | if (pred->left != FILTER_PRED_INVALID) { | ||
417 | pred = &preds[pred->left]; | ||
418 | continue; | ||
419 | } | ||
420 | goto get_parent; | ||
421 | case MOVE_UP_FROM_LEFT: | ||
422 | pred = &preds[pred->right]; | ||
423 | move = MOVE_DOWN; | ||
424 | continue; | ||
425 | case MOVE_UP_FROM_RIGHT: | ||
426 | get_parent: | ||
427 | if (pred == root) | ||
428 | break; | ||
429 | pred = get_pred_parent(pred, preds, | ||
430 | pred->parent, | ||
431 | &move); | ||
432 | continue; | ||
433 | } | ||
434 | done = 1; | ||
435 | } while (!done); | ||
436 | |||
437 | /* We are fine. */ | ||
438 | return 0; | ||
439 | } | ||
440 | |||
384 | /* | 441 | /* |
385 | * A series of AND or ORs where found together. Instead of | 442 | * A series of AND or ORs where found together. Instead of |
386 | * climbing up and down the tree branches, an array of the | 443 | * climbing up and down the tree branches, an array of the |
@@ -410,99 +467,91 @@ static int process_ops(struct filter_pred *preds, | |||
410 | 467 | ||
411 | for (i = 0; i < op->val; i++) { | 468 | for (i = 0; i < op->val; i++) { |
412 | pred = &preds[op->ops[i]]; | 469 | pred = &preds[op->ops[i]]; |
413 | match = pred->fn(pred, rec); | 470 | if (!WARN_ON_ONCE(!pred->fn)) |
471 | match = pred->fn(pred, rec); | ||
414 | if (!!match == type) | 472 | if (!!match == type) |
415 | return match; | 473 | return match; |
416 | } | 474 | } |
417 | return match; | 475 | return match; |
418 | } | 476 | } |
419 | 477 | ||
478 | struct filter_match_preds_data { | ||
479 | struct filter_pred *preds; | ||
480 | int match; | ||
481 | void *rec; | ||
482 | }; | ||
483 | |||
484 | static int filter_match_preds_cb(enum move_type move, struct filter_pred *pred, | ||
485 | int *err, void *data) | ||
486 | { | ||
487 | struct filter_match_preds_data *d = data; | ||
488 | |||
489 | *err = 0; | ||
490 | switch (move) { | ||
491 | case MOVE_DOWN: | ||
492 | /* only AND and OR have children */ | ||
493 | if (pred->left != FILTER_PRED_INVALID) { | ||
494 | /* If ops is set, then it was folded. */ | ||
495 | if (!pred->ops) | ||
496 | return WALK_PRED_DEFAULT; | ||
497 | /* We can treat folded ops as a leaf node */ | ||
498 | d->match = process_ops(d->preds, pred, d->rec); | ||
499 | } else { | ||
500 | if (!WARN_ON_ONCE(!pred->fn)) | ||
501 | d->match = pred->fn(pred, d->rec); | ||
502 | } | ||
503 | |||
504 | return WALK_PRED_PARENT; | ||
505 | case MOVE_UP_FROM_LEFT: | ||
506 | /* | ||
507 | * Check for short circuits. | ||
508 | * | ||
509 | * Optimization: !!match == (pred->op == OP_OR) | ||
510 | * is the same as: | ||
511 | * if ((match && pred->op == OP_OR) || | ||
512 | * (!match && pred->op == OP_AND)) | ||
513 | */ | ||
514 | if (!!d->match == (pred->op == OP_OR)) | ||
515 | return WALK_PRED_PARENT; | ||
516 | break; | ||
517 | case MOVE_UP_FROM_RIGHT: | ||
518 | break; | ||
519 | } | ||
520 | |||
521 | return WALK_PRED_DEFAULT; | ||
522 | } | ||
523 | |||
420 | /* return 1 if event matches, 0 otherwise (discard) */ | 524 | /* return 1 if event matches, 0 otherwise (discard) */ |
421 | int filter_match_preds(struct event_filter *filter, void *rec) | 525 | int filter_match_preds(struct event_filter *filter, void *rec) |
422 | { | 526 | { |
423 | int match = -1; | ||
424 | enum move_type move = MOVE_DOWN; | ||
425 | struct filter_pred *preds; | 527 | struct filter_pred *preds; |
426 | struct filter_pred *pred; | ||
427 | struct filter_pred *root; | 528 | struct filter_pred *root; |
428 | int n_preds; | 529 | struct filter_match_preds_data data = { |
429 | int done = 0; | 530 | /* match is currently meaningless */ |
531 | .match = -1, | ||
532 | .rec = rec, | ||
533 | }; | ||
534 | int n_preds, ret; | ||
430 | 535 | ||
431 | /* no filter is considered a match */ | 536 | /* no filter is considered a match */ |
432 | if (!filter) | 537 | if (!filter) |
433 | return 1; | 538 | return 1; |
434 | 539 | ||
435 | n_preds = filter->n_preds; | 540 | n_preds = filter->n_preds; |
436 | |||
437 | if (!n_preds) | 541 | if (!n_preds) |
438 | return 1; | 542 | return 1; |
439 | 543 | ||
440 | /* | 544 | /* |
441 | * n_preds, root and filter->preds are protect with preemption disabled. | 545 | * n_preds, root and filter->preds are protect with preemption disabled. |
442 | */ | 546 | */ |
443 | preds = rcu_dereference_sched(filter->preds); | ||
444 | root = rcu_dereference_sched(filter->root); | 547 | root = rcu_dereference_sched(filter->root); |
445 | if (!root) | 548 | if (!root) |
446 | return 1; | 549 | return 1; |
447 | 550 | ||
448 | pred = root; | 551 | data.preds = preds = rcu_dereference_sched(filter->preds); |
449 | 552 | ret = walk_pred_tree(preds, root, filter_match_preds_cb, &data); | |
450 | /* match is currently meaningless */ | 553 | WARN_ON(ret); |
451 | match = -1; | 554 | return data.match; |
452 | |||
453 | do { | ||
454 | switch (move) { | ||
455 | case MOVE_DOWN: | ||
456 | /* only AND and OR have children */ | ||
457 | if (pred->left != FILTER_PRED_INVALID) { | ||
458 | /* If ops is set, then it was folded. */ | ||
459 | if (!pred->ops) { | ||
460 | /* keep going to down the left side */ | ||
461 | pred = &preds[pred->left]; | ||
462 | continue; | ||
463 | } | ||
464 | /* We can treat folded ops as a leaf node */ | ||
465 | match = process_ops(preds, pred, rec); | ||
466 | } else | ||
467 | match = pred->fn(pred, rec); | ||
468 | /* If this pred is the only pred */ | ||
469 | if (pred == root) | ||
470 | break; | ||
471 | pred = get_pred_parent(pred, preds, | ||
472 | pred->parent, &move); | ||
473 | continue; | ||
474 | case MOVE_UP_FROM_LEFT: | ||
475 | /* | ||
476 | * Check for short circuits. | ||
477 | * | ||
478 | * Optimization: !!match == (pred->op == OP_OR) | ||
479 | * is the same as: | ||
480 | * if ((match && pred->op == OP_OR) || | ||
481 | * (!match && pred->op == OP_AND)) | ||
482 | */ | ||
483 | if (!!match == (pred->op == OP_OR)) { | ||
484 | if (pred == root) | ||
485 | break; | ||
486 | pred = get_pred_parent(pred, preds, | ||
487 | pred->parent, &move); | ||
488 | continue; | ||
489 | } | ||
490 | /* now go down the right side of the tree. */ | ||
491 | pred = &preds[pred->right]; | ||
492 | move = MOVE_DOWN; | ||
493 | continue; | ||
494 | case MOVE_UP_FROM_RIGHT: | ||
495 | /* We finished this equation. */ | ||
496 | if (pred == root) | ||
497 | break; | ||
498 | pred = get_pred_parent(pred, preds, | ||
499 | pred->parent, &move); | ||
500 | continue; | ||
501 | } | ||
502 | done = 1; | ||
503 | } while (!done); | ||
504 | |||
505 | return match; | ||
506 | } | 555 | } |
507 | EXPORT_SYMBOL_GPL(filter_match_preds); | 556 | EXPORT_SYMBOL_GPL(filter_match_preds); |
508 | 557 | ||
@@ -628,22 +677,6 @@ find_event_field(struct ftrace_event_call *call, char *name) | |||
628 | return __find_event_field(head, name); | 677 | return __find_event_field(head, name); |
629 | } | 678 | } |
630 | 679 | ||
631 | static void filter_free_pred(struct filter_pred *pred) | ||
632 | { | ||
633 | if (!pred) | ||
634 | return; | ||
635 | |||
636 | kfree(pred->field_name); | ||
637 | kfree(pred); | ||
638 | } | ||
639 | |||
640 | static void filter_clear_pred(struct filter_pred *pred) | ||
641 | { | ||
642 | kfree(pred->field_name); | ||
643 | pred->field_name = NULL; | ||
644 | pred->regex.len = 0; | ||
645 | } | ||
646 | |||
647 | static int __alloc_pred_stack(struct pred_stack *stack, int n_preds) | 680 | static int __alloc_pred_stack(struct pred_stack *stack, int n_preds) |
648 | { | 681 | { |
649 | stack->preds = kzalloc(sizeof(*stack->preds)*(n_preds + 1), GFP_KERNEL); | 682 | stack->preds = kzalloc(sizeof(*stack->preds)*(n_preds + 1), GFP_KERNEL); |
@@ -689,20 +722,13 @@ __pop_pred_stack(struct pred_stack *stack) | |||
689 | static int filter_set_pred(struct event_filter *filter, | 722 | static int filter_set_pred(struct event_filter *filter, |
690 | int idx, | 723 | int idx, |
691 | struct pred_stack *stack, | 724 | struct pred_stack *stack, |
692 | struct filter_pred *src, | 725 | struct filter_pred *src) |
693 | filter_pred_fn_t fn) | ||
694 | { | 726 | { |
695 | struct filter_pred *dest = &filter->preds[idx]; | 727 | struct filter_pred *dest = &filter->preds[idx]; |
696 | struct filter_pred *left; | 728 | struct filter_pred *left; |
697 | struct filter_pred *right; | 729 | struct filter_pred *right; |
698 | 730 | ||
699 | *dest = *src; | 731 | *dest = *src; |
700 | if (src->field_name) { | ||
701 | dest->field_name = kstrdup(src->field_name, GFP_KERNEL); | ||
702 | if (!dest->field_name) | ||
703 | return -ENOMEM; | ||
704 | } | ||
705 | dest->fn = fn; | ||
706 | dest->index = idx; | 732 | dest->index = idx; |
707 | 733 | ||
708 | if (dest->op == OP_OR || dest->op == OP_AND) { | 734 | if (dest->op == OP_OR || dest->op == OP_AND) { |
@@ -743,11 +769,7 @@ static int filter_set_pred(struct event_filter *filter, | |||
743 | 769 | ||
744 | static void __free_preds(struct event_filter *filter) | 770 | static void __free_preds(struct event_filter *filter) |
745 | { | 771 | { |
746 | int i; | ||
747 | |||
748 | if (filter->preds) { | 772 | if (filter->preds) { |
749 | for (i = 0; i < filter->a_preds; i++) | ||
750 | kfree(filter->preds[i].field_name); | ||
751 | kfree(filter->preds); | 773 | kfree(filter->preds); |
752 | filter->preds = NULL; | 774 | filter->preds = NULL; |
753 | } | 775 | } |
@@ -840,23 +862,19 @@ static void filter_free_subsystem_filters(struct event_subsystem *system) | |||
840 | } | 862 | } |
841 | } | 863 | } |
842 | 864 | ||
843 | static int filter_add_pred_fn(struct filter_parse_state *ps, | 865 | static int filter_add_pred(struct filter_parse_state *ps, |
844 | struct ftrace_event_call *call, | 866 | struct event_filter *filter, |
845 | struct event_filter *filter, | 867 | struct filter_pred *pred, |
846 | struct filter_pred *pred, | 868 | struct pred_stack *stack) |
847 | struct pred_stack *stack, | ||
848 | filter_pred_fn_t fn) | ||
849 | { | 869 | { |
850 | int idx, err; | 870 | int err; |
851 | 871 | ||
852 | if (WARN_ON(filter->n_preds == filter->a_preds)) { | 872 | if (WARN_ON(filter->n_preds == filter->a_preds)) { |
853 | parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0); | 873 | parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0); |
854 | return -ENOSPC; | 874 | return -ENOSPC; |
855 | } | 875 | } |
856 | 876 | ||
857 | idx = filter->n_preds; | 877 | err = filter_set_pred(filter, filter->n_preds, stack, pred); |
858 | filter_clear_pred(&filter->preds[idx]); | ||
859 | err = filter_set_pred(filter, idx, stack, pred, fn); | ||
860 | if (err) | 878 | if (err) |
861 | return err; | 879 | return err; |
862 | 880 | ||
@@ -937,31 +955,15 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size, | |||
937 | return fn; | 955 | return fn; |
938 | } | 956 | } |
939 | 957 | ||
940 | static int filter_add_pred(struct filter_parse_state *ps, | 958 | static int init_pred(struct filter_parse_state *ps, |
941 | struct ftrace_event_call *call, | 959 | struct ftrace_event_field *field, |
942 | struct event_filter *filter, | 960 | struct filter_pred *pred) |
943 | struct filter_pred *pred, | 961 | |
944 | struct pred_stack *stack, | ||
945 | bool dry_run) | ||
946 | { | 962 | { |
947 | struct ftrace_event_field *field; | 963 | filter_pred_fn_t fn = filter_pred_none; |
948 | filter_pred_fn_t fn; | ||
949 | unsigned long long val; | 964 | unsigned long long val; |
950 | int ret; | 965 | int ret; |
951 | 966 | ||
952 | fn = pred->fn = filter_pred_none; | ||
953 | |||
954 | if (pred->op == OP_AND) | ||
955 | goto add_pred_fn; | ||
956 | else if (pred->op == OP_OR) | ||
957 | goto add_pred_fn; | ||
958 | |||
959 | field = find_event_field(call, pred->field_name); | ||
960 | if (!field) { | ||
961 | parse_error(ps, FILT_ERR_FIELD_NOT_FOUND, 0); | ||
962 | return -EINVAL; | ||
963 | } | ||
964 | |||
965 | pred->offset = field->offset; | 967 | pred->offset = field->offset; |
966 | 968 | ||
967 | if (!is_legal_op(field, pred->op)) { | 969 | if (!is_legal_op(field, pred->op)) { |
@@ -1001,9 +1003,7 @@ static int filter_add_pred(struct filter_parse_state *ps, | |||
1001 | if (pred->op == OP_NE) | 1003 | if (pred->op == OP_NE) |
1002 | pred->not = 1; | 1004 | pred->not = 1; |
1003 | 1005 | ||
1004 | add_pred_fn: | 1006 | pred->fn = fn; |
1005 | if (!dry_run) | ||
1006 | return filter_add_pred_fn(ps, call, filter, pred, stack, fn); | ||
1007 | return 0; | 1007 | return 0; |
1008 | } | 1008 | } |
1009 | 1009 | ||
@@ -1302,39 +1302,37 @@ parse_operand: | |||
1302 | return 0; | 1302 | return 0; |
1303 | } | 1303 | } |
1304 | 1304 | ||
1305 | static struct filter_pred *create_pred(int op, char *operand1, char *operand2) | 1305 | static struct filter_pred *create_pred(struct filter_parse_state *ps, |
1306 | struct ftrace_event_call *call, | ||
1307 | int op, char *operand1, char *operand2) | ||
1306 | { | 1308 | { |
1307 | struct filter_pred *pred; | 1309 | struct ftrace_event_field *field; |
1310 | static struct filter_pred pred; | ||
1308 | 1311 | ||
1309 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | 1312 | memset(&pred, 0, sizeof(pred)); |
1310 | if (!pred) | 1313 | pred.op = op; |
1311 | return NULL; | ||
1312 | 1314 | ||
1313 | pred->field_name = kstrdup(operand1, GFP_KERNEL); | 1315 | if (op == OP_AND || op == OP_OR) |
1314 | if (!pred->field_name) { | 1316 | return &pred; |
1315 | kfree(pred); | 1317 | |
1318 | if (!operand1 || !operand2) { | ||
1319 | parse_error(ps, FILT_ERR_MISSING_FIELD, 0); | ||
1316 | return NULL; | 1320 | return NULL; |
1317 | } | 1321 | } |
1318 | 1322 | ||
1319 | strcpy(pred->regex.pattern, operand2); | 1323 | field = find_event_field(call, operand1); |
1320 | pred->regex.len = strlen(pred->regex.pattern); | 1324 | if (!field) { |
1321 | 1325 | parse_error(ps, FILT_ERR_FIELD_NOT_FOUND, 0); | |
1322 | pred->op = op; | ||
1323 | |||
1324 | return pred; | ||
1325 | } | ||
1326 | |||
1327 | static struct filter_pred *create_logical_pred(int op) | ||
1328 | { | ||
1329 | struct filter_pred *pred; | ||
1330 | |||
1331 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | ||
1332 | if (!pred) | ||
1333 | return NULL; | 1326 | return NULL; |
1327 | } | ||
1334 | 1328 | ||
1335 | pred->op = op; | 1329 | strcpy(pred.regex.pattern, operand2); |
1330 | pred.regex.len = strlen(pred.regex.pattern); | ||
1336 | 1331 | ||
1337 | return pred; | 1332 | #ifdef CONFIG_FTRACE_STARTUP_TEST |
1333 | pred.field = field; | ||
1334 | #endif | ||
1335 | return init_pred(ps, field, &pred) ? NULL : &pred; | ||
1338 | } | 1336 | } |
1339 | 1337 | ||
1340 | static int check_preds(struct filter_parse_state *ps) | 1338 | static int check_preds(struct filter_parse_state *ps) |
@@ -1375,6 +1373,23 @@ static int count_preds(struct filter_parse_state *ps) | |||
1375 | return n_preds; | 1373 | return n_preds; |
1376 | } | 1374 | } |
1377 | 1375 | ||
1376 | struct check_pred_data { | ||
1377 | int count; | ||
1378 | int max; | ||
1379 | }; | ||
1380 | |||
1381 | static int check_pred_tree_cb(enum move_type move, struct filter_pred *pred, | ||
1382 | int *err, void *data) | ||
1383 | { | ||
1384 | struct check_pred_data *d = data; | ||
1385 | |||
1386 | if (WARN_ON(d->count++ > d->max)) { | ||
1387 | *err = -EINVAL; | ||
1388 | return WALK_PRED_ABORT; | ||
1389 | } | ||
1390 | return WALK_PRED_DEFAULT; | ||
1391 | } | ||
1392 | |||
1378 | /* | 1393 | /* |
1379 | * The tree is walked at filtering of an event. If the tree is not correctly | 1394 | * The tree is walked at filtering of an event. If the tree is not correctly |
1380 | * built, it may cause an infinite loop. Check here that the tree does | 1395 | * built, it may cause an infinite loop. Check here that the tree does |
@@ -1383,107 +1398,76 @@ static int count_preds(struct filter_parse_state *ps) | |||
1383 | static int check_pred_tree(struct event_filter *filter, | 1398 | static int check_pred_tree(struct event_filter *filter, |
1384 | struct filter_pred *root) | 1399 | struct filter_pred *root) |
1385 | { | 1400 | { |
1386 | struct filter_pred *preds; | 1401 | struct check_pred_data data = { |
1387 | struct filter_pred *pred; | 1402 | /* |
1388 | enum move_type move = MOVE_DOWN; | 1403 | * The max that we can hit a node is three times. |
1389 | int count = 0; | 1404 | * Once going down, once coming up from left, and |
1390 | int done = 0; | 1405 | * once coming up from right. This is more than enough |
1391 | int max; | 1406 | * since leafs are only hit a single time. |
1392 | 1407 | */ | |
1393 | /* | 1408 | .max = 3 * filter->n_preds, |
1394 | * The max that we can hit a node is three times. | 1409 | .count = 0, |
1395 | * Once going down, once coming up from left, and | 1410 | }; |
1396 | * once coming up from right. This is more than enough | ||
1397 | * since leafs are only hit a single time. | ||
1398 | */ | ||
1399 | max = 3 * filter->n_preds; | ||
1400 | 1411 | ||
1401 | preds = filter->preds; | 1412 | return walk_pred_tree(filter->preds, root, |
1402 | if (!preds) | 1413 | check_pred_tree_cb, &data); |
1403 | return -EINVAL; | 1414 | } |
1404 | pred = root; | ||
1405 | 1415 | ||
1406 | do { | 1416 | static int count_leafs_cb(enum move_type move, struct filter_pred *pred, |
1407 | if (WARN_ON(count++ > max)) | 1417 | int *err, void *data) |
1408 | return -EINVAL; | 1418 | { |
1419 | int *count = data; | ||
1409 | 1420 | ||
1410 | switch (move) { | 1421 | if ((move == MOVE_DOWN) && |
1411 | case MOVE_DOWN: | 1422 | (pred->left == FILTER_PRED_INVALID)) |
1412 | if (pred->left != FILTER_PRED_INVALID) { | 1423 | (*count)++; |
1413 | pred = &preds[pred->left]; | ||
1414 | continue; | ||
1415 | } | ||
1416 | /* A leaf at the root is just a leaf in the tree */ | ||
1417 | if (pred == root) | ||
1418 | break; | ||
1419 | pred = get_pred_parent(pred, preds, | ||
1420 | pred->parent, &move); | ||
1421 | continue; | ||
1422 | case MOVE_UP_FROM_LEFT: | ||
1423 | pred = &preds[pred->right]; | ||
1424 | move = MOVE_DOWN; | ||
1425 | continue; | ||
1426 | case MOVE_UP_FROM_RIGHT: | ||
1427 | if (pred == root) | ||
1428 | break; | ||
1429 | pred = get_pred_parent(pred, preds, | ||
1430 | pred->parent, &move); | ||
1431 | continue; | ||
1432 | } | ||
1433 | done = 1; | ||
1434 | } while (!done); | ||
1435 | 1424 | ||
1436 | /* We are fine. */ | 1425 | return WALK_PRED_DEFAULT; |
1437 | return 0; | ||
1438 | } | 1426 | } |
1439 | 1427 | ||
1440 | static int count_leafs(struct filter_pred *preds, struct filter_pred *root) | 1428 | static int count_leafs(struct filter_pred *preds, struct filter_pred *root) |
1441 | { | 1429 | { |
1442 | struct filter_pred *pred; | 1430 | int count = 0, ret; |
1443 | enum move_type move = MOVE_DOWN; | ||
1444 | int count = 0; | ||
1445 | int done = 0; | ||
1446 | 1431 | ||
1447 | pred = root; | 1432 | ret = walk_pred_tree(preds, root, count_leafs_cb, &count); |
1433 | WARN_ON(ret); | ||
1434 | return count; | ||
1435 | } | ||
1448 | 1436 | ||
1449 | do { | 1437 | struct fold_pred_data { |
1450 | switch (move) { | 1438 | struct filter_pred *root; |
1451 | case MOVE_DOWN: | 1439 | int count; |
1452 | if (pred->left != FILTER_PRED_INVALID) { | 1440 | int children; |
1453 | pred = &preds[pred->left]; | 1441 | }; |
1454 | continue; | ||
1455 | } | ||
1456 | /* A leaf at the root is just a leaf in the tree */ | ||
1457 | if (pred == root) | ||
1458 | return 1; | ||
1459 | count++; | ||
1460 | pred = get_pred_parent(pred, preds, | ||
1461 | pred->parent, &move); | ||
1462 | continue; | ||
1463 | case MOVE_UP_FROM_LEFT: | ||
1464 | pred = &preds[pred->right]; | ||
1465 | move = MOVE_DOWN; | ||
1466 | continue; | ||
1467 | case MOVE_UP_FROM_RIGHT: | ||
1468 | if (pred == root) | ||
1469 | break; | ||
1470 | pred = get_pred_parent(pred, preds, | ||
1471 | pred->parent, &move); | ||
1472 | continue; | ||
1473 | } | ||
1474 | done = 1; | ||
1475 | } while (!done); | ||
1476 | 1442 | ||
1477 | return count; | 1443 | static int fold_pred_cb(enum move_type move, struct filter_pred *pred, |
1444 | int *err, void *data) | ||
1445 | { | ||
1446 | struct fold_pred_data *d = data; | ||
1447 | struct filter_pred *root = d->root; | ||
1448 | |||
1449 | if (move != MOVE_DOWN) | ||
1450 | return WALK_PRED_DEFAULT; | ||
1451 | if (pred->left != FILTER_PRED_INVALID) | ||
1452 | return WALK_PRED_DEFAULT; | ||
1453 | |||
1454 | if (WARN_ON(d->count == d->children)) { | ||
1455 | *err = -EINVAL; | ||
1456 | return WALK_PRED_ABORT; | ||
1457 | } | ||
1458 | |||
1459 | pred->index &= ~FILTER_PRED_FOLD; | ||
1460 | root->ops[d->count++] = pred->index; | ||
1461 | return WALK_PRED_DEFAULT; | ||
1478 | } | 1462 | } |
1479 | 1463 | ||
1480 | static int fold_pred(struct filter_pred *preds, struct filter_pred *root) | 1464 | static int fold_pred(struct filter_pred *preds, struct filter_pred *root) |
1481 | { | 1465 | { |
1482 | struct filter_pred *pred; | 1466 | struct fold_pred_data data = { |
1483 | enum move_type move = MOVE_DOWN; | 1467 | .root = root, |
1484 | int count = 0; | 1468 | .count = 0, |
1469 | }; | ||
1485 | int children; | 1470 | int children; |
1486 | int done = 0; | ||
1487 | 1471 | ||
1488 | /* No need to keep the fold flag */ | 1472 | /* No need to keep the fold flag */ |
1489 | root->index &= ~FILTER_PRED_FOLD; | 1473 | root->index &= ~FILTER_PRED_FOLD; |
@@ -1501,37 +1485,26 @@ static int fold_pred(struct filter_pred *preds, struct filter_pred *root) | |||
1501 | return -ENOMEM; | 1485 | return -ENOMEM; |
1502 | 1486 | ||
1503 | root->val = children; | 1487 | root->val = children; |
1488 | data.children = children; | ||
1489 | return walk_pred_tree(preds, root, fold_pred_cb, &data); | ||
1490 | } | ||
1504 | 1491 | ||
1505 | pred = root; | 1492 | static int fold_pred_tree_cb(enum move_type move, struct filter_pred *pred, |
1506 | do { | 1493 | int *err, void *data) |
1507 | switch (move) { | 1494 | { |
1508 | case MOVE_DOWN: | 1495 | struct filter_pred *preds = data; |
1509 | if (pred->left != FILTER_PRED_INVALID) { | ||
1510 | pred = &preds[pred->left]; | ||
1511 | continue; | ||
1512 | } | ||
1513 | if (WARN_ON(count == children)) | ||
1514 | return -EINVAL; | ||
1515 | pred->index &= ~FILTER_PRED_FOLD; | ||
1516 | root->ops[count++] = pred->index; | ||
1517 | pred = get_pred_parent(pred, preds, | ||
1518 | pred->parent, &move); | ||
1519 | continue; | ||
1520 | case MOVE_UP_FROM_LEFT: | ||
1521 | pred = &preds[pred->right]; | ||
1522 | move = MOVE_DOWN; | ||
1523 | continue; | ||
1524 | case MOVE_UP_FROM_RIGHT: | ||
1525 | if (pred == root) | ||
1526 | break; | ||
1527 | pred = get_pred_parent(pred, preds, | ||
1528 | pred->parent, &move); | ||
1529 | continue; | ||
1530 | } | ||
1531 | done = 1; | ||
1532 | } while (!done); | ||
1533 | 1496 | ||
1534 | return 0; | 1497 | if (move != MOVE_DOWN) |
1498 | return WALK_PRED_DEFAULT; | ||
1499 | if (!(pred->index & FILTER_PRED_FOLD)) | ||
1500 | return WALK_PRED_DEFAULT; | ||
1501 | |||
1502 | *err = fold_pred(preds, pred); | ||
1503 | if (*err) | ||
1504 | return WALK_PRED_ABORT; | ||
1505 | |||
1506 | /* eveyrhing below is folded, continue with parent */ | ||
1507 | return WALK_PRED_PARENT; | ||
1535 | } | 1508 | } |
1536 | 1509 | ||
1537 | /* | 1510 | /* |
@@ -1542,51 +1515,8 @@ static int fold_pred(struct filter_pred *preds, struct filter_pred *root) | |||
1542 | static int fold_pred_tree(struct event_filter *filter, | 1515 | static int fold_pred_tree(struct event_filter *filter, |
1543 | struct filter_pred *root) | 1516 | struct filter_pred *root) |
1544 | { | 1517 | { |
1545 | struct filter_pred *preds; | 1518 | return walk_pred_tree(filter->preds, root, fold_pred_tree_cb, |
1546 | struct filter_pred *pred; | 1519 | filter->preds); |
1547 | enum move_type move = MOVE_DOWN; | ||
1548 | int done = 0; | ||
1549 | int err; | ||
1550 | |||
1551 | preds = filter->preds; | ||
1552 | if (!preds) | ||
1553 | return -EINVAL; | ||
1554 | pred = root; | ||
1555 | |||
1556 | do { | ||
1557 | switch (move) { | ||
1558 | case MOVE_DOWN: | ||
1559 | if (pred->index & FILTER_PRED_FOLD) { | ||
1560 | err = fold_pred(preds, pred); | ||
1561 | if (err) | ||
1562 | return err; | ||
1563 | /* Folded nodes are like leafs */ | ||
1564 | } else if (pred->left != FILTER_PRED_INVALID) { | ||
1565 | pred = &preds[pred->left]; | ||
1566 | continue; | ||
1567 | } | ||
1568 | |||
1569 | /* A leaf at the root is just a leaf in the tree */ | ||
1570 | if (pred == root) | ||
1571 | break; | ||
1572 | pred = get_pred_parent(pred, preds, | ||
1573 | pred->parent, &move); | ||
1574 | continue; | ||
1575 | case MOVE_UP_FROM_LEFT: | ||
1576 | pred = &preds[pred->right]; | ||
1577 | move = MOVE_DOWN; | ||
1578 | continue; | ||
1579 | case MOVE_UP_FROM_RIGHT: | ||
1580 | if (pred == root) | ||
1581 | break; | ||
1582 | pred = get_pred_parent(pred, preds, | ||
1583 | pred->parent, &move); | ||
1584 | continue; | ||
1585 | } | ||
1586 | done = 1; | ||
1587 | } while (!done); | ||
1588 | |||
1589 | return 0; | ||
1590 | } | 1520 | } |
1591 | 1521 | ||
1592 | static int replace_preds(struct ftrace_event_call *call, | 1522 | static int replace_preds(struct ftrace_event_call *call, |
@@ -1643,27 +1573,17 @@ static int replace_preds(struct ftrace_event_call *call, | |||
1643 | goto fail; | 1573 | goto fail; |
1644 | } | 1574 | } |
1645 | 1575 | ||
1646 | if (elt->op == OP_AND || elt->op == OP_OR) { | 1576 | pred = create_pred(ps, call, elt->op, operand1, operand2); |
1647 | pred = create_logical_pred(elt->op); | 1577 | if (!pred) { |
1648 | goto add_pred; | ||
1649 | } | ||
1650 | |||
1651 | if (!operand1 || !operand2) { | ||
1652 | parse_error(ps, FILT_ERR_MISSING_FIELD, 0); | ||
1653 | err = -EINVAL; | 1578 | err = -EINVAL; |
1654 | goto fail; | 1579 | goto fail; |
1655 | } | 1580 | } |
1656 | 1581 | ||
1657 | pred = create_pred(elt->op, operand1, operand2); | 1582 | if (!dry_run) { |
1658 | add_pred: | 1583 | err = filter_add_pred(ps, filter, pred, &stack); |
1659 | if (!pred) { | 1584 | if (err) |
1660 | err = -ENOMEM; | 1585 | goto fail; |
1661 | goto fail; | ||
1662 | } | 1586 | } |
1663 | err = filter_add_pred(ps, call, filter, pred, &stack, dry_run); | ||
1664 | filter_free_pred(pred); | ||
1665 | if (err) | ||
1666 | goto fail; | ||
1667 | 1587 | ||
1668 | operand1 = operand2 = NULL; | 1588 | operand1 = operand2 = NULL; |
1669 | } | 1589 | } |
@@ -1958,17 +1878,14 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, | |||
1958 | int err; | 1878 | int err; |
1959 | struct event_filter *filter; | 1879 | struct event_filter *filter; |
1960 | struct filter_parse_state *ps; | 1880 | struct filter_parse_state *ps; |
1961 | struct ftrace_event_call *call = NULL; | 1881 | struct ftrace_event_call *call; |
1962 | 1882 | ||
1963 | mutex_lock(&event_mutex); | 1883 | mutex_lock(&event_mutex); |
1964 | 1884 | ||
1965 | list_for_each_entry(call, &ftrace_events, list) { | 1885 | call = event->tp_event; |
1966 | if (call->event.type == event_id) | ||
1967 | break; | ||
1968 | } | ||
1969 | 1886 | ||
1970 | err = -EINVAL; | 1887 | err = -EINVAL; |
1971 | if (&call->list == &ftrace_events) | 1888 | if (!call) |
1972 | goto out_unlock; | 1889 | goto out_unlock; |
1973 | 1890 | ||
1974 | err = -EEXIST; | 1891 | err = -EEXIST; |
@@ -2012,3 +1929,215 @@ out_unlock: | |||
2012 | 1929 | ||
2013 | #endif /* CONFIG_PERF_EVENTS */ | 1930 | #endif /* CONFIG_PERF_EVENTS */ |
2014 | 1931 | ||
1932 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
1933 | |||
1934 | #include <linux/types.h> | ||
1935 | #include <linux/tracepoint.h> | ||
1936 | |||
1937 | #define CREATE_TRACE_POINTS | ||
1938 | #include "trace_events_filter_test.h" | ||
1939 | |||
1940 | static int test_get_filter(char *filter_str, struct ftrace_event_call *call, | ||
1941 | struct event_filter **pfilter) | ||
1942 | { | ||
1943 | struct event_filter *filter; | ||
1944 | struct filter_parse_state *ps; | ||
1945 | int err = -ENOMEM; | ||
1946 | |||
1947 | filter = __alloc_filter(); | ||
1948 | if (!filter) | ||
1949 | goto out; | ||
1950 | |||
1951 | ps = kzalloc(sizeof(*ps), GFP_KERNEL); | ||
1952 | if (!ps) | ||
1953 | goto free_filter; | ||
1954 | |||
1955 | parse_init(ps, filter_ops, filter_str); | ||
1956 | err = filter_parse(ps); | ||
1957 | if (err) | ||
1958 | goto free_ps; | ||
1959 | |||
1960 | err = replace_preds(call, filter, ps, filter_str, false); | ||
1961 | if (!err) | ||
1962 | *pfilter = filter; | ||
1963 | |||
1964 | free_ps: | ||
1965 | filter_opstack_clear(ps); | ||
1966 | postfix_clear(ps); | ||
1967 | kfree(ps); | ||
1968 | |||
1969 | free_filter: | ||
1970 | if (err) | ||
1971 | __free_filter(filter); | ||
1972 | |||
1973 | out: | ||
1974 | return err; | ||
1975 | } | ||
1976 | |||
1977 | #define DATA_REC(m, va, vb, vc, vd, ve, vf, vg, vh, nvisit) \ | ||
1978 | { \ | ||
1979 | .filter = FILTER, \ | ||
1980 | .rec = { .a = va, .b = vb, .c = vc, .d = vd, \ | ||
1981 | .e = ve, .f = vf, .g = vg, .h = vh }, \ | ||
1982 | .match = m, \ | ||
1983 | .not_visited = nvisit, \ | ||
1984 | } | ||
1985 | #define YES 1 | ||
1986 | #define NO 0 | ||
1987 | |||
1988 | static struct test_filter_data_t { | ||
1989 | char *filter; | ||
1990 | struct ftrace_raw_ftrace_test_filter rec; | ||
1991 | int match; | ||
1992 | char *not_visited; | ||
1993 | } test_filter_data[] = { | ||
1994 | #define FILTER "a == 1 && b == 1 && c == 1 && d == 1 && " \ | ||
1995 | "e == 1 && f == 1 && g == 1 && h == 1" | ||
1996 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, ""), | ||
1997 | DATA_REC(NO, 0, 1, 1, 1, 1, 1, 1, 1, "bcdefgh"), | ||
1998 | DATA_REC(NO, 1, 1, 1, 1, 1, 1, 1, 0, ""), | ||
1999 | #undef FILTER | ||
2000 | #define FILTER "a == 1 || b == 1 || c == 1 || d == 1 || " \ | ||
2001 | "e == 1 || f == 1 || g == 1 || h == 1" | ||
2002 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""), | ||
2003 | DATA_REC(YES, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2004 | DATA_REC(YES, 1, 0, 0, 0, 0, 0, 0, 0, "bcdefgh"), | ||
2005 | #undef FILTER | ||
2006 | #define FILTER "(a == 1 || b == 1) && (c == 1 || d == 1) && " \ | ||
2007 | "(e == 1 || f == 1) && (g == 1 || h == 1)" | ||
2008 | DATA_REC(NO, 0, 0, 1, 1, 1, 1, 1, 1, "dfh"), | ||
2009 | DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2010 | DATA_REC(YES, 1, 0, 1, 0, 0, 1, 0, 1, "bd"), | ||
2011 | DATA_REC(NO, 1, 0, 1, 0, 0, 1, 0, 0, "bd"), | ||
2012 | #undef FILTER | ||
2013 | #define FILTER "(a == 1 && b == 1) || (c == 1 && d == 1) || " \ | ||
2014 | "(e == 1 && f == 1) || (g == 1 && h == 1)" | ||
2015 | DATA_REC(YES, 1, 0, 1, 1, 1, 1, 1, 1, "efgh"), | ||
2016 | DATA_REC(YES, 0, 0, 0, 0, 0, 0, 1, 1, ""), | ||
2017 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2018 | #undef FILTER | ||
2019 | #define FILTER "(a == 1 && b == 1) && (c == 1 && d == 1) && " \ | ||
2020 | "(e == 1 && f == 1) || (g == 1 && h == 1)" | ||
2021 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 0, "gh"), | ||
2022 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2023 | DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, ""), | ||
2024 | #undef FILTER | ||
2025 | #define FILTER "((a == 1 || b == 1) || (c == 1 || d == 1) || " \ | ||
2026 | "(e == 1 || f == 1)) && (g == 1 || h == 1)" | ||
2027 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 1, "bcdef"), | ||
2028 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""), | ||
2029 | DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, "h"), | ||
2030 | #undef FILTER | ||
2031 | #define FILTER "((((((((a == 1) && (b == 1)) || (c == 1)) && (d == 1)) || " \ | ||
2032 | "(e == 1)) && (f == 1)) || (g == 1)) && (h == 1))" | ||
2033 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "ceg"), | ||
2034 | DATA_REC(NO, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2035 | DATA_REC(NO, 1, 0, 1, 0, 1, 0, 1, 0, ""), | ||
2036 | #undef FILTER | ||
2037 | #define FILTER "((((((((a == 1) || (b == 1)) && (c == 1)) || (d == 1)) && " \ | ||
2038 | "(e == 1)) || (f == 1)) && (g == 1)) || (h == 1))" | ||
2039 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "bdfh"), | ||
2040 | DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2041 | DATA_REC(YES, 1, 0, 1, 0, 1, 0, 1, 0, "bdfh"), | ||
2042 | }; | ||
2043 | |||
2044 | #undef DATA_REC | ||
2045 | #undef FILTER | ||
2046 | #undef YES | ||
2047 | #undef NO | ||
2048 | |||
2049 | #define DATA_CNT (sizeof(test_filter_data)/sizeof(struct test_filter_data_t)) | ||
2050 | |||
2051 | static int test_pred_visited; | ||
2052 | |||
2053 | static int test_pred_visited_fn(struct filter_pred *pred, void *event) | ||
2054 | { | ||
2055 | struct ftrace_event_field *field = pred->field; | ||
2056 | |||
2057 | test_pred_visited = 1; | ||
2058 | printk(KERN_INFO "\npred visited %s\n", field->name); | ||
2059 | return 1; | ||
2060 | } | ||
2061 | |||
2062 | static int test_walk_pred_cb(enum move_type move, struct filter_pred *pred, | ||
2063 | int *err, void *data) | ||
2064 | { | ||
2065 | char *fields = data; | ||
2066 | |||
2067 | if ((move == MOVE_DOWN) && | ||
2068 | (pred->left == FILTER_PRED_INVALID)) { | ||
2069 | struct ftrace_event_field *field = pred->field; | ||
2070 | |||
2071 | if (!field) { | ||
2072 | WARN(1, "all leafs should have field defined"); | ||
2073 | return WALK_PRED_DEFAULT; | ||
2074 | } | ||
2075 | if (!strchr(fields, *field->name)) | ||
2076 | return WALK_PRED_DEFAULT; | ||
2077 | |||
2078 | WARN_ON(!pred->fn); | ||
2079 | pred->fn = test_pred_visited_fn; | ||
2080 | } | ||
2081 | return WALK_PRED_DEFAULT; | ||
2082 | } | ||
2083 | |||
2084 | static __init int ftrace_test_event_filter(void) | ||
2085 | { | ||
2086 | int i; | ||
2087 | |||
2088 | printk(KERN_INFO "Testing ftrace filter: "); | ||
2089 | |||
2090 | for (i = 0; i < DATA_CNT; i++) { | ||
2091 | struct event_filter *filter = NULL; | ||
2092 | struct test_filter_data_t *d = &test_filter_data[i]; | ||
2093 | int err; | ||
2094 | |||
2095 | err = test_get_filter(d->filter, &event_ftrace_test_filter, | ||
2096 | &filter); | ||
2097 | if (err) { | ||
2098 | printk(KERN_INFO | ||
2099 | "Failed to get filter for '%s', err %d\n", | ||
2100 | d->filter, err); | ||
2101 | break; | ||
2102 | } | ||
2103 | |||
2104 | /* | ||
2105 | * The preemption disabling is not really needed for self | ||
2106 | * tests, but the rcu dereference will complain without it. | ||
2107 | */ | ||
2108 | preempt_disable(); | ||
2109 | if (*d->not_visited) | ||
2110 | walk_pred_tree(filter->preds, filter->root, | ||
2111 | test_walk_pred_cb, | ||
2112 | d->not_visited); | ||
2113 | |||
2114 | test_pred_visited = 0; | ||
2115 | err = filter_match_preds(filter, &d->rec); | ||
2116 | preempt_enable(); | ||
2117 | |||
2118 | __free_filter(filter); | ||
2119 | |||
2120 | if (test_pred_visited) { | ||
2121 | printk(KERN_INFO | ||
2122 | "Failed, unwanted pred visited for filter %s\n", | ||
2123 | d->filter); | ||
2124 | break; | ||
2125 | } | ||
2126 | |||
2127 | if (err != d->match) { | ||
2128 | printk(KERN_INFO | ||
2129 | "Failed to match filter '%s', expected %d\n", | ||
2130 | d->filter, d->match); | ||
2131 | break; | ||
2132 | } | ||
2133 | } | ||
2134 | |||
2135 | if (i == DATA_CNT) | ||
2136 | printk(KERN_CONT "OK\n"); | ||
2137 | |||
2138 | return 0; | ||
2139 | } | ||
2140 | |||
2141 | late_initcall(ftrace_test_event_filter); | ||
2142 | |||
2143 | #endif /* CONFIG_FTRACE_STARTUP_TEST */ | ||
diff --git a/kernel/trace/trace_events_filter_test.h b/kernel/trace/trace_events_filter_test.h new file mode 100644 index 000000000000..bfd4dba0d603 --- /dev/null +++ b/kernel/trace/trace_events_filter_test.h | |||
@@ -0,0 +1,50 @@ | |||
1 | #undef TRACE_SYSTEM | ||
2 | #define TRACE_SYSTEM test | ||
3 | |||
4 | #if !defined(_TRACE_TEST_H) || defined(TRACE_HEADER_MULTI_READ) | ||
5 | #define _TRACE_TEST_H | ||
6 | |||
7 | #include <linux/tracepoint.h> | ||
8 | |||
9 | TRACE_EVENT(ftrace_test_filter, | ||
10 | |||
11 | TP_PROTO(int a, int b, int c, int d, int e, int f, int g, int h), | ||
12 | |||
13 | TP_ARGS(a, b, c, d, e, f, g, h), | ||
14 | |||
15 | TP_STRUCT__entry( | ||
16 | __field(int, a) | ||
17 | __field(int, b) | ||
18 | __field(int, c) | ||
19 | __field(int, d) | ||
20 | __field(int, e) | ||
21 | __field(int, f) | ||
22 | __field(int, g) | ||
23 | __field(int, h) | ||
24 | ), | ||
25 | |||
26 | TP_fast_assign( | ||
27 | __entry->a = a; | ||
28 | __entry->b = b; | ||
29 | __entry->c = c; | ||
30 | __entry->d = d; | ||
31 | __entry->e = e; | ||
32 | __entry->f = f; | ||
33 | __entry->g = g; | ||
34 | __entry->h = h; | ||
35 | ), | ||
36 | |||
37 | TP_printk("a %d, b %d, c %d, d %d, e %d, f %d, g %d, h %d", | ||
38 | __entry->a, __entry->b, __entry->c, __entry->d, | ||
39 | __entry->e, __entry->f, __entry->g, __entry->h) | ||
40 | ); | ||
41 | |||
42 | #endif /* _TRACE_TEST_H || TRACE_HEADER_MULTI_READ */ | ||
43 | |||
44 | #undef TRACE_INCLUDE_PATH | ||
45 | #undef TRACE_INCLUDE_FILE | ||
46 | #define TRACE_INCLUDE_PATH . | ||
47 | #define TRACE_INCLUDE_FILE trace_events_filter_test | ||
48 | |||
49 | /* This part must be outside protection */ | ||
50 | #include <trace/define_trace.h> | ||
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 11186212068c..20dad0d7a163 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c | |||
@@ -505,13 +505,13 @@ EXPORT_SYMBOL(trace_hardirqs_off_caller); | |||
505 | #ifdef CONFIG_PREEMPT_TRACER | 505 | #ifdef CONFIG_PREEMPT_TRACER |
506 | void trace_preempt_on(unsigned long a0, unsigned long a1) | 506 | void trace_preempt_on(unsigned long a0, unsigned long a1) |
507 | { | 507 | { |
508 | if (preempt_trace()) | 508 | if (preempt_trace() && !irq_trace()) |
509 | stop_critical_timing(a0, a1); | 509 | stop_critical_timing(a0, a1); |
510 | } | 510 | } |
511 | 511 | ||
512 | void trace_preempt_off(unsigned long a0, unsigned long a1) | 512 | void trace_preempt_off(unsigned long a0, unsigned long a1) |
513 | { | 513 | { |
514 | if (preempt_trace()) | 514 | if (preempt_trace() && !irq_trace()) |
515 | start_critical_timing(a0, a1); | 515 | start_critical_timing(a0, a1); |
516 | } | 516 | } |
517 | #endif /* CONFIG_PREEMPT_TRACER */ | 517 | #endif /* CONFIG_PREEMPT_TRACER */ |
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 5fb3697bf0e5..00d527c945a4 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
@@ -836,11 +836,17 @@ static void __unregister_trace_probe(struct trace_probe *tp) | |||
836 | } | 836 | } |
837 | 837 | ||
838 | /* Unregister a trace_probe and probe_event: call with locking probe_lock */ | 838 | /* Unregister a trace_probe and probe_event: call with locking probe_lock */ |
839 | static void unregister_trace_probe(struct trace_probe *tp) | 839 | static int unregister_trace_probe(struct trace_probe *tp) |
840 | { | 840 | { |
841 | /* Enabled event can not be unregistered */ | ||
842 | if (trace_probe_is_enabled(tp)) | ||
843 | return -EBUSY; | ||
844 | |||
841 | __unregister_trace_probe(tp); | 845 | __unregister_trace_probe(tp); |
842 | list_del(&tp->list); | 846 | list_del(&tp->list); |
843 | unregister_probe_event(tp); | 847 | unregister_probe_event(tp); |
848 | |||
849 | return 0; | ||
844 | } | 850 | } |
845 | 851 | ||
846 | /* Register a trace_probe and probe_event */ | 852 | /* Register a trace_probe and probe_event */ |
@@ -854,7 +860,9 @@ static int register_trace_probe(struct trace_probe *tp) | |||
854 | /* Delete old (same name) event if exist */ | 860 | /* Delete old (same name) event if exist */ |
855 | old_tp = find_trace_probe(tp->call.name, tp->call.class->system); | 861 | old_tp = find_trace_probe(tp->call.name, tp->call.class->system); |
856 | if (old_tp) { | 862 | if (old_tp) { |
857 | unregister_trace_probe(old_tp); | 863 | ret = unregister_trace_probe(old_tp); |
864 | if (ret < 0) | ||
865 | goto end; | ||
858 | free_trace_probe(old_tp); | 866 | free_trace_probe(old_tp); |
859 | } | 867 | } |
860 | 868 | ||
@@ -892,6 +900,7 @@ static int trace_probe_module_callback(struct notifier_block *nb, | |||
892 | mutex_lock(&probe_lock); | 900 | mutex_lock(&probe_lock); |
893 | list_for_each_entry(tp, &probe_list, list) { | 901 | list_for_each_entry(tp, &probe_list, list) { |
894 | if (trace_probe_within_module(tp, mod)) { | 902 | if (trace_probe_within_module(tp, mod)) { |
903 | /* Don't need to check busy - this should have gone. */ | ||
895 | __unregister_trace_probe(tp); | 904 | __unregister_trace_probe(tp); |
896 | ret = __register_trace_probe(tp); | 905 | ret = __register_trace_probe(tp); |
897 | if (ret) | 906 | if (ret) |
@@ -1205,10 +1214,11 @@ static int create_trace_probe(int argc, char **argv) | |||
1205 | return -ENOENT; | 1214 | return -ENOENT; |
1206 | } | 1215 | } |
1207 | /* delete an event */ | 1216 | /* delete an event */ |
1208 | unregister_trace_probe(tp); | 1217 | ret = unregister_trace_probe(tp); |
1209 | free_trace_probe(tp); | 1218 | if (ret == 0) |
1219 | free_trace_probe(tp); | ||
1210 | mutex_unlock(&probe_lock); | 1220 | mutex_unlock(&probe_lock); |
1211 | return 0; | 1221 | return ret; |
1212 | } | 1222 | } |
1213 | 1223 | ||
1214 | if (argc < 2) { | 1224 | if (argc < 2) { |
@@ -1317,18 +1327,29 @@ error: | |||
1317 | return ret; | 1327 | return ret; |
1318 | } | 1328 | } |
1319 | 1329 | ||
1320 | static void release_all_trace_probes(void) | 1330 | static int release_all_trace_probes(void) |
1321 | { | 1331 | { |
1322 | struct trace_probe *tp; | 1332 | struct trace_probe *tp; |
1333 | int ret = 0; | ||
1323 | 1334 | ||
1324 | mutex_lock(&probe_lock); | 1335 | mutex_lock(&probe_lock); |
1336 | /* Ensure no probe is in use. */ | ||
1337 | list_for_each_entry(tp, &probe_list, list) | ||
1338 | if (trace_probe_is_enabled(tp)) { | ||
1339 | ret = -EBUSY; | ||
1340 | goto end; | ||
1341 | } | ||
1325 | /* TODO: Use batch unregistration */ | 1342 | /* TODO: Use batch unregistration */ |
1326 | while (!list_empty(&probe_list)) { | 1343 | while (!list_empty(&probe_list)) { |
1327 | tp = list_entry(probe_list.next, struct trace_probe, list); | 1344 | tp = list_entry(probe_list.next, struct trace_probe, list); |
1328 | unregister_trace_probe(tp); | 1345 | unregister_trace_probe(tp); |
1329 | free_trace_probe(tp); | 1346 | free_trace_probe(tp); |
1330 | } | 1347 | } |
1348 | |||
1349 | end: | ||
1331 | mutex_unlock(&probe_lock); | 1350 | mutex_unlock(&probe_lock); |
1351 | |||
1352 | return ret; | ||
1332 | } | 1353 | } |
1333 | 1354 | ||
1334 | /* Probes listing interfaces */ | 1355 | /* Probes listing interfaces */ |
@@ -1380,9 +1401,13 @@ static const struct seq_operations probes_seq_op = { | |||
1380 | 1401 | ||
1381 | static int probes_open(struct inode *inode, struct file *file) | 1402 | static int probes_open(struct inode *inode, struct file *file) |
1382 | { | 1403 | { |
1383 | if ((file->f_mode & FMODE_WRITE) && | 1404 | int ret; |
1384 | (file->f_flags & O_TRUNC)) | 1405 | |
1385 | release_all_trace_probes(); | 1406 | if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { |
1407 | ret = release_all_trace_probes(); | ||
1408 | if (ret < 0) | ||
1409 | return ret; | ||
1410 | } | ||
1386 | 1411 | ||
1387 | return seq_open(file, &probes_seq_op); | 1412 | return seq_open(file, &probes_seq_op); |
1388 | } | 1413 | } |
@@ -2055,6 +2080,21 @@ static __init int kprobe_trace_self_tests_init(void) | |||
2055 | 2080 | ||
2056 | ret = target(1, 2, 3, 4, 5, 6); | 2081 | ret = target(1, 2, 3, 4, 5, 6); |
2057 | 2082 | ||
2083 | /* Disable trace points before removing it */ | ||
2084 | tp = find_trace_probe("testprobe", KPROBE_EVENT_SYSTEM); | ||
2085 | if (WARN_ON_ONCE(tp == NULL)) { | ||
2086 | pr_warning("error on getting test probe.\n"); | ||
2087 | warn++; | ||
2088 | } else | ||
2089 | disable_trace_probe(tp, TP_FLAG_TRACE); | ||
2090 | |||
2091 | tp = find_trace_probe("testprobe2", KPROBE_EVENT_SYSTEM); | ||
2092 | if (WARN_ON_ONCE(tp == NULL)) { | ||
2093 | pr_warning("error on getting 2nd test probe.\n"); | ||
2094 | warn++; | ||
2095 | } else | ||
2096 | disable_trace_probe(tp, TP_FLAG_TRACE); | ||
2097 | |||
2058 | ret = command_trace_probe("-:testprobe"); | 2098 | ret = command_trace_probe("-:testprobe"); |
2059 | if (WARN_ON_ONCE(ret)) { | 2099 | if (WARN_ON_ONCE(ret)) { |
2060 | pr_warning("error on deleting a probe.\n"); | 2100 | pr_warning("error on deleting a probe.\n"); |
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 1f06468a10d7..6fd4ffd042f9 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c | |||
@@ -59,18 +59,19 @@ void hold_module_trace_bprintk_format(const char **start, const char **end) | |||
59 | continue; | 59 | continue; |
60 | } | 60 | } |
61 | 61 | ||
62 | fmt = NULL; | ||
62 | tb_fmt = kmalloc(sizeof(*tb_fmt), GFP_KERNEL); | 63 | tb_fmt = kmalloc(sizeof(*tb_fmt), GFP_KERNEL); |
63 | if (tb_fmt) | 64 | if (tb_fmt) { |
64 | fmt = kmalloc(strlen(*iter) + 1, GFP_KERNEL); | 65 | fmt = kmalloc(strlen(*iter) + 1, GFP_KERNEL); |
65 | if (tb_fmt && fmt) { | 66 | if (fmt) { |
66 | list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); | 67 | list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); |
67 | strcpy(fmt, *iter); | 68 | strcpy(fmt, *iter); |
68 | tb_fmt->fmt = fmt; | 69 | tb_fmt->fmt = fmt; |
69 | *iter = tb_fmt->fmt; | 70 | } else |
70 | } else { | 71 | kfree(tb_fmt); |
71 | kfree(tb_fmt); | ||
72 | *iter = NULL; | ||
73 | } | 72 | } |
73 | *iter = fmt; | ||
74 | |||
74 | } | 75 | } |
75 | mutex_unlock(&btrace_mutex); | 76 | mutex_unlock(&btrace_mutex); |
76 | } | 77 | } |
diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index b219f1449c54..db110b8ae030 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c | |||
@@ -34,11 +34,16 @@ extern struct tracepoint * const __stop___tracepoints_ptrs[]; | |||
34 | static const int tracepoint_debug; | 34 | static const int tracepoint_debug; |
35 | 35 | ||
36 | /* | 36 | /* |
37 | * tracepoints_mutex nests inside module_mutex. Tracepoints mutex protects the | 37 | * Tracepoints mutex protects the builtin and module tracepoints and the hash |
38 | * builtin and module tracepoints and the hash table. | 38 | * table, as well as the local module list. |
39 | */ | 39 | */ |
40 | static DEFINE_MUTEX(tracepoints_mutex); | 40 | static DEFINE_MUTEX(tracepoints_mutex); |
41 | 41 | ||
42 | #ifdef CONFIG_MODULES | ||
43 | /* Local list of struct module */ | ||
44 | static LIST_HEAD(tracepoint_module_list); | ||
45 | #endif /* CONFIG_MODULES */ | ||
46 | |||
42 | /* | 47 | /* |
43 | * Tracepoint hash table, containing the active tracepoints. | 48 | * Tracepoint hash table, containing the active tracepoints. |
44 | * Protected by tracepoints_mutex. | 49 | * Protected by tracepoints_mutex. |
@@ -292,9 +297,10 @@ static void disable_tracepoint(struct tracepoint *elem) | |||
292 | * @end: end of the range | 297 | * @end: end of the range |
293 | * | 298 | * |
294 | * Updates the probe callback corresponding to a range of tracepoints. | 299 | * Updates the probe callback corresponding to a range of tracepoints. |
300 | * Called with tracepoints_mutex held. | ||
295 | */ | 301 | */ |
296 | void tracepoint_update_probe_range(struct tracepoint * const *begin, | 302 | static void tracepoint_update_probe_range(struct tracepoint * const *begin, |
297 | struct tracepoint * const *end) | 303 | struct tracepoint * const *end) |
298 | { | 304 | { |
299 | struct tracepoint * const *iter; | 305 | struct tracepoint * const *iter; |
300 | struct tracepoint_entry *mark_entry; | 306 | struct tracepoint_entry *mark_entry; |
@@ -302,7 +308,6 @@ void tracepoint_update_probe_range(struct tracepoint * const *begin, | |||
302 | if (!begin) | 308 | if (!begin) |
303 | return; | 309 | return; |
304 | 310 | ||
305 | mutex_lock(&tracepoints_mutex); | ||
306 | for (iter = begin; iter < end; iter++) { | 311 | for (iter = begin; iter < end; iter++) { |
307 | mark_entry = get_tracepoint((*iter)->name); | 312 | mark_entry = get_tracepoint((*iter)->name); |
308 | if (mark_entry) { | 313 | if (mark_entry) { |
@@ -312,11 +317,27 @@ void tracepoint_update_probe_range(struct tracepoint * const *begin, | |||
312 | disable_tracepoint(*iter); | 317 | disable_tracepoint(*iter); |
313 | } | 318 | } |
314 | } | 319 | } |
315 | mutex_unlock(&tracepoints_mutex); | ||
316 | } | 320 | } |
317 | 321 | ||
322 | #ifdef CONFIG_MODULES | ||
323 | void module_update_tracepoints(void) | ||
324 | { | ||
325 | struct tp_module *tp_mod; | ||
326 | |||
327 | list_for_each_entry(tp_mod, &tracepoint_module_list, list) | ||
328 | tracepoint_update_probe_range(tp_mod->tracepoints_ptrs, | ||
329 | tp_mod->tracepoints_ptrs + tp_mod->num_tracepoints); | ||
330 | } | ||
331 | #else /* CONFIG_MODULES */ | ||
332 | void module_update_tracepoints(void) | ||
333 | { | ||
334 | } | ||
335 | #endif /* CONFIG_MODULES */ | ||
336 | |||
337 | |||
318 | /* | 338 | /* |
319 | * Update probes, removing the faulty probes. | 339 | * Update probes, removing the faulty probes. |
340 | * Called with tracepoints_mutex held. | ||
320 | */ | 341 | */ |
321 | static void tracepoint_update_probes(void) | 342 | static void tracepoint_update_probes(void) |
322 | { | 343 | { |
@@ -359,11 +380,12 @@ int tracepoint_probe_register(const char *name, void *probe, void *data) | |||
359 | 380 | ||
360 | mutex_lock(&tracepoints_mutex); | 381 | mutex_lock(&tracepoints_mutex); |
361 | old = tracepoint_add_probe(name, probe, data); | 382 | old = tracepoint_add_probe(name, probe, data); |
362 | mutex_unlock(&tracepoints_mutex); | 383 | if (IS_ERR(old)) { |
363 | if (IS_ERR(old)) | 384 | mutex_unlock(&tracepoints_mutex); |
364 | return PTR_ERR(old); | 385 | return PTR_ERR(old); |
365 | 386 | } | |
366 | tracepoint_update_probes(); /* may update entry */ | 387 | tracepoint_update_probes(); /* may update entry */ |
388 | mutex_unlock(&tracepoints_mutex); | ||
367 | release_probes(old); | 389 | release_probes(old); |
368 | return 0; | 390 | return 0; |
369 | } | 391 | } |
@@ -402,11 +424,12 @@ int tracepoint_probe_unregister(const char *name, void *probe, void *data) | |||
402 | 424 | ||
403 | mutex_lock(&tracepoints_mutex); | 425 | mutex_lock(&tracepoints_mutex); |
404 | old = tracepoint_remove_probe(name, probe, data); | 426 | old = tracepoint_remove_probe(name, probe, data); |
405 | mutex_unlock(&tracepoints_mutex); | 427 | if (IS_ERR(old)) { |
406 | if (IS_ERR(old)) | 428 | mutex_unlock(&tracepoints_mutex); |
407 | return PTR_ERR(old); | 429 | return PTR_ERR(old); |
408 | 430 | } | |
409 | tracepoint_update_probes(); /* may update entry */ | 431 | tracepoint_update_probes(); /* may update entry */ |
432 | mutex_unlock(&tracepoints_mutex); | ||
410 | release_probes(old); | 433 | release_probes(old); |
411 | return 0; | 434 | return 0; |
412 | } | 435 | } |
@@ -489,9 +512,8 @@ void tracepoint_probe_update_all(void) | |||
489 | if (!list_empty(&old_probes)) | 512 | if (!list_empty(&old_probes)) |
490 | list_replace_init(&old_probes, &release_probes); | 513 | list_replace_init(&old_probes, &release_probes); |
491 | need_update = 0; | 514 | need_update = 0; |
492 | mutex_unlock(&tracepoints_mutex); | ||
493 | |||
494 | tracepoint_update_probes(); | 515 | tracepoint_update_probes(); |
516 | mutex_unlock(&tracepoints_mutex); | ||
495 | list_for_each_entry_safe(pos, next, &release_probes, u.list) { | 517 | list_for_each_entry_safe(pos, next, &release_probes, u.list) { |
496 | list_del(&pos->u.list); | 518 | list_del(&pos->u.list); |
497 | call_rcu_sched(&pos->u.rcu, rcu_free_old_probes); | 519 | call_rcu_sched(&pos->u.rcu, rcu_free_old_probes); |
@@ -509,7 +531,7 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_update_all); | |||
509 | * Will return the first tracepoint in the range if the input tracepoint is | 531 | * Will return the first tracepoint in the range if the input tracepoint is |
510 | * NULL. | 532 | * NULL. |
511 | */ | 533 | */ |
512 | int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, | 534 | static int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, |
513 | struct tracepoint * const *begin, struct tracepoint * const *end) | 535 | struct tracepoint * const *begin, struct tracepoint * const *end) |
514 | { | 536 | { |
515 | if (!*tracepoint && begin != end) { | 537 | if (!*tracepoint && begin != end) { |
@@ -520,11 +542,12 @@ int tracepoint_get_iter_range(struct tracepoint * const **tracepoint, | |||
520 | return 1; | 542 | return 1; |
521 | return 0; | 543 | return 0; |
522 | } | 544 | } |
523 | EXPORT_SYMBOL_GPL(tracepoint_get_iter_range); | ||
524 | 545 | ||
546 | #ifdef CONFIG_MODULES | ||
525 | static void tracepoint_get_iter(struct tracepoint_iter *iter) | 547 | static void tracepoint_get_iter(struct tracepoint_iter *iter) |
526 | { | 548 | { |
527 | int found = 0; | 549 | int found = 0; |
550 | struct tp_module *iter_mod; | ||
528 | 551 | ||
529 | /* Core kernel tracepoints */ | 552 | /* Core kernel tracepoints */ |
530 | if (!iter->module) { | 553 | if (!iter->module) { |
@@ -534,12 +557,43 @@ static void tracepoint_get_iter(struct tracepoint_iter *iter) | |||
534 | if (found) | 557 | if (found) |
535 | goto end; | 558 | goto end; |
536 | } | 559 | } |
537 | /* tracepoints in modules. */ | 560 | /* Tracepoints in modules */ |
538 | found = module_get_iter_tracepoints(iter); | 561 | mutex_lock(&tracepoints_mutex); |
562 | list_for_each_entry(iter_mod, &tracepoint_module_list, list) { | ||
563 | /* | ||
564 | * Sorted module list | ||
565 | */ | ||
566 | if (iter_mod < iter->module) | ||
567 | continue; | ||
568 | else if (iter_mod > iter->module) | ||
569 | iter->tracepoint = NULL; | ||
570 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
571 | iter_mod->tracepoints_ptrs, | ||
572 | iter_mod->tracepoints_ptrs | ||
573 | + iter_mod->num_tracepoints); | ||
574 | if (found) { | ||
575 | iter->module = iter_mod; | ||
576 | break; | ||
577 | } | ||
578 | } | ||
579 | mutex_unlock(&tracepoints_mutex); | ||
539 | end: | 580 | end: |
540 | if (!found) | 581 | if (!found) |
541 | tracepoint_iter_reset(iter); | 582 | tracepoint_iter_reset(iter); |
542 | } | 583 | } |
584 | #else /* CONFIG_MODULES */ | ||
585 | static void tracepoint_get_iter(struct tracepoint_iter *iter) | ||
586 | { | ||
587 | int found = 0; | ||
588 | |||
589 | /* Core kernel tracepoints */ | ||
590 | found = tracepoint_get_iter_range(&iter->tracepoint, | ||
591 | __start___tracepoints_ptrs, | ||
592 | __stop___tracepoints_ptrs); | ||
593 | if (!found) | ||
594 | tracepoint_iter_reset(iter); | ||
595 | } | ||
596 | #endif /* CONFIG_MODULES */ | ||
543 | 597 | ||
544 | void tracepoint_iter_start(struct tracepoint_iter *iter) | 598 | void tracepoint_iter_start(struct tracepoint_iter *iter) |
545 | { | 599 | { |
@@ -566,26 +620,98 @@ EXPORT_SYMBOL_GPL(tracepoint_iter_stop); | |||
566 | 620 | ||
567 | void tracepoint_iter_reset(struct tracepoint_iter *iter) | 621 | void tracepoint_iter_reset(struct tracepoint_iter *iter) |
568 | { | 622 | { |
623 | #ifdef CONFIG_MODULES | ||
569 | iter->module = NULL; | 624 | iter->module = NULL; |
625 | #endif /* CONFIG_MODULES */ | ||
570 | iter->tracepoint = NULL; | 626 | iter->tracepoint = NULL; |
571 | } | 627 | } |
572 | EXPORT_SYMBOL_GPL(tracepoint_iter_reset); | 628 | EXPORT_SYMBOL_GPL(tracepoint_iter_reset); |
573 | 629 | ||
574 | #ifdef CONFIG_MODULES | 630 | #ifdef CONFIG_MODULES |
631 | static int tracepoint_module_coming(struct module *mod) | ||
632 | { | ||
633 | struct tp_module *tp_mod, *iter; | ||
634 | int ret = 0; | ||
635 | |||
636 | /* | ||
637 | * We skip modules that tain the kernel, especially those with different | ||
638 | * module header (for forced load), to make sure we don't cause a crash. | ||
639 | */ | ||
640 | if (mod->taints) | ||
641 | return 0; | ||
642 | mutex_lock(&tracepoints_mutex); | ||
643 | tp_mod = kmalloc(sizeof(struct tp_module), GFP_KERNEL); | ||
644 | if (!tp_mod) { | ||
645 | ret = -ENOMEM; | ||
646 | goto end; | ||
647 | } | ||
648 | tp_mod->num_tracepoints = mod->num_tracepoints; | ||
649 | tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs; | ||
650 | |||
651 | /* | ||
652 | * tracepoint_module_list is kept sorted by struct module pointer | ||
653 | * address for iteration on tracepoints from a seq_file that can release | ||
654 | * the mutex between calls. | ||
655 | */ | ||
656 | list_for_each_entry_reverse(iter, &tracepoint_module_list, list) { | ||
657 | BUG_ON(iter == tp_mod); /* Should never be in the list twice */ | ||
658 | if (iter < tp_mod) { | ||
659 | /* We belong to the location right after iter. */ | ||
660 | list_add(&tp_mod->list, &iter->list); | ||
661 | goto module_added; | ||
662 | } | ||
663 | } | ||
664 | /* We belong to the beginning of the list */ | ||
665 | list_add(&tp_mod->list, &tracepoint_module_list); | ||
666 | module_added: | ||
667 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | ||
668 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
669 | end: | ||
670 | mutex_unlock(&tracepoints_mutex); | ||
671 | return ret; | ||
672 | } | ||
673 | |||
674 | static int tracepoint_module_going(struct module *mod) | ||
675 | { | ||
676 | struct tp_module *pos; | ||
677 | |||
678 | mutex_lock(&tracepoints_mutex); | ||
679 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | ||
680 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
681 | list_for_each_entry(pos, &tracepoint_module_list, list) { | ||
682 | if (pos->tracepoints_ptrs == mod->tracepoints_ptrs) { | ||
683 | list_del(&pos->list); | ||
684 | kfree(pos); | ||
685 | break; | ||
686 | } | ||
687 | } | ||
688 | /* | ||
689 | * In the case of modules that were tainted at "coming", we'll simply | ||
690 | * walk through the list without finding it. We cannot use the "tainted" | ||
691 | * flag on "going", in case a module taints the kernel only after being | ||
692 | * loaded. | ||
693 | */ | ||
694 | mutex_unlock(&tracepoints_mutex); | ||
695 | return 0; | ||
696 | } | ||
575 | 697 | ||
576 | int tracepoint_module_notify(struct notifier_block *self, | 698 | int tracepoint_module_notify(struct notifier_block *self, |
577 | unsigned long val, void *data) | 699 | unsigned long val, void *data) |
578 | { | 700 | { |
579 | struct module *mod = data; | 701 | struct module *mod = data; |
702 | int ret = 0; | ||
580 | 703 | ||
581 | switch (val) { | 704 | switch (val) { |
582 | case MODULE_STATE_COMING: | 705 | case MODULE_STATE_COMING: |
706 | ret = tracepoint_module_coming(mod); | ||
707 | break; | ||
708 | case MODULE_STATE_LIVE: | ||
709 | break; | ||
583 | case MODULE_STATE_GOING: | 710 | case MODULE_STATE_GOING: |
584 | tracepoint_update_probe_range(mod->tracepoints_ptrs, | 711 | ret = tracepoint_module_going(mod); |
585 | mod->tracepoints_ptrs + mod->num_tracepoints); | ||
586 | break; | 712 | break; |
587 | } | 713 | } |
588 | return 0; | 714 | return ret; |
589 | } | 715 | } |
590 | 716 | ||
591 | struct notifier_block tracepoint_module_nb = { | 717 | struct notifier_block tracepoint_module_nb = { |
@@ -598,7 +724,6 @@ static int init_tracepoints(void) | |||
598 | return register_module_notifier(&tracepoint_module_nb); | 724 | return register_module_notifier(&tracepoint_module_nb); |
599 | } | 725 | } |
600 | __initcall(init_tracepoints); | 726 | __initcall(init_tracepoints); |
601 | |||
602 | #endif /* CONFIG_MODULES */ | 727 | #endif /* CONFIG_MODULES */ |
603 | 728 | ||
604 | #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS | 729 | #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS |
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 36491cd5b7d4..d680381b0e9c 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c | |||
@@ -321,7 +321,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) | |||
321 | */ | 321 | */ |
322 | static int watchdog(void *unused) | 322 | static int watchdog(void *unused) |
323 | { | 323 | { |
324 | static struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; | 324 | struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; |
325 | struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); | 325 | struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); |
326 | 326 | ||
327 | sched_setscheduler(current, SCHED_FIFO, ¶m); | 327 | sched_setscheduler(current, SCHED_FIFO, ¶m); |
@@ -350,7 +350,8 @@ static int watchdog(void *unused) | |||
350 | set_current_state(TASK_INTERRUPTIBLE); | 350 | set_current_state(TASK_INTERRUPTIBLE); |
351 | } | 351 | } |
352 | __set_current_state(TASK_RUNNING); | 352 | __set_current_state(TASK_RUNNING); |
353 | 353 | param.sched_priority = 0; | |
354 | sched_setscheduler(current, SCHED_NORMAL, ¶m); | ||
354 | return 0; | 355 | return 0; |
355 | } | 356 | } |
356 | 357 | ||
@@ -438,7 +439,7 @@ static int watchdog_enable(int cpu) | |||
438 | 439 | ||
439 | /* create the watchdog thread */ | 440 | /* create the watchdog thread */ |
440 | if (!p) { | 441 | if (!p) { |
441 | p = kthread_create(watchdog, (void *)(unsigned long)cpu, "watchdog/%d", cpu); | 442 | p = kthread_create_on_node(watchdog, NULL, cpu_to_node(cpu), "watchdog/%d", cpu); |
442 | if (IS_ERR(p)) { | 443 | if (IS_ERR(p)) { |
443 | printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu); | 444 | printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu); |
444 | if (!err) { | 445 | if (!err) { |
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 85c5f026930d..fe6762ed56bd 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -72,6 +72,19 @@ OPTIONS | |||
72 | CPUs are specified with -: 0-2. Default is to report samples on all | 72 | CPUs are specified with -: 0-2. Default is to report samples on all |
73 | CPUs. | 73 | CPUs. |
74 | 74 | ||
75 | --asm-raw:: | ||
76 | Show raw instruction encoding of assembly instructions. | ||
77 | |||
78 | --source:: | ||
79 | Interleave source code with assembly code. Enabled by default, | ||
80 | disable with --no-source. | ||
81 | |||
82 | --symfs=<directory>:: | ||
83 | Look for files with symbols relative to this directory. | ||
84 | |||
85 | -M:: | ||
86 | --disassembler-style=:: Set disassembler style for objdump. | ||
87 | |||
75 | SEE ALSO | 88 | SEE ALSO |
76 | -------- | 89 | -------- |
77 | linkperf:perf-record[1], linkperf:perf-report[1] | 90 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt index 5eaac6f26d51..cc22325ffd1b 100644 --- a/tools/perf/Documentation/perf-buildid-list.txt +++ b/tools/perf/Documentation/perf-buildid-list.txt | |||
@@ -16,6 +16,9 @@ This command displays the buildids found in a perf.data file, so that other | |||
16 | tools can be used to fetch packages with matching symbol tables for use by | 16 | tools can be used to fetch packages with matching symbol tables for use by |
17 | perf report. | 17 | perf report. |
18 | 18 | ||
19 | It can also be used to show the build id of the running kernel or in an ELF | ||
20 | file using -i/--input. | ||
21 | |||
19 | OPTIONS | 22 | OPTIONS |
20 | ------- | 23 | ------- |
21 | -H:: | 24 | -H:: |
@@ -27,6 +30,9 @@ OPTIONS | |||
27 | -f:: | 30 | -f:: |
28 | --force:: | 31 | --force:: |
29 | Don't do ownership validation. | 32 | Don't do ownership validation. |
33 | -k:: | ||
34 | --kernel:: | ||
35 | Show running kernel build id. | ||
30 | -v:: | 36 | -v:: |
31 | --verbose:: | 37 | --verbose:: |
32 | Be more verbose. | 38 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 04253c07d19a..212f24d672e1 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -134,6 +134,24 @@ OPTIONS | |||
134 | CPUs are specified with -: 0-2. Default is to report samples on all | 134 | CPUs are specified with -: 0-2. Default is to report samples on all |
135 | CPUs. | 135 | CPUs. |
136 | 136 | ||
137 | -M:: | ||
138 | --disassembler-style=:: Set disassembler style for objdump. | ||
139 | |||
140 | --source:: | ||
141 | Interleave source code with assembly code. Enabled by default, | ||
142 | disable with --no-source. | ||
143 | |||
144 | --asm-raw:: | ||
145 | Show raw instruction encoding of assembly instructions. | ||
146 | |||
147 | --show-total-period:: Show a column with the sum of periods. | ||
148 | |||
149 | -I:: | ||
150 | --show-info:: | ||
151 | Display extended information about the perf.data file. This adds | ||
152 | information which may be very large and thus may clutter the display. | ||
153 | It currently includes: cpu and numa topology of the host system. | ||
154 | |||
137 | SEE ALSO | 155 | SEE ALSO |
138 | -------- | 156 | -------- |
139 | linkperf:perf-stat[1] | 157 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 46822d5fde1c..5b212b57f70b 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt | |||
@@ -8,7 +8,7 @@ perf-sched - Tool to trace/measure scheduler properties (latencies) | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf sched' {record|latency|map|replay|trace} | 11 | 'perf sched' {record|latency|map|replay|script} |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
@@ -20,8 +20,8 @@ There are five variants of perf sched: | |||
20 | 'perf sched latency' to report the per task scheduling latencies | 20 | 'perf sched latency' to report the per task scheduling latencies |
21 | and other scheduling properties of the workload. | 21 | and other scheduling properties of the workload. |
22 | 22 | ||
23 | 'perf sched trace' to see a detailed trace of the workload that | 23 | 'perf sched script' to see a detailed trace of the workload that |
24 | was recorded. | 24 | was recorded (aliased to 'perf script' for now). |
25 | 25 | ||
26 | 'perf sched replay' to simulate the workload that was recorded | 26 | 'perf sched replay' to simulate the workload that was recorded |
27 | via perf sched record. (this is done by starting up mockup threads | 27 | via perf sched record. (this is done by starting up mockup threads |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index db017867d9e8..dec87ecb530e 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -188,6 +188,13 @@ OPTIONS | |||
188 | CPUs are specified with -: 0-2. Default is to report samples on all | 188 | CPUs are specified with -: 0-2. Default is to report samples on all |
189 | CPUs. | 189 | CPUs. |
190 | 190 | ||
191 | -I:: | ||
192 | --show-info:: | ||
193 | Display extended information about the perf.data file. This adds | ||
194 | information which may be very large and thus may clutter the display. | ||
195 | It currently includes: cpu and numa topology of the host system. | ||
196 | It can only be used with the perf script report mode. | ||
197 | |||
191 | SEE ALSO | 198 | SEE ALSO |
192 | -------- | 199 | -------- |
193 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 200 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 918cc38ee6d1..8966b9ab2014 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -94,6 +94,22 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha | |||
94 | corresponding events, i.e., they always refer to events defined earlier on the command | 94 | corresponding events, i.e., they always refer to events defined earlier on the command |
95 | line. | 95 | line. |
96 | 96 | ||
97 | -o file:: | ||
98 | --output file:: | ||
99 | Print the output into the designated file. | ||
100 | |||
101 | --append:: | ||
102 | Append to the output file designated with the -o option. Ignored if -o is not specified. | ||
103 | |||
104 | --log-fd:: | ||
105 | |||
106 | Log output to fd, instead of stderr. Complementary to --output, and mutually exclusive | ||
107 | with it. --append may be used here. Examples: | ||
108 | 3>results perf stat --log-fd 3 -- $cmd | ||
109 | 3>>results perf stat --log-fd 3 --append -- $cmd | ||
110 | |||
111 | |||
112 | |||
97 | EXAMPLES | 113 | EXAMPLES |
98 | -------- | 114 | -------- |
99 | 115 | ||
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index f6eb1cdafb77..b1a5bbbfebef 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -106,6 +106,51 @@ Default is to monitor all CPUS. | |||
106 | --zero:: | 106 | --zero:: |
107 | Zero history across display updates. | 107 | Zero history across display updates. |
108 | 108 | ||
109 | -s:: | ||
110 | --sort:: | ||
111 | Sort by key(s): pid, comm, dso, symbol, parent | ||
112 | |||
113 | -n:: | ||
114 | --show-nr-samples:: | ||
115 | Show a column with the number of samples. | ||
116 | |||
117 | --show-total-period:: | ||
118 | Show a column with the sum of periods. | ||
119 | |||
120 | --dsos:: | ||
121 | Only consider symbols in these dsos. | ||
122 | |||
123 | --comms:: | ||
124 | Only consider symbols in these comms. | ||
125 | |||
126 | --symbols:: | ||
127 | Only consider these symbols. | ||
128 | |||
129 | -M:: | ||
130 | --disassembler-style=:: Set disassembler style for objdump. | ||
131 | |||
132 | --source:: | ||
133 | Interleave source code with assembly code. Enabled by default, | ||
134 | disable with --no-source. | ||
135 | |||
136 | --asm-raw:: | ||
137 | Show raw instruction encoding of assembly instructions. | ||
138 | |||
139 | -G [type,min,order]:: | ||
140 | --call-graph:: | ||
141 | Display call chains using type, min percent threshold and order. | ||
142 | type can be either: | ||
143 | - flat: single column, linear exposure of call chains. | ||
144 | - graph: use a graph tree, displaying absolute overhead rates. | ||
145 | - fractal: like graph, but displays relative rates. Each branch of | ||
146 | the tree is considered as a new profiled object. | ||
147 | |||
148 | order can be either: | ||
149 | - callee: callee based call graph. | ||
150 | - caller: inverted caller based call graph. | ||
151 | |||
152 | Default: fractal,0.5,callee. | ||
153 | |||
109 | INTERACTIVE PROMPTING KEYS | 154 | INTERACTIVE PROMPTING KEYS |
110 | -------------------------- | 155 | -------------------------- |
111 | 156 | ||
@@ -130,9 +175,6 @@ INTERACTIVE PROMPTING KEYS | |||
130 | [S]:: | 175 | [S]:: |
131 | Stop annotation, return to full profile display. | 176 | Stop annotation, return to full profile display. |
132 | 177 | ||
133 | [w]:: | ||
134 | Toggle between weighted sum and individual count[E]r profile. | ||
135 | |||
136 | [z]:: | 178 | [z]:: |
137 | Toggle event count zeroing across display updates. | 179 | Toggle event count zeroing across display updates. |
138 | 180 | ||
diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example new file mode 100644 index 000000000000..d1448668f4d4 --- /dev/null +++ b/tools/perf/Documentation/perfconfig.example | |||
@@ -0,0 +1,20 @@ | |||
1 | [colors] | ||
2 | |||
3 | # These were the old defaults | ||
4 | top = red, lightgray | ||
5 | medium = green, lightgray | ||
6 | normal = black, lightgray | ||
7 | selected = lightgray, magenta | ||
8 | code = blue, lightgray | ||
9 | |||
10 | [tui] | ||
11 | |||
12 | # Defaults if linked with libslang | ||
13 | report = on | ||
14 | annotate = on | ||
15 | top = on | ||
16 | |||
17 | [buildid] | ||
18 | |||
19 | # Default, disable using /dev/null | ||
20 | dir = /root/.debug | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e9d5c271db69..b98e3075646b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -466,13 +466,13 @@ else | |||
466 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o | 466 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o |
467 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o | 467 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o |
468 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o | 468 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o |
469 | LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o | ||
470 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o | 469 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o |
471 | LIB_OBJS += $(OUTPUT)util/ui/progress.o | 470 | LIB_OBJS += $(OUTPUT)util/ui/progress.o |
472 | LIB_OBJS += $(OUTPUT)util/ui/util.o | 471 | LIB_OBJS += $(OUTPUT)util/ui/util.o |
473 | LIB_H += util/ui/browser.h | 472 | LIB_H += util/ui/browser.h |
474 | LIB_H += util/ui/browsers/map.h | 473 | LIB_H += util/ui/browsers/map.h |
475 | LIB_H += util/ui/helpline.h | 474 | LIB_H += util/ui/helpline.h |
475 | LIB_H += util/ui/keysyms.h | ||
476 | LIB_H += util/ui/libslang.h | 476 | LIB_H += util/ui/libslang.h |
477 | LIB_H += util/ui/progress.h | 477 | LIB_H += util/ui/progress.h |
478 | LIB_H += util/ui/util.h | 478 | LIB_H += util/ui/util.h |
@@ -729,9 +729,6 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS | |||
729 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS | 729 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS |
730 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 730 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
731 | 731 | ||
732 | $(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS | ||
733 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
734 | |||
735 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | 732 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS |
736 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 733 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
737 | 734 | ||
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index 15130b50dfe3..744e629797be 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile | |||
@@ -2,3 +2,4 @@ ifndef NO_DWARF | |||
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | endif | 4 | endif |
5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c new file mode 100644 index 000000000000..eba80c292945 --- /dev/null +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "../../util/header.h" | ||
8 | |||
9 | #define __stringify_1(x) #x | ||
10 | #define __stringify(x) __stringify_1(x) | ||
11 | |||
12 | #define mfspr(rn) ({unsigned long rval; \ | ||
13 | asm volatile("mfspr %0," __stringify(rn) \ | ||
14 | : "=r" (rval)); rval; }) | ||
15 | |||
16 | #define SPRN_PVR 0x11F /* Processor Version Register */ | ||
17 | #define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ | ||
18 | #define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */ | ||
19 | |||
20 | int | ||
21 | get_cpuid(char *buffer, size_t sz) | ||
22 | { | ||
23 | unsigned long pvr; | ||
24 | int nb; | ||
25 | |||
26 | pvr = mfspr(SPRN_PVR); | ||
27 | |||
28 | nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); | ||
29 | |||
30 | /* look for end marker to ensure the entire data fit */ | ||
31 | if (strchr(buffer, '$')) { | ||
32 | buffer[nb-1] = '\0'; | ||
33 | return 0; | ||
34 | } | ||
35 | return -1; | ||
36 | } | ||
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 15130b50dfe3..744e629797be 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile | |||
@@ -2,3 +2,4 @@ ifndef NO_DWARF | |||
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | endif | 4 | endif |
5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | ||
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c new file mode 100644 index 000000000000..f94006068d2b --- /dev/null +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | |||
7 | #include "../../util/header.h" | ||
8 | |||
9 | static inline void | ||
10 | cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, | ||
11 | unsigned int *d) | ||
12 | { | ||
13 | __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t" | ||
14 | "movl %%ebx, %%esi\n\t.byte 0x5b" | ||
15 | : "=a" (*a), | ||
16 | "=S" (*b), | ||
17 | "=c" (*c), | ||
18 | "=d" (*d) | ||
19 | : "a" (op)); | ||
20 | } | ||
21 | |||
22 | int | ||
23 | get_cpuid(char *buffer, size_t sz) | ||
24 | { | ||
25 | unsigned int a, b, c, d, lvl; | ||
26 | int family = -1, model = -1, step = -1; | ||
27 | int nb; | ||
28 | char vendor[16]; | ||
29 | |||
30 | cpuid(0, &lvl, &b, &c, &d); | ||
31 | strncpy(&vendor[0], (char *)(&b), 4); | ||
32 | strncpy(&vendor[4], (char *)(&d), 4); | ||
33 | strncpy(&vendor[8], (char *)(&c), 4); | ||
34 | vendor[12] = '\0'; | ||
35 | |||
36 | if (lvl >= 1) { | ||
37 | cpuid(1, &a, &b, &c, &d); | ||
38 | |||
39 | family = (a >> 8) & 0xf; /* bits 11 - 8 */ | ||
40 | model = (a >> 4) & 0xf; /* Bits 7 - 4 */ | ||
41 | step = a & 0xf; | ||
42 | |||
43 | /* extended family */ | ||
44 | if (family == 0xf) | ||
45 | family += (a >> 20) & 0xff; | ||
46 | |||
47 | /* extended model */ | ||
48 | if (family >= 0x6) | ||
49 | model += ((a >> 16) & 0xf) << 4; | ||
50 | } | ||
51 | nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); | ||
52 | |||
53 | /* look for end marker to ensure the entire data fit */ | ||
54 | if (strchr(buffer, '$')) { | ||
55 | buffer[nb-1] = '\0'; | ||
56 | return 0; | ||
57 | } | ||
58 | return -1; | ||
59 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 555aefd7fe01..46b4c24f338e 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -114,10 +114,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) | |||
114 | print_line, full_paths, 0, 0); | 114 | print_line, full_paths, 0, 0); |
115 | } | 115 | } |
116 | 116 | ||
117 | static void hists__find_annotations(struct hists *self, int evidx) | 117 | static void hists__find_annotations(struct hists *self, int evidx, |
118 | int nr_events) | ||
118 | { | 119 | { |
119 | struct rb_node *nd = rb_first(&self->entries), *next; | 120 | struct rb_node *nd = rb_first(&self->entries), *next; |
120 | int key = KEY_RIGHT; | 121 | int key = K_RIGHT; |
121 | 122 | ||
122 | while (nd) { | 123 | while (nd) { |
123 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 124 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
@@ -129,7 +130,7 @@ static void hists__find_annotations(struct hists *self, int evidx) | |||
129 | notes = symbol__annotation(he->ms.sym); | 130 | notes = symbol__annotation(he->ms.sym); |
130 | if (notes->src == NULL) { | 131 | if (notes->src == NULL) { |
131 | find_next: | 132 | find_next: |
132 | if (key == KEY_LEFT) | 133 | if (key == K_LEFT) |
133 | nd = rb_prev(nd); | 134 | nd = rb_prev(nd); |
134 | else | 135 | else |
135 | nd = rb_next(nd); | 136 | nd = rb_next(nd); |
@@ -137,12 +138,13 @@ find_next: | |||
137 | } | 138 | } |
138 | 139 | ||
139 | if (use_browser > 0) { | 140 | if (use_browser > 0) { |
140 | key = hist_entry__tui_annotate(he, evidx); | 141 | key = hist_entry__tui_annotate(he, evidx, nr_events, |
142 | NULL, NULL, 0); | ||
141 | switch (key) { | 143 | switch (key) { |
142 | case KEY_RIGHT: | 144 | case K_RIGHT: |
143 | next = rb_next(nd); | 145 | next = rb_next(nd); |
144 | break; | 146 | break; |
145 | case KEY_LEFT: | 147 | case K_LEFT: |
146 | next = rb_prev(nd); | 148 | next = rb_prev(nd); |
147 | break; | 149 | break; |
148 | default: | 150 | default: |
@@ -215,7 +217,8 @@ static int __cmd_annotate(void) | |||
215 | total_nr_samples += nr_samples; | 217 | total_nr_samples += nr_samples; |
216 | hists__collapse_resort(hists); | 218 | hists__collapse_resort(hists); |
217 | hists__output_resort(hists); | 219 | hists__output_resort(hists); |
218 | hists__find_annotations(hists, pos->idx); | 220 | hists__find_annotations(hists, pos->idx, |
221 | session->evlist->nr_entries); | ||
219 | } | 222 | } |
220 | } | 223 | } |
221 | 224 | ||
@@ -267,6 +270,14 @@ static const struct option options[] = { | |||
267 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 270 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
268 | "Don't shorten the displayed pathnames"), | 271 | "Don't shorten the displayed pathnames"), |
269 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 272 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
273 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
274 | "Look for files with symbols relative to this directory"), | ||
275 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
276 | "Interleave source code with assembly code (default)"), | ||
277 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
278 | "Display raw encoding of assembly instructions (default)"), | ||
279 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
280 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
270 | OPT_END() | 281 | OPT_END() |
271 | }; | 282 | }; |
272 | 283 | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 5af32ae9031e..cb690a65bf02 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * builtin-buildid-list.c | 2 | * builtin-buildid-list.c |
3 | * | 3 | * |
4 | * Builtin buildid-list command: list buildids in perf.data | 4 | * Builtin buildid-list command: list buildids in perf.data, in the running |
5 | * kernel and in ELF files. | ||
5 | * | 6 | * |
6 | * Copyright (C) 2009, Red Hat Inc. | 7 | * Copyright (C) 2009, Red Hat Inc. |
7 | * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com> | 8 | * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com> |
@@ -15,8 +16,11 @@ | |||
15 | #include "util/session.h" | 16 | #include "util/session.h" |
16 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
17 | 18 | ||
19 | #include <libelf.h> | ||
20 | |||
18 | static char const *input_name = "perf.data"; | 21 | static char const *input_name = "perf.data"; |
19 | static bool force; | 22 | static bool force; |
23 | static bool show_kernel; | ||
20 | static bool with_hits; | 24 | static bool with_hits; |
21 | 25 | ||
22 | static const char * const buildid_list_usage[] = { | 26 | static const char * const buildid_list_usage[] = { |
@@ -29,12 +33,13 @@ static const struct option options[] = { | |||
29 | OPT_STRING('i', "input", &input_name, "file", | 33 | OPT_STRING('i', "input", &input_name, "file", |
30 | "input file name"), | 34 | "input file name"), |
31 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 35 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
36 | OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"), | ||
32 | OPT_INCR('v', "verbose", &verbose, | 37 | OPT_INCR('v', "verbose", &verbose, |
33 | "be more verbose"), | 38 | "be more verbose"), |
34 | OPT_END() | 39 | OPT_END() |
35 | }; | 40 | }; |
36 | 41 | ||
37 | static int __cmd_buildid_list(void) | 42 | static int perf_session__list_build_ids(void) |
38 | { | 43 | { |
39 | struct perf_session *session; | 44 | struct perf_session *session; |
40 | 45 | ||
@@ -52,6 +57,49 @@ static int __cmd_buildid_list(void) | |||
52 | return 0; | 57 | return 0; |
53 | } | 58 | } |
54 | 59 | ||
60 | static int sysfs__fprintf_build_id(FILE *fp) | ||
61 | { | ||
62 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
63 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
64 | |||
65 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
66 | sizeof(kallsyms_build_id)) != 0) | ||
67 | return -1; | ||
68 | |||
69 | build_id__sprintf(kallsyms_build_id, sizeof(kallsyms_build_id), | ||
70 | sbuild_id); | ||
71 | fprintf(fp, "%s\n", sbuild_id); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int filename__fprintf_build_id(const char *name, FILE *fp) | ||
76 | { | ||
77 | u8 build_id[BUILD_ID_SIZE]; | ||
78 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
79 | |||
80 | if (filename__read_build_id(name, build_id, | ||
81 | sizeof(build_id)) != sizeof(build_id)) | ||
82 | return 0; | ||
83 | |||
84 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
85 | return fprintf(fp, "%s\n", sbuild_id); | ||
86 | } | ||
87 | |||
88 | static int __cmd_buildid_list(void) | ||
89 | { | ||
90 | if (show_kernel) | ||
91 | return sysfs__fprintf_build_id(stdout); | ||
92 | |||
93 | elf_version(EV_CURRENT); | ||
94 | /* | ||
95 | * See if this is an ELF file first: | ||
96 | */ | ||
97 | if (filename__fprintf_build_id(input_name, stdout)) | ||
98 | return 0; | ||
99 | |||
100 | return perf_session__list_build_ids(); | ||
101 | } | ||
102 | |||
55 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) | 103 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) |
56 | { | 104 | { |
57 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); | 105 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index e8219990f8b8..b39f3a1ee7dc 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -162,7 +162,7 @@ static int __cmd_diff(void) | |||
162 | 162 | ||
163 | hists__match(&session[0]->hists, &session[1]->hists); | 163 | hists__match(&session[0]->hists, &session[1]->hists); |
164 | hists__fprintf(&session[1]->hists, &session[0]->hists, | 164 | hists__fprintf(&session[1]->hists, &session[0]->hists, |
165 | show_displacement, stdout); | 165 | show_displacement, true, 0, 0, stdout); |
166 | out_delete: | 166 | out_delete: |
167 | for (i = 0; i < 2; ++i) | 167 | for (i = 0; i < 2; ++i) |
168 | perf_session__delete(session[i]); | 168 | perf_session__delete(session[i]); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f4c3fbee4bad..f82480fa7f27 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -73,6 +73,7 @@ static off_t post_processing_offset; | |||
73 | 73 | ||
74 | static struct perf_session *session; | 74 | static struct perf_session *session; |
75 | static const char *cpu_list; | 75 | static const char *cpu_list; |
76 | static const char *progname; | ||
76 | 77 | ||
77 | static void advance_output(size_t size) | 78 | static void advance_output(size_t size) |
78 | { | 79 | { |
@@ -137,17 +138,29 @@ static void mmap_read(struct perf_mmap *md) | |||
137 | 138 | ||
138 | static volatile int done = 0; | 139 | static volatile int done = 0; |
139 | static volatile int signr = -1; | 140 | static volatile int signr = -1; |
141 | static volatile int child_finished = 0; | ||
140 | 142 | ||
141 | static void sig_handler(int sig) | 143 | static void sig_handler(int sig) |
142 | { | 144 | { |
145 | if (sig == SIGCHLD) | ||
146 | child_finished = 1; | ||
147 | |||
143 | done = 1; | 148 | done = 1; |
144 | signr = sig; | 149 | signr = sig; |
145 | } | 150 | } |
146 | 151 | ||
147 | static void sig_atexit(void) | 152 | static void sig_atexit(void) |
148 | { | 153 | { |
149 | if (child_pid > 0) | 154 | int status; |
150 | kill(child_pid, SIGTERM); | 155 | |
156 | if (child_pid > 0) { | ||
157 | if (!child_finished) | ||
158 | kill(child_pid, SIGTERM); | ||
159 | |||
160 | wait(&status); | ||
161 | if (WIFSIGNALED(status)) | ||
162 | psignal(WTERMSIG(status), progname); | ||
163 | } | ||
151 | 164 | ||
152 | if (signr == -1 || signr == SIGUSR1) | 165 | if (signr == -1 || signr == SIGUSR1) |
153 | return; | 166 | return; |
@@ -446,6 +459,8 @@ static int __cmd_record(int argc, const char **argv) | |||
446 | char buf; | 459 | char buf; |
447 | struct machine *machine; | 460 | struct machine *machine; |
448 | 461 | ||
462 | progname = argv[0]; | ||
463 | |||
449 | page_size = sysconf(_SC_PAGE_SIZE); | 464 | page_size = sysconf(_SC_PAGE_SIZE); |
450 | 465 | ||
451 | atexit(sig_atexit); | 466 | atexit(sig_atexit); |
@@ -514,6 +529,19 @@ static int __cmd_record(int argc, const char **argv) | |||
514 | if (have_tracepoints(&evsel_list->entries)) | 529 | if (have_tracepoints(&evsel_list->entries)) |
515 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 530 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
516 | 531 | ||
532 | perf_header__set_feat(&session->header, HEADER_HOSTNAME); | ||
533 | perf_header__set_feat(&session->header, HEADER_OSRELEASE); | ||
534 | perf_header__set_feat(&session->header, HEADER_ARCH); | ||
535 | perf_header__set_feat(&session->header, HEADER_CPUDESC); | ||
536 | perf_header__set_feat(&session->header, HEADER_NRCPUS); | ||
537 | perf_header__set_feat(&session->header, HEADER_EVENT_DESC); | ||
538 | perf_header__set_feat(&session->header, HEADER_CMDLINE); | ||
539 | perf_header__set_feat(&session->header, HEADER_VERSION); | ||
540 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); | ||
541 | perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); | ||
542 | perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); | ||
543 | perf_header__set_feat(&session->header, HEADER_CPUID); | ||
544 | |||
517 | /* 512 kiB: default amount of unprivileged mlocked memory */ | 545 | /* 512 kiB: default amount of unprivileged mlocked memory */ |
518 | if (mmap_pages == UINT_MAX) | 546 | if (mmap_pages == UINT_MAX) |
519 | mmap_pages = (512 * 1024) / page_size; | 547 | mmap_pages = (512 * 1024) / page_size; |
@@ -785,6 +813,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
785 | int err = -ENOMEM; | 813 | int err = -ENOMEM; |
786 | struct perf_evsel *pos; | 814 | struct perf_evsel *pos; |
787 | 815 | ||
816 | perf_header__set_cmdline(argc, argv); | ||
817 | |||
788 | evsel_list = perf_evlist__new(NULL, NULL); | 818 | evsel_list = perf_evlist__new(NULL, NULL); |
789 | if (evsel_list == NULL) | 819 | if (evsel_list == NULL) |
790 | return -ENOMEM; | 820 | return -ENOMEM; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7ff277bdb78..4d7c8340c326 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -40,6 +40,7 @@ static char const *input_name = "perf.data"; | |||
40 | static bool force, use_tui, use_stdio; | 40 | static bool force, use_tui, use_stdio; |
41 | static bool hide_unresolved; | 41 | static bool hide_unresolved; |
42 | static bool dont_use_callchains; | 42 | static bool dont_use_callchains; |
43 | static bool show_full_info; | ||
43 | 44 | ||
44 | static bool show_threads; | 45 | static bool show_threads; |
45 | static struct perf_read_values show_threads_values; | 46 | static struct perf_read_values show_threads_values; |
@@ -229,13 +230,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
229 | 230 | ||
230 | list_for_each_entry(pos, &evlist->entries, node) { | 231 | list_for_each_entry(pos, &evlist->entries, node) { |
231 | struct hists *hists = &pos->hists; | 232 | struct hists *hists = &pos->hists; |
232 | const char *evname = NULL; | 233 | const char *evname = event_name(pos); |
233 | |||
234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | ||
235 | evname = event_name(pos); | ||
236 | 234 | ||
237 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 235 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
238 | hists__fprintf(hists, NULL, false, stdout); | 236 | hists__fprintf(hists, NULL, false, true, 0, 0, stdout); |
239 | fprintf(stdout, "\n\n"); | 237 | fprintf(stdout, "\n\n"); |
240 | } | 238 | } |
241 | 239 | ||
@@ -276,6 +274,9 @@ static int __cmd_report(void) | |||
276 | goto out_delete; | 274 | goto out_delete; |
277 | } | 275 | } |
278 | 276 | ||
277 | if (use_browser <= 0) | ||
278 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
279 | |||
279 | if (show_threads) | 280 | if (show_threads) |
280 | perf_read_values_init(&show_threads_values); | 281 | perf_read_values_init(&show_threads_values); |
281 | 282 | ||
@@ -330,9 +331,10 @@ static int __cmd_report(void) | |||
330 | goto out_delete; | 331 | goto out_delete; |
331 | } | 332 | } |
332 | 333 | ||
333 | if (use_browser > 0) | 334 | if (use_browser > 0) { |
334 | perf_evlist__tui_browse_hists(session->evlist, help); | 335 | perf_evlist__tui_browse_hists(session->evlist, help, |
335 | else | 336 | NULL, NULL, 0); |
337 | } else | ||
336 | perf_evlist__tty_browse_hists(session->evlist, help); | 338 | perf_evlist__tty_browse_hists(session->evlist, help); |
337 | 339 | ||
338 | out_delete: | 340 | out_delete: |
@@ -487,6 +489,16 @@ static const struct option options[] = { | |||
487 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 489 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
488 | "Look for files with symbols relative to this directory"), | 490 | "Look for files with symbols relative to this directory"), |
489 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 491 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
492 | OPT_BOOLEAN('I', "show-info", &show_full_info, | ||
493 | "Display extended information about perf.data file"), | ||
494 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
495 | "Interleave source code with assembly code (default)"), | ||
496 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
497 | "Display raw encoding of assembly instructions (default)"), | ||
498 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
499 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
500 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | ||
501 | "Show a column with the sum of periods"), | ||
490 | OPT_END() | 502 | OPT_END() |
491 | }; | 503 | }; |
492 | 504 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 09024ec2ab2e..2f62a2952269 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -22,6 +22,7 @@ static u64 last_timestamp; | |||
22 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
23 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
24 | static bool no_callchain; | 24 | static bool no_callchain; |
25 | static bool show_full_info; | ||
25 | static const char *cpu_list; | 26 | static const char *cpu_list; |
26 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 27 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
27 | 28 | ||
@@ -1083,7 +1084,8 @@ static const struct option options[] = { | |||
1083 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", | 1084 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", |
1084 | parse_output_fields), | 1085 | parse_output_fields), |
1085 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 1086 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
1086 | 1087 | OPT_BOOLEAN('I', "show-info", &show_full_info, | |
1088 | "display extended information from perf.data file"), | ||
1087 | OPT_END() | 1089 | OPT_END() |
1088 | }; | 1090 | }; |
1089 | 1091 | ||
@@ -1268,6 +1270,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1268 | return -1; | 1270 | return -1; |
1269 | } | 1271 | } |
1270 | 1272 | ||
1273 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
1274 | |||
1271 | if (!no_callchain) | 1275 | if (!no_callchain) |
1272 | symbol_conf.use_callchain = true; | 1276 | symbol_conf.use_callchain = true; |
1273 | else | 1277 | else |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 5deb17d9e795..7ce65f52415e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -194,6 +194,9 @@ static const char *cpu_list; | |||
194 | static const char *csv_sep = NULL; | 194 | static const char *csv_sep = NULL; |
195 | static bool csv_output = false; | 195 | static bool csv_output = false; |
196 | static bool group = false; | 196 | static bool group = false; |
197 | static const char *output_name = NULL; | ||
198 | static FILE *output = NULL; | ||
199 | static int output_fd; | ||
197 | 200 | ||
198 | static volatile int done = 0; | 201 | static volatile int done = 0; |
199 | 202 | ||
@@ -251,8 +254,13 @@ static double avg_stats(struct stats *stats) | |||
251 | */ | 254 | */ |
252 | static double stddev_stats(struct stats *stats) | 255 | static double stddev_stats(struct stats *stats) |
253 | { | 256 | { |
254 | double variance = stats->M2 / (stats->n - 1); | 257 | double variance, variance_mean; |
255 | double variance_mean = variance / stats->n; | 258 | |
259 | if (!stats->n) | ||
260 | return 0.0; | ||
261 | |||
262 | variance = stats->M2 / (stats->n - 1); | ||
263 | variance_mean = variance / stats->n; | ||
256 | 264 | ||
257 | return sqrt(variance_mean); | 265 | return sqrt(variance_mean); |
258 | } | 266 | } |
@@ -352,7 +360,7 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
352 | update_stats(&ps->res_stats[i], count[i]); | 360 | update_stats(&ps->res_stats[i], count[i]); |
353 | 361 | ||
354 | if (verbose) { | 362 | if (verbose) { |
355 | fprintf(stderr, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", | 363 | fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", |
356 | event_name(counter), count[0], count[1], count[2]); | 364 | event_name(counter), count[0], count[1], count[2]); |
357 | } | 365 | } |
358 | 366 | ||
@@ -487,6 +495,8 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
487 | if (forks) { | 495 | if (forks) { |
488 | close(go_pipe[1]); | 496 | close(go_pipe[1]); |
489 | wait(&status); | 497 | wait(&status); |
498 | if (WIFSIGNALED(status)) | ||
499 | psignal(WTERMSIG(status), argv[0]); | ||
490 | } else { | 500 | } else { |
491 | while(!done) sleep(1); | 501 | while(!done) sleep(1); |
492 | } | 502 | } |
@@ -519,9 +529,9 @@ static void print_noise_pct(double total, double avg) | |||
519 | pct = 100.0*total/avg; | 529 | pct = 100.0*total/avg; |
520 | 530 | ||
521 | if (csv_output) | 531 | if (csv_output) |
522 | fprintf(stderr, "%s%.2f%%", csv_sep, pct); | 532 | fprintf(output, "%s%.2f%%", csv_sep, pct); |
523 | else | 533 | else if (pct) |
524 | fprintf(stderr, " ( +-%6.2f%% )", pct); | 534 | fprintf(output, " ( +-%6.2f%% )", pct); |
525 | } | 535 | } |
526 | 536 | ||
527 | static void print_noise(struct perf_evsel *evsel, double avg) | 537 | static void print_noise(struct perf_evsel *evsel, double avg) |
@@ -546,16 +556,17 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
546 | csv_output ? 0 : -4, | 556 | csv_output ? 0 : -4, |
547 | evsel_list->cpus->map[cpu], csv_sep); | 557 | evsel_list->cpus->map[cpu], csv_sep); |
548 | 558 | ||
549 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); | 559 | fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel)); |
550 | 560 | ||
551 | if (evsel->cgrp) | 561 | if (evsel->cgrp) |
552 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | 562 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
553 | 563 | ||
554 | if (csv_output) | 564 | if (csv_output) |
555 | return; | 565 | return; |
556 | 566 | ||
557 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 567 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
558 | fprintf(stderr, " # %8.3f CPUs utilized ", avg / avg_stats(&walltime_nsecs_stats)); | 568 | fprintf(output, " # %8.3f CPUs utilized ", |
569 | avg / avg_stats(&walltime_nsecs_stats)); | ||
559 | } | 570 | } |
560 | 571 | ||
561 | static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __used, double avg) | 572 | static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -576,9 +587,9 @@ static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __us | |||
576 | else if (ratio > 10.0) | 587 | else if (ratio > 10.0) |
577 | color = PERF_COLOR_YELLOW; | 588 | color = PERF_COLOR_YELLOW; |
578 | 589 | ||
579 | fprintf(stderr, " # "); | 590 | fprintf(output, " # "); |
580 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 591 | color_fprintf(output, color, "%6.2f%%", ratio); |
581 | fprintf(stderr, " frontend cycles idle "); | 592 | fprintf(output, " frontend cycles idle "); |
582 | } | 593 | } |
583 | 594 | ||
584 | static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __used, double avg) | 595 | static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -599,9 +610,9 @@ static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __use | |||
599 | else if (ratio > 20.0) | 610 | else if (ratio > 20.0) |
600 | color = PERF_COLOR_YELLOW; | 611 | color = PERF_COLOR_YELLOW; |
601 | 612 | ||
602 | fprintf(stderr, " # "); | 613 | fprintf(output, " # "); |
603 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 614 | color_fprintf(output, color, "%6.2f%%", ratio); |
604 | fprintf(stderr, " backend cycles idle "); | 615 | fprintf(output, " backend cycles idle "); |
605 | } | 616 | } |
606 | 617 | ||
607 | static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 618 | static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -622,9 +633,9 @@ static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double | |||
622 | else if (ratio > 5.0) | 633 | else if (ratio > 5.0) |
623 | color = PERF_COLOR_YELLOW; | 634 | color = PERF_COLOR_YELLOW; |
624 | 635 | ||
625 | fprintf(stderr, " # "); | 636 | fprintf(output, " # "); |
626 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 637 | color_fprintf(output, color, "%6.2f%%", ratio); |
627 | fprintf(stderr, " of all branches "); | 638 | fprintf(output, " of all branches "); |
628 | } | 639 | } |
629 | 640 | ||
630 | static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 641 | static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -645,9 +656,9 @@ static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, dou | |||
645 | else if (ratio > 5.0) | 656 | else if (ratio > 5.0) |
646 | color = PERF_COLOR_YELLOW; | 657 | color = PERF_COLOR_YELLOW; |
647 | 658 | ||
648 | fprintf(stderr, " # "); | 659 | fprintf(output, " # "); |
649 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 660 | color_fprintf(output, color, "%6.2f%%", ratio); |
650 | fprintf(stderr, " of all L1-dcache hits "); | 661 | fprintf(output, " of all L1-dcache hits "); |
651 | } | 662 | } |
652 | 663 | ||
653 | static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 664 | static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -668,9 +679,9 @@ static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, dou | |||
668 | else if (ratio > 5.0) | 679 | else if (ratio > 5.0) |
669 | color = PERF_COLOR_YELLOW; | 680 | color = PERF_COLOR_YELLOW; |
670 | 681 | ||
671 | fprintf(stderr, " # "); | 682 | fprintf(output, " # "); |
672 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 683 | color_fprintf(output, color, "%6.2f%%", ratio); |
673 | fprintf(stderr, " of all L1-icache hits "); | 684 | fprintf(output, " of all L1-icache hits "); |
674 | } | 685 | } |
675 | 686 | ||
676 | static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 687 | static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -691,9 +702,9 @@ static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do | |||
691 | else if (ratio > 5.0) | 702 | else if (ratio > 5.0) |
692 | color = PERF_COLOR_YELLOW; | 703 | color = PERF_COLOR_YELLOW; |
693 | 704 | ||
694 | fprintf(stderr, " # "); | 705 | fprintf(output, " # "); |
695 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 706 | color_fprintf(output, color, "%6.2f%%", ratio); |
696 | fprintf(stderr, " of all dTLB cache hits "); | 707 | fprintf(output, " of all dTLB cache hits "); |
697 | } | 708 | } |
698 | 709 | ||
699 | static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 710 | static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -714,9 +725,9 @@ static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do | |||
714 | else if (ratio > 5.0) | 725 | else if (ratio > 5.0) |
715 | color = PERF_COLOR_YELLOW; | 726 | color = PERF_COLOR_YELLOW; |
716 | 727 | ||
717 | fprintf(stderr, " # "); | 728 | fprintf(output, " # "); |
718 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 729 | color_fprintf(output, color, "%6.2f%%", ratio); |
719 | fprintf(stderr, " of all iTLB cache hits "); | 730 | fprintf(output, " of all iTLB cache hits "); |
720 | } | 731 | } |
721 | 732 | ||
722 | static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) | 733 | static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg) |
@@ -737,9 +748,9 @@ static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, doub | |||
737 | else if (ratio > 5.0) | 748 | else if (ratio > 5.0) |
738 | color = PERF_COLOR_YELLOW; | 749 | color = PERF_COLOR_YELLOW; |
739 | 750 | ||
740 | fprintf(stderr, " # "); | 751 | fprintf(output, " # "); |
741 | color_fprintf(stderr, color, "%6.2f%%", ratio); | 752 | color_fprintf(output, color, "%6.2f%%", ratio); |
742 | fprintf(stderr, " of all LL-cache hits "); | 753 | fprintf(output, " of all LL-cache hits "); |
743 | } | 754 | } |
744 | 755 | ||
745 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | 756 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) |
@@ -762,10 +773,10 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
762 | else | 773 | else |
763 | cpu = 0; | 774 | cpu = 0; |
764 | 775 | ||
765 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel)); | 776 | fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel)); |
766 | 777 | ||
767 | if (evsel->cgrp) | 778 | if (evsel->cgrp) |
768 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | 779 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
769 | 780 | ||
770 | if (csv_output) | 781 | if (csv_output) |
771 | return; | 782 | return; |
@@ -776,14 +787,14 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
776 | if (total) | 787 | if (total) |
777 | ratio = avg / total; | 788 | ratio = avg / total; |
778 | 789 | ||
779 | fprintf(stderr, " # %5.2f insns per cycle ", ratio); | 790 | fprintf(output, " # %5.2f insns per cycle ", ratio); |
780 | 791 | ||
781 | total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); | 792 | total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); |
782 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); | 793 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); |
783 | 794 | ||
784 | if (total && avg) { | 795 | if (total && avg) { |
785 | ratio = total / avg; | 796 | ratio = total / avg; |
786 | fprintf(stderr, "\n # %5.2f stalled cycles per insn", ratio); | 797 | fprintf(output, "\n # %5.2f stalled cycles per insn", ratio); |
787 | } | 798 | } |
788 | 799 | ||
789 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && | 800 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && |
@@ -831,7 +842,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
831 | if (total) | 842 | if (total) |
832 | ratio = avg * 100 / total; | 843 | ratio = avg * 100 / total; |
833 | 844 | ||
834 | fprintf(stderr, " # %8.3f %% of all cache refs ", ratio); | 845 | fprintf(output, " # %8.3f %% of all cache refs ", ratio); |
835 | 846 | ||
836 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { | 847 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
837 | print_stalled_cycles_frontend(cpu, evsel, avg); | 848 | print_stalled_cycles_frontend(cpu, evsel, avg); |
@@ -843,16 +854,16 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
843 | if (total) | 854 | if (total) |
844 | ratio = 1.0 * avg / total; | 855 | ratio = 1.0 * avg / total; |
845 | 856 | ||
846 | fprintf(stderr, " # %8.3f GHz ", ratio); | 857 | fprintf(output, " # %8.3f GHz ", ratio); |
847 | } else if (runtime_nsecs_stats[cpu].n != 0) { | 858 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
848 | total = avg_stats(&runtime_nsecs_stats[cpu]); | 859 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
849 | 860 | ||
850 | if (total) | 861 | if (total) |
851 | ratio = 1000.0 * avg / total; | 862 | ratio = 1000.0 * avg / total; |
852 | 863 | ||
853 | fprintf(stderr, " # %8.3f M/sec ", ratio); | 864 | fprintf(output, " # %8.3f M/sec ", ratio); |
854 | } else { | 865 | } else { |
855 | fprintf(stderr, " "); | 866 | fprintf(output, " "); |
856 | } | 867 | } |
857 | } | 868 | } |
858 | 869 | ||
@@ -867,7 +878,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
867 | int scaled = counter->counts->scaled; | 878 | int scaled = counter->counts->scaled; |
868 | 879 | ||
869 | if (scaled == -1) { | 880 | if (scaled == -1) { |
870 | fprintf(stderr, "%*s%s%*s", | 881 | fprintf(output, "%*s%s%*s", |
871 | csv_output ? 0 : 18, | 882 | csv_output ? 0 : 18, |
872 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | 883 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
873 | csv_sep, | 884 | csv_sep, |
@@ -875,9 +886,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
875 | event_name(counter)); | 886 | event_name(counter)); |
876 | 887 | ||
877 | if (counter->cgrp) | 888 | if (counter->cgrp) |
878 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | 889 | fprintf(output, "%s%s", csv_sep, counter->cgrp->name); |
879 | 890 | ||
880 | fputc('\n', stderr); | 891 | fputc('\n', output); |
881 | return; | 892 | return; |
882 | } | 893 | } |
883 | 894 | ||
@@ -889,7 +900,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
889 | print_noise(counter, avg); | 900 | print_noise(counter, avg); |
890 | 901 | ||
891 | if (csv_output) { | 902 | if (csv_output) { |
892 | fputc('\n', stderr); | 903 | fputc('\n', output); |
893 | return; | 904 | return; |
894 | } | 905 | } |
895 | 906 | ||
@@ -899,9 +910,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
899 | avg_enabled = avg_stats(&ps->res_stats[1]); | 910 | avg_enabled = avg_stats(&ps->res_stats[1]); |
900 | avg_running = avg_stats(&ps->res_stats[2]); | 911 | avg_running = avg_stats(&ps->res_stats[2]); |
901 | 912 | ||
902 | fprintf(stderr, " [%5.2f%%]", 100 * avg_running / avg_enabled); | 913 | fprintf(output, " [%5.2f%%]", 100 * avg_running / avg_enabled); |
903 | } | 914 | } |
904 | fprintf(stderr, "\n"); | 915 | fprintf(output, "\n"); |
905 | } | 916 | } |
906 | 917 | ||
907 | /* | 918 | /* |
@@ -918,7 +929,7 @@ static void print_counter(struct perf_evsel *counter) | |||
918 | ena = counter->counts->cpu[cpu].ena; | 929 | ena = counter->counts->cpu[cpu].ena; |
919 | run = counter->counts->cpu[cpu].run; | 930 | run = counter->counts->cpu[cpu].run; |
920 | if (run == 0 || ena == 0) { | 931 | if (run == 0 || ena == 0) { |
921 | fprintf(stderr, "CPU%*d%s%*s%s%*s", | 932 | fprintf(output, "CPU%*d%s%*s%s%*s", |
922 | csv_output ? 0 : -4, | 933 | csv_output ? 0 : -4, |
923 | evsel_list->cpus->map[cpu], csv_sep, | 934 | evsel_list->cpus->map[cpu], csv_sep, |
924 | csv_output ? 0 : 18, | 935 | csv_output ? 0 : 18, |
@@ -928,9 +939,10 @@ static void print_counter(struct perf_evsel *counter) | |||
928 | event_name(counter)); | 939 | event_name(counter)); |
929 | 940 | ||
930 | if (counter->cgrp) | 941 | if (counter->cgrp) |
931 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | 942 | fprintf(output, "%s%s", |
943 | csv_sep, counter->cgrp->name); | ||
932 | 944 | ||
933 | fputc('\n', stderr); | 945 | fputc('\n', output); |
934 | continue; | 946 | continue; |
935 | } | 947 | } |
936 | 948 | ||
@@ -943,9 +955,10 @@ static void print_counter(struct perf_evsel *counter) | |||
943 | print_noise(counter, 1.0); | 955 | print_noise(counter, 1.0); |
944 | 956 | ||
945 | if (run != ena) | 957 | if (run != ena) |
946 | fprintf(stderr, " (%.2f%%)", 100.0 * run / ena); | 958 | fprintf(output, " (%.2f%%)", |
959 | 100.0 * run / ena); | ||
947 | } | 960 | } |
948 | fputc('\n', stderr); | 961 | fputc('\n', output); |
949 | } | 962 | } |
950 | } | 963 | } |
951 | 964 | ||
@@ -957,21 +970,21 @@ static void print_stat(int argc, const char **argv) | |||
957 | fflush(stdout); | 970 | fflush(stdout); |
958 | 971 | ||
959 | if (!csv_output) { | 972 | if (!csv_output) { |
960 | fprintf(stderr, "\n"); | 973 | fprintf(output, "\n"); |
961 | fprintf(stderr, " Performance counter stats for "); | 974 | fprintf(output, " Performance counter stats for "); |
962 | if(target_pid == -1 && target_tid == -1) { | 975 | if(target_pid == -1 && target_tid == -1) { |
963 | fprintf(stderr, "\'%s", argv[0]); | 976 | fprintf(output, "\'%s", argv[0]); |
964 | for (i = 1; i < argc; i++) | 977 | for (i = 1; i < argc; i++) |
965 | fprintf(stderr, " %s", argv[i]); | 978 | fprintf(output, " %s", argv[i]); |
966 | } else if (target_pid != -1) | 979 | } else if (target_pid != -1) |
967 | fprintf(stderr, "process id \'%d", target_pid); | 980 | fprintf(output, "process id \'%d", target_pid); |
968 | else | 981 | else |
969 | fprintf(stderr, "thread id \'%d", target_tid); | 982 | fprintf(output, "thread id \'%d", target_tid); |
970 | 983 | ||
971 | fprintf(stderr, "\'"); | 984 | fprintf(output, "\'"); |
972 | if (run_count > 1) | 985 | if (run_count > 1) |
973 | fprintf(stderr, " (%d runs)", run_count); | 986 | fprintf(output, " (%d runs)", run_count); |
974 | fprintf(stderr, ":\n\n"); | 987 | fprintf(output, ":\n\n"); |
975 | } | 988 | } |
976 | 989 | ||
977 | if (no_aggr) { | 990 | if (no_aggr) { |
@@ -984,15 +997,15 @@ static void print_stat(int argc, const char **argv) | |||
984 | 997 | ||
985 | if (!csv_output) { | 998 | if (!csv_output) { |
986 | if (!null_run) | 999 | if (!null_run) |
987 | fprintf(stderr, "\n"); | 1000 | fprintf(output, "\n"); |
988 | fprintf(stderr, " %17.9f seconds time elapsed", | 1001 | fprintf(output, " %17.9f seconds time elapsed", |
989 | avg_stats(&walltime_nsecs_stats)/1e9); | 1002 | avg_stats(&walltime_nsecs_stats)/1e9); |
990 | if (run_count > 1) { | 1003 | if (run_count > 1) { |
991 | fprintf(stderr, " "); | 1004 | fprintf(output, " "); |
992 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), | 1005 | print_noise_pct(stddev_stats(&walltime_nsecs_stats), |
993 | avg_stats(&walltime_nsecs_stats)); | 1006 | avg_stats(&walltime_nsecs_stats)); |
994 | } | 1007 | } |
995 | fprintf(stderr, "\n\n"); | 1008 | fprintf(output, "\n\n"); |
996 | } | 1009 | } |
997 | } | 1010 | } |
998 | 1011 | ||
@@ -1030,6 +1043,8 @@ static int stat__set_big_num(const struct option *opt __used, | |||
1030 | return 0; | 1043 | return 0; |
1031 | } | 1044 | } |
1032 | 1045 | ||
1046 | static bool append_file; | ||
1047 | |||
1033 | static const struct option options[] = { | 1048 | static const struct option options[] = { |
1034 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 1049 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
1035 | "event selector. use 'perf list' to list available events", | 1050 | "event selector. use 'perf list' to list available events", |
@@ -1070,6 +1085,11 @@ static const struct option options[] = { | |||
1070 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 1085 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
1071 | "monitor event in cgroup name only", | 1086 | "monitor event in cgroup name only", |
1072 | parse_cgroups), | 1087 | parse_cgroups), |
1088 | OPT_STRING('o', "output", &output_name, "file", | ||
1089 | "output file name"), | ||
1090 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | ||
1091 | OPT_INTEGER(0, "log-fd", &output_fd, | ||
1092 | "log output to fd, instead of stderr"), | ||
1073 | OPT_END() | 1093 | OPT_END() |
1074 | }; | 1094 | }; |
1075 | 1095 | ||
@@ -1141,6 +1161,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
1141 | { | 1161 | { |
1142 | struct perf_evsel *pos; | 1162 | struct perf_evsel *pos; |
1143 | int status = -ENOMEM; | 1163 | int status = -ENOMEM; |
1164 | const char *mode; | ||
1144 | 1165 | ||
1145 | setlocale(LC_ALL, ""); | 1166 | setlocale(LC_ALL, ""); |
1146 | 1167 | ||
@@ -1151,16 +1172,46 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
1151 | argc = parse_options(argc, argv, options, stat_usage, | 1172 | argc = parse_options(argc, argv, options, stat_usage, |
1152 | PARSE_OPT_STOP_AT_NON_OPTION); | 1173 | PARSE_OPT_STOP_AT_NON_OPTION); |
1153 | 1174 | ||
1154 | if (csv_sep) | 1175 | output = stderr; |
1176 | if (output_name && strcmp(output_name, "-")) | ||
1177 | output = NULL; | ||
1178 | |||
1179 | if (output_name && output_fd) { | ||
1180 | fprintf(stderr, "cannot use both --output and --log-fd\n"); | ||
1181 | usage_with_options(stat_usage, options); | ||
1182 | } | ||
1183 | if (!output) { | ||
1184 | struct timespec tm; | ||
1185 | mode = append_file ? "a" : "w"; | ||
1186 | |||
1187 | output = fopen(output_name, mode); | ||
1188 | if (!output) { | ||
1189 | perror("failed to create output file"); | ||
1190 | exit(-1); | ||
1191 | } | ||
1192 | clock_gettime(CLOCK_REALTIME, &tm); | ||
1193 | fprintf(output, "# started on %s\n", ctime(&tm.tv_sec)); | ||
1194 | } else if (output_fd != 2) { | ||
1195 | mode = append_file ? "a" : "w"; | ||
1196 | output = fdopen(output_fd, mode); | ||
1197 | if (!output) { | ||
1198 | perror("Failed opening logfd"); | ||
1199 | return -errno; | ||
1200 | } | ||
1201 | } | ||
1202 | |||
1203 | if (csv_sep) { | ||
1155 | csv_output = true; | 1204 | csv_output = true; |
1156 | else | 1205 | if (!strcmp(csv_sep, "\\t")) |
1206 | csv_sep = "\t"; | ||
1207 | } else | ||
1157 | csv_sep = DEFAULT_SEPARATOR; | 1208 | csv_sep = DEFAULT_SEPARATOR; |
1158 | 1209 | ||
1159 | /* | 1210 | /* |
1160 | * let the spreadsheet do the pretty-printing | 1211 | * let the spreadsheet do the pretty-printing |
1161 | */ | 1212 | */ |
1162 | if (csv_output) { | 1213 | if (csv_output) { |
1163 | /* User explicitely passed -B? */ | 1214 | /* User explicitly passed -B? */ |
1164 | if (big_num_opt == 1) { | 1215 | if (big_num_opt == 1) { |
1165 | fprintf(stderr, "-B option not supported with -x\n"); | 1216 | fprintf(stderr, "-B option not supported with -x\n"); |
1166 | usage_with_options(stat_usage, options); | 1217 | usage_with_options(stat_usage, options); |
@@ -1226,7 +1277,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
1226 | status = 0; | 1277 | status = 0; |
1227 | for (run_idx = 0; run_idx < run_count; run_idx++) { | 1278 | for (run_idx = 0; run_idx < run_count; run_idx++) { |
1228 | if (run_count != 1 && verbose) | 1279 | if (run_count != 1 && verbose) |
1229 | fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx + 1); | 1280 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
1281 | run_idx + 1); | ||
1230 | 1282 | ||
1231 | if (sync_run) | 1283 | if (sync_run) |
1232 | sync(); | 1284 | sync(); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d28013b7d61c..7a871714d44e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * any workload, CPU or specific PID. | 5 | * any workload, CPU or specific PID. |
6 | * | 6 | * |
7 | * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com> | 7 | * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com> |
8 | * 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | * | 9 | * |
9 | * Improvements and fixes by: | 10 | * Improvements and fixes by: |
10 | * | 11 | * |
@@ -36,6 +37,7 @@ | |||
36 | #include "util/parse-events.h" | 37 | #include "util/parse-events.h" |
37 | #include "util/cpumap.h" | 38 | #include "util/cpumap.h" |
38 | #include "util/xyarray.h" | 39 | #include "util/xyarray.h" |
40 | #include "util/sort.h" | ||
39 | 41 | ||
40 | #include "util/debug.h" | 42 | #include "util/debug.h" |
41 | 43 | ||
@@ -65,12 +67,8 @@ | |||
65 | static struct perf_top top = { | 67 | static struct perf_top top = { |
66 | .count_filter = 5, | 68 | .count_filter = 5, |
67 | .delay_secs = 2, | 69 | .delay_secs = 2, |
68 | .display_weighted = -1, | ||
69 | .target_pid = -1, | 70 | .target_pid = -1, |
70 | .target_tid = -1, | 71 | .target_tid = -1, |
71 | .active_symbols = LIST_HEAD_INIT(top.active_symbols), | ||
72 | .active_symbols_lock = PTHREAD_MUTEX_INITIALIZER, | ||
73 | .active_symbols_cond = PTHREAD_COND_INITIALIZER, | ||
74 | .freq = 1000, /* 1 KHz */ | 72 | .freq = 1000, /* 1 KHz */ |
75 | }; | 73 | }; |
76 | 74 | ||
@@ -78,6 +76,12 @@ static bool system_wide = false; | |||
78 | 76 | ||
79 | static bool use_tui, use_stdio; | 77 | static bool use_tui, use_stdio; |
80 | 78 | ||
79 | static bool sort_has_symbols; | ||
80 | |||
81 | static bool dont_use_callchains; | ||
82 | static char callchain_default_opt[] = "fractal,0.5,callee"; | ||
83 | |||
84 | |||
81 | static int default_interval = 0; | 85 | static int default_interval = 0; |
82 | 86 | ||
83 | static bool kptr_restrict_warned; | 87 | static bool kptr_restrict_warned; |
@@ -85,7 +89,6 @@ static bool vmlinux_warned; | |||
85 | static bool inherit = false; | 89 | static bool inherit = false; |
86 | static int realtime_prio = 0; | 90 | static int realtime_prio = 0; |
87 | static bool group = false; | 91 | static bool group = false; |
88 | static unsigned int page_size; | ||
89 | static unsigned int mmap_pages = 128; | 92 | static unsigned int mmap_pages = 128; |
90 | 93 | ||
91 | static bool dump_symtab = false; | 94 | static bool dump_symtab = false; |
@@ -93,7 +96,6 @@ static bool dump_symtab = false; | |||
93 | static struct winsize winsize; | 96 | static struct winsize winsize; |
94 | 97 | ||
95 | static const char *sym_filter = NULL; | 98 | static const char *sym_filter = NULL; |
96 | struct sym_entry *sym_filter_entry_sched = NULL; | ||
97 | static int sym_pcnt_filter = 5; | 99 | static int sym_pcnt_filter = 5; |
98 | 100 | ||
99 | /* | 101 | /* |
@@ -136,18 +138,18 @@ static void sig_winch_handler(int sig __used) | |||
136 | update_print_entries(&winsize); | 138 | update_print_entries(&winsize); |
137 | } | 139 | } |
138 | 140 | ||
139 | static int parse_source(struct sym_entry *syme) | 141 | static int parse_source(struct hist_entry *he) |
140 | { | 142 | { |
141 | struct symbol *sym; | 143 | struct symbol *sym; |
142 | struct annotation *notes; | 144 | struct annotation *notes; |
143 | struct map *map; | 145 | struct map *map; |
144 | int err = -1; | 146 | int err = -1; |
145 | 147 | ||
146 | if (!syme) | 148 | if (!he || !he->ms.sym) |
147 | return -1; | 149 | return -1; |
148 | 150 | ||
149 | sym = sym_entry__symbol(syme); | 151 | sym = he->ms.sym; |
150 | map = syme->map; | 152 | map = he->ms.map; |
151 | 153 | ||
152 | /* | 154 | /* |
153 | * We can't annotate with just /proc/kallsyms | 155 | * We can't annotate with just /proc/kallsyms |
@@ -175,53 +177,62 @@ static int parse_source(struct sym_entry *syme) | |||
175 | return err; | 177 | return err; |
176 | } | 178 | } |
177 | 179 | ||
178 | err = symbol__annotate(sym, syme->map, 0); | 180 | err = symbol__annotate(sym, map, 0); |
179 | if (err == 0) { | 181 | if (err == 0) { |
180 | out_assign: | 182 | out_assign: |
181 | top.sym_filter_entry = syme; | 183 | top.sym_filter_entry = he; |
182 | } | 184 | } |
183 | 185 | ||
184 | pthread_mutex_unlock(¬es->lock); | 186 | pthread_mutex_unlock(¬es->lock); |
185 | return err; | 187 | return err; |
186 | } | 188 | } |
187 | 189 | ||
188 | static void __zero_source_counters(struct sym_entry *syme) | 190 | static void __zero_source_counters(struct hist_entry *he) |
189 | { | 191 | { |
190 | struct symbol *sym = sym_entry__symbol(syme); | 192 | struct symbol *sym = he->ms.sym; |
191 | symbol__annotate_zero_histograms(sym); | 193 | symbol__annotate_zero_histograms(sym); |
192 | } | 194 | } |
193 | 195 | ||
194 | static void record_precise_ip(struct sym_entry *syme, struct map *map, | 196 | static void record_precise_ip(struct hist_entry *he, int counter, u64 ip) |
195 | int counter, u64 ip) | ||
196 | { | 197 | { |
197 | struct annotation *notes; | 198 | struct annotation *notes; |
198 | struct symbol *sym; | 199 | struct symbol *sym; |
199 | 200 | ||
200 | if (syme != top.sym_filter_entry) | 201 | if (he == NULL || he->ms.sym == NULL || |
202 | (he != top.sym_filter_entry && use_browser != 1)) | ||
201 | return; | 203 | return; |
202 | 204 | ||
203 | sym = sym_entry__symbol(syme); | 205 | sym = he->ms.sym; |
204 | notes = symbol__annotation(sym); | 206 | notes = symbol__annotation(sym); |
205 | 207 | ||
206 | if (pthread_mutex_trylock(¬es->lock)) | 208 | if (pthread_mutex_trylock(¬es->lock)) |
207 | return; | 209 | return; |
208 | 210 | ||
209 | ip = map->map_ip(map, ip); | 211 | if (notes->src == NULL && |
210 | symbol__inc_addr_samples(sym, map, counter, ip); | 212 | symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { |
213 | pthread_mutex_unlock(¬es->lock); | ||
214 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
215 | sym->name); | ||
216 | sleep(1); | ||
217 | return; | ||
218 | } | ||
219 | |||
220 | ip = he->ms.map->map_ip(he->ms.map, ip); | ||
221 | symbol__inc_addr_samples(sym, he->ms.map, counter, ip); | ||
211 | 222 | ||
212 | pthread_mutex_unlock(¬es->lock); | 223 | pthread_mutex_unlock(¬es->lock); |
213 | } | 224 | } |
214 | 225 | ||
215 | static void show_details(struct sym_entry *syme) | 226 | static void show_details(struct hist_entry *he) |
216 | { | 227 | { |
217 | struct annotation *notes; | 228 | struct annotation *notes; |
218 | struct symbol *symbol; | 229 | struct symbol *symbol; |
219 | int more; | 230 | int more; |
220 | 231 | ||
221 | if (!syme) | 232 | if (!he) |
222 | return; | 233 | return; |
223 | 234 | ||
224 | symbol = sym_entry__symbol(syme); | 235 | symbol = he->ms.sym; |
225 | notes = symbol__annotation(symbol); | 236 | notes = symbol__annotation(symbol); |
226 | 237 | ||
227 | pthread_mutex_lock(¬es->lock); | 238 | pthread_mutex_lock(¬es->lock); |
@@ -232,7 +243,7 @@ static void show_details(struct sym_entry *syme) | |||
232 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); | 243 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
233 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 244 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
234 | 245 | ||
235 | more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx, | 246 | more = symbol__annotate_printf(symbol, he->ms.map, top.sym_evsel->idx, |
236 | 0, sym_pcnt_filter, top.print_entries, 4); | 247 | 0, sym_pcnt_filter, top.print_entries, 4); |
237 | if (top.zero) | 248 | if (top.zero) |
238 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | 249 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); |
@@ -246,21 +257,28 @@ out_unlock: | |||
246 | 257 | ||
247 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 258 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
248 | 259 | ||
249 | static void __list_insert_active_sym(struct sym_entry *syme) | 260 | static struct hist_entry * |
261 | perf_session__add_hist_entry(struct perf_session *session, | ||
262 | struct addr_location *al, | ||
263 | struct perf_sample *sample, | ||
264 | struct perf_evsel *evsel) | ||
250 | { | 265 | { |
251 | list_add(&syme->node, &top.active_symbols); | 266 | struct hist_entry *he; |
267 | |||
268 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); | ||
269 | if (he == NULL) | ||
270 | return NULL; | ||
271 | |||
272 | session->hists.stats.total_period += sample->period; | ||
273 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
274 | return he; | ||
252 | } | 275 | } |
253 | 276 | ||
254 | static void print_sym_table(struct perf_session *session) | 277 | static void print_sym_table(void) |
255 | { | 278 | { |
256 | char bf[160]; | 279 | char bf[160]; |
257 | int printed = 0; | 280 | int printed = 0; |
258 | struct rb_node *nd; | ||
259 | struct sym_entry *syme; | ||
260 | struct rb_root tmp = RB_ROOT; | ||
261 | const int win_width = winsize.ws_col - 1; | 281 | const int win_width = winsize.ws_col - 1; |
262 | int sym_width, dso_width, dso_short_width; | ||
263 | float sum_ksamples = perf_top__decay_samples(&top, &tmp); | ||
264 | 282 | ||
265 | puts(CONSOLE_CLEAR); | 283 | puts(CONSOLE_CLEAR); |
266 | 284 | ||
@@ -271,10 +289,12 @@ static void print_sym_table(struct perf_session *session) | |||
271 | 289 | ||
272 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 290 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
273 | 291 | ||
274 | if (session->hists.stats.total_lost != 0) { | 292 | if (top.total_lost_warned != top.session->hists.stats.total_lost) { |
293 | top.total_lost_warned = top.session->hists.stats.total_lost; | ||
275 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); | 294 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); |
276 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", | 295 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", |
277 | session->hists.stats.total_lost); | 296 | top.total_lost_warned); |
297 | ++printed; | ||
278 | } | 298 | } |
279 | 299 | ||
280 | if (top.sym_filter_entry) { | 300 | if (top.sym_filter_entry) { |
@@ -282,58 +302,15 @@ static void print_sym_table(struct perf_session *session) | |||
282 | return; | 302 | return; |
283 | } | 303 | } |
284 | 304 | ||
285 | perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, | 305 | hists__collapse_resort_threaded(&top.sym_evsel->hists); |
286 | &sym_width); | 306 | hists__output_resort_threaded(&top.sym_evsel->hists); |
287 | 307 | hists__decay_entries_threaded(&top.sym_evsel->hists, | |
288 | if (sym_width + dso_width > winsize.ws_col - 29) { | 308 | top.hide_user_symbols, |
289 | dso_width = dso_short_width; | 309 | top.hide_kernel_symbols); |
290 | if (sym_width + dso_width > winsize.ws_col - 29) | 310 | hists__output_recalc_col_len(&top.sym_evsel->hists, winsize.ws_row - 3); |
291 | sym_width = winsize.ws_col - dso_width - 29; | ||
292 | } | ||
293 | putchar('\n'); | 311 | putchar('\n'); |
294 | if (top.evlist->nr_entries == 1) | 312 | hists__fprintf(&top.sym_evsel->hists, NULL, false, false, |
295 | printf(" samples pcnt"); | 313 | winsize.ws_row - 4 - printed, win_width, stdout); |
296 | else | ||
297 | printf(" weight samples pcnt"); | ||
298 | |||
299 | if (verbose) | ||
300 | printf(" RIP "); | ||
301 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | ||
302 | printf(" %s _______ _____", | ||
303 | top.evlist->nr_entries == 1 ? " " : "______"); | ||
304 | if (verbose) | ||
305 | printf(" ________________"); | ||
306 | printf(" %-*.*s", sym_width, sym_width, graph_line); | ||
307 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
308 | puts("\n"); | ||
309 | |||
310 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
311 | struct symbol *sym; | ||
312 | double pcnt; | ||
313 | |||
314 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
315 | sym = sym_entry__symbol(syme); | ||
316 | if (++printed > top.print_entries || | ||
317 | (int)syme->snap_count < top.count_filter) | ||
318 | continue; | ||
319 | |||
320 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | ||
321 | sum_ksamples)); | ||
322 | |||
323 | if (top.evlist->nr_entries == 1 || !top.display_weighted) | ||
324 | printf("%20.2f ", syme->weight); | ||
325 | else | ||
326 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
327 | |||
328 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | ||
329 | if (verbose) | ||
330 | printf(" %016" PRIx64, sym->start); | ||
331 | printf(" %-*.*s", sym_width, sym_width, sym->name); | ||
332 | printf(" %-*.*s\n", dso_width, dso_width, | ||
333 | dso_width >= syme->map->dso->long_name_len ? | ||
334 | syme->map->dso->long_name : | ||
335 | syme->map->dso->short_name); | ||
336 | } | ||
337 | } | 314 | } |
338 | 315 | ||
339 | static void prompt_integer(int *target, const char *msg) | 316 | static void prompt_integer(int *target, const char *msg) |
@@ -371,10 +348,11 @@ static void prompt_percent(int *target, const char *msg) | |||
371 | *target = tmp; | 348 | *target = tmp; |
372 | } | 349 | } |
373 | 350 | ||
374 | static void prompt_symbol(struct sym_entry **target, const char *msg) | 351 | static void prompt_symbol(struct hist_entry **target, const char *msg) |
375 | { | 352 | { |
376 | char *buf = malloc(0), *p; | 353 | char *buf = malloc(0), *p; |
377 | struct sym_entry *syme = *target, *n, *found = NULL; | 354 | struct hist_entry *syme = *target, *n, *found = NULL; |
355 | struct rb_node *next; | ||
378 | size_t dummy = 0; | 356 | size_t dummy = 0; |
379 | 357 | ||
380 | /* zero counters of active symbol */ | 358 | /* zero counters of active symbol */ |
@@ -391,17 +369,14 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
391 | if (p) | 369 | if (p) |
392 | *p = 0; | 370 | *p = 0; |
393 | 371 | ||
394 | pthread_mutex_lock(&top.active_symbols_lock); | 372 | next = rb_first(&top.sym_evsel->hists.entries); |
395 | syme = list_entry(top.active_symbols.next, struct sym_entry, node); | 373 | while (next) { |
396 | pthread_mutex_unlock(&top.active_symbols_lock); | 374 | n = rb_entry(next, struct hist_entry, rb_node); |
397 | 375 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | |
398 | list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { | 376 | found = n; |
399 | struct symbol *sym = sym_entry__symbol(syme); | ||
400 | |||
401 | if (!strcmp(buf, sym->name)) { | ||
402 | found = syme; | ||
403 | break; | 377 | break; |
404 | } | 378 | } |
379 | next = rb_next(&n->rb_node); | ||
405 | } | 380 | } |
406 | 381 | ||
407 | if (!found) { | 382 | if (!found) { |
@@ -420,7 +395,7 @@ static void print_mapped_keys(void) | |||
420 | char *name = NULL; | 395 | char *name = NULL; |
421 | 396 | ||
422 | if (top.sym_filter_entry) { | 397 | if (top.sym_filter_entry) { |
423 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | 398 | struct symbol *sym = top.sym_filter_entry->ms.sym; |
424 | name = sym->name; | 399 | name = sym->name; |
425 | } | 400 | } |
426 | 401 | ||
@@ -437,9 +412,6 @@ static void print_mapped_keys(void) | |||
437 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 412 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
438 | fprintf(stdout, "\t[S] stop annotation.\n"); | 413 | fprintf(stdout, "\t[S] stop annotation.\n"); |
439 | 414 | ||
440 | if (top.evlist->nr_entries > 1) | ||
441 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); | ||
442 | |||
443 | fprintf(stdout, | 415 | fprintf(stdout, |
444 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 416 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
445 | top.hide_kernel_symbols ? "yes" : "no"); | 417 | top.hide_kernel_symbols ? "yes" : "no"); |
@@ -466,7 +438,6 @@ static int key_mapped(int c) | |||
466 | case 'S': | 438 | case 'S': |
467 | return 1; | 439 | return 1; |
468 | case 'E': | 440 | case 'E': |
469 | case 'w': | ||
470 | return top.evlist->nr_entries > 1 ? 1 : 0; | 441 | return top.evlist->nr_entries > 1 ? 1 : 0; |
471 | default: | 442 | default: |
472 | break; | 443 | break; |
@@ -475,7 +446,7 @@ static int key_mapped(int c) | |||
475 | return 0; | 446 | return 0; |
476 | } | 447 | } |
477 | 448 | ||
478 | static void handle_keypress(struct perf_session *session, int c) | 449 | static void handle_keypress(int c) |
479 | { | 450 | { |
480 | if (!key_mapped(c)) { | 451 | if (!key_mapped(c)) { |
481 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 452 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
@@ -551,7 +522,7 @@ static void handle_keypress(struct perf_session *session, int c) | |||
551 | case 'Q': | 522 | case 'Q': |
552 | printf("exiting.\n"); | 523 | printf("exiting.\n"); |
553 | if (dump_symtab) | 524 | if (dump_symtab) |
554 | perf_session__fprintf_dsos(session, stderr); | 525 | perf_session__fprintf_dsos(top.session, stderr); |
555 | exit(0); | 526 | exit(0); |
556 | case 's': | 527 | case 's': |
557 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); | 528 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); |
@@ -560,7 +531,7 @@ static void handle_keypress(struct perf_session *session, int c) | |||
560 | if (!top.sym_filter_entry) | 531 | if (!top.sym_filter_entry) |
561 | break; | 532 | break; |
562 | else { | 533 | else { |
563 | struct sym_entry *syme = top.sym_filter_entry; | 534 | struct hist_entry *syme = top.sym_filter_entry; |
564 | 535 | ||
565 | top.sym_filter_entry = NULL; | 536 | top.sym_filter_entry = NULL; |
566 | __zero_source_counters(syme); | 537 | __zero_source_counters(syme); |
@@ -569,9 +540,6 @@ static void handle_keypress(struct perf_session *session, int c) | |||
569 | case 'U': | 540 | case 'U': |
570 | top.hide_user_symbols = !top.hide_user_symbols; | 541 | top.hide_user_symbols = !top.hide_user_symbols; |
571 | break; | 542 | break; |
572 | case 'w': | ||
573 | top.display_weighted = ~top.display_weighted; | ||
574 | break; | ||
575 | case 'z': | 543 | case 'z': |
576 | top.zero = !top.zero; | 544 | top.zero = !top.zero; |
577 | break; | 545 | break; |
@@ -580,19 +548,31 @@ static void handle_keypress(struct perf_session *session, int c) | |||
580 | } | 548 | } |
581 | } | 549 | } |
582 | 550 | ||
551 | static void perf_top__sort_new_samples(void *arg) | ||
552 | { | ||
553 | struct perf_top *t = arg; | ||
554 | perf_top__reset_sample_counters(t); | ||
555 | |||
556 | if (t->evlist->selected != NULL) | ||
557 | t->sym_evsel = t->evlist->selected; | ||
558 | |||
559 | hists__collapse_resort_threaded(&t->sym_evsel->hists); | ||
560 | hists__output_resort_threaded(&t->sym_evsel->hists); | ||
561 | hists__decay_entries_threaded(&t->sym_evsel->hists, | ||
562 | top.hide_user_symbols, | ||
563 | top.hide_kernel_symbols); | ||
564 | hists__output_recalc_col_len(&t->sym_evsel->hists, winsize.ws_row - 3); | ||
565 | } | ||
566 | |||
583 | static void *display_thread_tui(void *arg __used) | 567 | static void *display_thread_tui(void *arg __used) |
584 | { | 568 | { |
585 | int err = 0; | 569 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
586 | pthread_mutex_lock(&top.active_symbols_lock); | 570 | |
587 | while (list_empty(&top.active_symbols)) { | 571 | perf_top__sort_new_samples(&top); |
588 | err = pthread_cond_wait(&top.active_symbols_cond, | 572 | perf_evlist__tui_browse_hists(top.evlist, help, |
589 | &top.active_symbols_lock); | 573 | perf_top__sort_new_samples, |
590 | if (err) | 574 | &top, top.delay_secs); |
591 | break; | 575 | |
592 | } | ||
593 | pthread_mutex_unlock(&top.active_symbols_lock); | ||
594 | if (!err) | ||
595 | perf_top__tui_browser(&top); | ||
596 | exit_browser(0); | 576 | exit_browser(0); |
597 | exit(0); | 577 | exit(0); |
598 | return NULL; | 578 | return NULL; |
@@ -603,7 +583,6 @@ static void *display_thread(void *arg __used) | |||
603 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 583 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
604 | struct termios tc, save; | 584 | struct termios tc, save; |
605 | int delay_msecs, c; | 585 | int delay_msecs, c; |
606 | struct perf_session *session = (struct perf_session *) arg; | ||
607 | 586 | ||
608 | tcgetattr(0, &save); | 587 | tcgetattr(0, &save); |
609 | tc = save; | 588 | tc = save; |
@@ -611,20 +590,35 @@ static void *display_thread(void *arg __used) | |||
611 | tc.c_cc[VMIN] = 0; | 590 | tc.c_cc[VMIN] = 0; |
612 | tc.c_cc[VTIME] = 0; | 591 | tc.c_cc[VTIME] = 0; |
613 | 592 | ||
593 | pthread__unblock_sigwinch(); | ||
614 | repeat: | 594 | repeat: |
615 | delay_msecs = top.delay_secs * 1000; | 595 | delay_msecs = top.delay_secs * 1000; |
616 | tcsetattr(0, TCSANOW, &tc); | 596 | tcsetattr(0, TCSANOW, &tc); |
617 | /* trash return*/ | 597 | /* trash return*/ |
618 | getc(stdin); | 598 | getc(stdin); |
619 | 599 | ||
620 | do { | 600 | while (1) { |
621 | print_sym_table(session); | 601 | print_sym_table(); |
622 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); | 602 | /* |
623 | 603 | * Either timeout expired or we got an EINTR due to SIGWINCH, | |
604 | * refresh screen in both cases. | ||
605 | */ | ||
606 | switch (poll(&stdin_poll, 1, delay_msecs)) { | ||
607 | case 0: | ||
608 | continue; | ||
609 | case -1: | ||
610 | if (errno == EINTR) | ||
611 | continue; | ||
612 | /* Fall trhu */ | ||
613 | default: | ||
614 | goto process_hotkey; | ||
615 | } | ||
616 | } | ||
617 | process_hotkey: | ||
624 | c = getc(stdin); | 618 | c = getc(stdin); |
625 | tcsetattr(0, TCSAFLUSH, &save); | 619 | tcsetattr(0, TCSAFLUSH, &save); |
626 | 620 | ||
627 | handle_keypress(session, c); | 621 | handle_keypress(c); |
628 | goto repeat; | 622 | goto repeat; |
629 | 623 | ||
630 | return NULL; | 624 | return NULL; |
@@ -645,9 +639,8 @@ static const char *skip_symbols[] = { | |||
645 | NULL | 639 | NULL |
646 | }; | 640 | }; |
647 | 641 | ||
648 | static int symbol_filter(struct map *map, struct symbol *sym) | 642 | static int symbol_filter(struct map *map __used, struct symbol *sym) |
649 | { | 643 | { |
650 | struct sym_entry *syme; | ||
651 | const char *name = sym->name; | 644 | const char *name = sym->name; |
652 | int i; | 645 | int i; |
653 | 646 | ||
@@ -667,16 +660,6 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
667 | strstr(name, "_text_end")) | 660 | strstr(name, "_text_end")) |
668 | return 1; | 661 | return 1; |
669 | 662 | ||
670 | syme = symbol__priv(sym); | ||
671 | syme->map = map; | ||
672 | symbol__annotate_init(map, sym); | ||
673 | |||
674 | if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { | ||
675 | /* schedule initial sym_filter_entry setup */ | ||
676 | sym_filter_entry_sched = syme; | ||
677 | sym_filter = NULL; | ||
678 | } | ||
679 | |||
680 | for (i = 0; skip_symbols[i]; i++) { | 663 | for (i = 0; skip_symbols[i]; i++) { |
681 | if (!strcmp(skip_symbols[i], name)) { | 664 | if (!strcmp(skip_symbols[i], name)) { |
682 | sym->ignore = true; | 665 | sym->ignore = true; |
@@ -691,10 +674,11 @@ static void perf_event__process_sample(const union perf_event *event, | |||
691 | struct perf_sample *sample, | 674 | struct perf_sample *sample, |
692 | struct perf_session *session) | 675 | struct perf_session *session) |
693 | { | 676 | { |
677 | struct symbol *parent = NULL; | ||
694 | u64 ip = event->ip.ip; | 678 | u64 ip = event->ip.ip; |
695 | struct sym_entry *syme; | ||
696 | struct addr_location al; | 679 | struct addr_location al; |
697 | struct machine *machine; | 680 | struct machine *machine; |
681 | int err; | ||
698 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 682 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
699 | 683 | ||
700 | ++top.samples; | 684 | ++top.samples; |
@@ -783,46 +767,41 @@ static void perf_event__process_sample(const union perf_event *event, | |||
783 | sleep(5); | 767 | sleep(5); |
784 | vmlinux_warned = true; | 768 | vmlinux_warned = true; |
785 | } | 769 | } |
786 | |||
787 | return; | ||
788 | } | ||
789 | |||
790 | /* let's see, whether we need to install initial sym_filter_entry */ | ||
791 | if (sym_filter_entry_sched) { | ||
792 | top.sym_filter_entry = sym_filter_entry_sched; | ||
793 | sym_filter_entry_sched = NULL; | ||
794 | if (parse_source(top.sym_filter_entry) < 0) { | ||
795 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); | ||
796 | |||
797 | pr_err("Can't annotate %s", sym->name); | ||
798 | if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) { | ||
799 | pr_err(": No vmlinux file was found in the path:\n"); | ||
800 | machine__fprintf_vmlinux_path(machine, stderr); | ||
801 | } else | ||
802 | pr_err(".\n"); | ||
803 | exit(1); | ||
804 | } | ||
805 | } | 770 | } |
806 | 771 | ||
807 | syme = symbol__priv(al.sym); | 772 | if (al.sym == NULL || !al.sym->ignore) { |
808 | if (!al.sym->ignore) { | ||
809 | struct perf_evsel *evsel; | 773 | struct perf_evsel *evsel; |
774 | struct hist_entry *he; | ||
810 | 775 | ||
811 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 776 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
812 | assert(evsel != NULL); | 777 | assert(evsel != NULL); |
813 | syme->count[evsel->idx]++; | 778 | |
814 | record_precise_ip(syme, al.map, evsel->idx, ip); | 779 | if ((sort__has_parent || symbol_conf.use_callchain) && |
815 | pthread_mutex_lock(&top.active_symbols_lock); | 780 | sample->callchain) { |
816 | if (list_empty(&syme->node) || !syme->node.next) { | 781 | err = perf_session__resolve_callchain(session, al.thread, |
817 | static bool first = true; | 782 | sample->callchain, &parent); |
818 | __list_insert_active_sym(syme); | 783 | if (err) |
819 | if (first) { | 784 | return; |
820 | pthread_cond_broadcast(&top.active_symbols_cond); | ||
821 | first = false; | ||
822 | } | ||
823 | } | 785 | } |
824 | pthread_mutex_unlock(&top.active_symbols_lock); | 786 | |
787 | he = perf_session__add_hist_entry(session, &al, sample, evsel); | ||
788 | if (he == NULL) { | ||
789 | pr_err("Problem incrementing symbol period, skipping event\n"); | ||
790 | return; | ||
791 | } | ||
792 | |||
793 | if (symbol_conf.use_callchain) { | ||
794 | err = callchain_append(he->callchain, &session->callchain_cursor, | ||
795 | sample->period); | ||
796 | if (err) | ||
797 | return; | ||
798 | } | ||
799 | |||
800 | if (sort_has_symbols) | ||
801 | record_precise_ip(he, evsel->idx, ip); | ||
825 | } | 802 | } |
803 | |||
804 | return; | ||
826 | } | 805 | } |
827 | 806 | ||
828 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | 807 | static void perf_session__mmap_read_idx(struct perf_session *self, int idx) |
@@ -873,7 +852,11 @@ static void start_counters(struct perf_evlist *evlist) | |||
873 | attr->read_format |= PERF_FORMAT_ID; | 852 | attr->read_format |= PERF_FORMAT_ID; |
874 | } | 853 | } |
875 | 854 | ||
855 | if (symbol_conf.use_callchain) | ||
856 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
857 | |||
876 | attr->mmap = 1; | 858 | attr->mmap = 1; |
859 | attr->comm = 1; | ||
877 | attr->inherit = inherit; | 860 | attr->inherit = inherit; |
878 | try_again: | 861 | try_again: |
879 | if (perf_evsel__open(counter, top.evlist->cpus, | 862 | if (perf_evsel__open(counter, top.evlist->cpus, |
@@ -928,35 +911,56 @@ out_err: | |||
928 | exit(0); | 911 | exit(0); |
929 | } | 912 | } |
930 | 913 | ||
914 | static int setup_sample_type(void) | ||
915 | { | ||
916 | if (!sort_has_symbols) { | ||
917 | if (symbol_conf.use_callchain) { | ||
918 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); | ||
919 | return -EINVAL; | ||
920 | } | ||
921 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | ||
922 | if (callchain_register_param(&callchain_param) < 0) { | ||
923 | ui__warning("Can't register callchain params.\n"); | ||
924 | return -EINVAL; | ||
925 | } | ||
926 | } | ||
927 | |||
928 | return 0; | ||
929 | } | ||
930 | |||
931 | static int __cmd_top(void) | 931 | static int __cmd_top(void) |
932 | { | 932 | { |
933 | pthread_t thread; | 933 | pthread_t thread; |
934 | int ret __used; | 934 | int ret; |
935 | /* | 935 | /* |
936 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 936 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
937 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 937 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
938 | */ | 938 | */ |
939 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL); | 939 | top.session = perf_session__new(NULL, O_WRONLY, false, false, NULL); |
940 | if (session == NULL) | 940 | if (top.session == NULL) |
941 | return -ENOMEM; | 941 | return -ENOMEM; |
942 | 942 | ||
943 | ret = setup_sample_type(); | ||
944 | if (ret) | ||
945 | goto out_delete; | ||
946 | |||
943 | if (top.target_tid != -1) | 947 | if (top.target_tid != -1) |
944 | perf_event__synthesize_thread_map(top.evlist->threads, | 948 | perf_event__synthesize_thread_map(top.evlist->threads, |
945 | perf_event__process, session); | 949 | perf_event__process, top.session); |
946 | else | 950 | else |
947 | perf_event__synthesize_threads(perf_event__process, session); | 951 | perf_event__synthesize_threads(perf_event__process, top.session); |
948 | 952 | ||
949 | start_counters(top.evlist); | 953 | start_counters(top.evlist); |
950 | session->evlist = top.evlist; | 954 | top.session->evlist = top.evlist; |
951 | perf_session__update_sample_type(session); | 955 | perf_session__update_sample_type(top.session); |
952 | 956 | ||
953 | /* Wait for a minimal set of events before starting the snapshot */ | 957 | /* Wait for a minimal set of events before starting the snapshot */ |
954 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 958 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
955 | 959 | ||
956 | perf_session__mmap_read(session); | 960 | perf_session__mmap_read(top.session); |
957 | 961 | ||
958 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 962 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
959 | display_thread), session)) { | 963 | display_thread), NULL)) { |
960 | printf("Could not create display thread.\n"); | 964 | printf("Could not create display thread.\n"); |
961 | exit(-1); | 965 | exit(-1); |
962 | } | 966 | } |
@@ -974,12 +978,96 @@ static int __cmd_top(void) | |||
974 | while (1) { | 978 | while (1) { |
975 | u64 hits = top.samples; | 979 | u64 hits = top.samples; |
976 | 980 | ||
977 | perf_session__mmap_read(session); | 981 | perf_session__mmap_read(top.session); |
978 | 982 | ||
979 | if (hits == top.samples) | 983 | if (hits == top.samples) |
980 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 984 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
981 | } | 985 | } |
982 | 986 | ||
987 | out_delete: | ||
988 | perf_session__delete(top.session); | ||
989 | top.session = NULL; | ||
990 | |||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | static int | ||
995 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
996 | int unset) | ||
997 | { | ||
998 | char *tok, *tok2; | ||
999 | char *endptr; | ||
1000 | |||
1001 | /* | ||
1002 | * --no-call-graph | ||
1003 | */ | ||
1004 | if (unset) { | ||
1005 | dont_use_callchains = true; | ||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | symbol_conf.use_callchain = true; | ||
1010 | |||
1011 | if (!arg) | ||
1012 | return 0; | ||
1013 | |||
1014 | tok = strtok((char *)arg, ","); | ||
1015 | if (!tok) | ||
1016 | return -1; | ||
1017 | |||
1018 | /* get the output mode */ | ||
1019 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1020 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1021 | |||
1022 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1023 | callchain_param.mode = CHAIN_FLAT; | ||
1024 | |||
1025 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1026 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1027 | |||
1028 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
1029 | callchain_param.mode = CHAIN_NONE; | ||
1030 | symbol_conf.use_callchain = false; | ||
1031 | |||
1032 | return 0; | ||
1033 | } | ||
1034 | |||
1035 | else | ||
1036 | return -1; | ||
1037 | |||
1038 | /* get the min percentage */ | ||
1039 | tok = strtok(NULL, ","); | ||
1040 | if (!tok) | ||
1041 | goto setup; | ||
1042 | |||
1043 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1044 | if (tok == endptr) | ||
1045 | return -1; | ||
1046 | |||
1047 | /* get the print limit */ | ||
1048 | tok2 = strtok(NULL, ","); | ||
1049 | if (!tok2) | ||
1050 | goto setup; | ||
1051 | |||
1052 | if (tok2[0] != 'c') { | ||
1053 | callchain_param.print_limit = strtod(tok2, &endptr); | ||
1054 | tok2 = strtok(NULL, ","); | ||
1055 | if (!tok2) | ||
1056 | goto setup; | ||
1057 | } | ||
1058 | |||
1059 | /* get the call chain order */ | ||
1060 | if (!strcmp(tok2, "caller")) | ||
1061 | callchain_param.order = ORDER_CALLER; | ||
1062 | else if (!strcmp(tok2, "callee")) | ||
1063 | callchain_param.order = ORDER_CALLEE; | ||
1064 | else | ||
1065 | return -1; | ||
1066 | setup: | ||
1067 | if (callchain_register_param(&callchain_param) < 0) { | ||
1068 | fprintf(stderr, "Can't register callchain params\n"); | ||
1069 | return -1; | ||
1070 | } | ||
983 | return 0; | 1071 | return 0; |
984 | } | 1072 | } |
985 | 1073 | ||
@@ -1019,7 +1107,7 @@ static const struct option options[] = { | |||
1019 | "put the counters into a counter group"), | 1107 | "put the counters into a counter group"), |
1020 | OPT_BOOLEAN('i', "inherit", &inherit, | 1108 | OPT_BOOLEAN('i', "inherit", &inherit, |
1021 | "child tasks inherit counters"), | 1109 | "child tasks inherit counters"), |
1022 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1110 | OPT_STRING(0, "sym-annotate", &sym_filter, "symbol name", |
1023 | "symbol to annotate"), | 1111 | "symbol to annotate"), |
1024 | OPT_BOOLEAN('z', "zero", &top.zero, | 1112 | OPT_BOOLEAN('z', "zero", &top.zero, |
1025 | "zero history across updates"), | 1113 | "zero history across updates"), |
@@ -1033,6 +1121,28 @@ static const struct option options[] = { | |||
1033 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | 1121 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), |
1034 | OPT_INCR('v', "verbose", &verbose, | 1122 | OPT_INCR('v', "verbose", &verbose, |
1035 | "be more verbose (show counter open errors, etc)"), | 1123 | "be more verbose (show counter open errors, etc)"), |
1124 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
1125 | "sort by key(s): pid, comm, dso, symbol, parent"), | ||
1126 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | ||
1127 | "Show a column with the number of samples"), | ||
1128 | OPT_CALLBACK_DEFAULT('G', "call-graph", NULL, "output_type,min_percent, call_order", | ||
1129 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | ||
1130 | "Default: fractal,0.5,callee", &parse_callchain_opt, | ||
1131 | callchain_default_opt), | ||
1132 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | ||
1133 | "Show a column with the sum of periods"), | ||
1134 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | ||
1135 | "only consider symbols in these dsos"), | ||
1136 | OPT_STRING(0, "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | ||
1137 | "only consider symbols in these comms"), | ||
1138 | OPT_STRING(0, "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | ||
1139 | "only consider these symbols"), | ||
1140 | OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src, | ||
1141 | "Interleave source code with assembly code (default)"), | ||
1142 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | ||
1143 | "Display raw encoding of assembly instructions (default)"), | ||
1144 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | ||
1145 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | ||
1036 | OPT_END() | 1146 | OPT_END() |
1037 | }; | 1147 | }; |
1038 | 1148 | ||
@@ -1045,18 +1155,16 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1045 | if (top.evlist == NULL) | 1155 | if (top.evlist == NULL) |
1046 | return -ENOMEM; | 1156 | return -ENOMEM; |
1047 | 1157 | ||
1048 | page_size = sysconf(_SC_PAGE_SIZE); | 1158 | symbol_conf.exclude_other = false; |
1049 | 1159 | ||
1050 | argc = parse_options(argc, argv, options, top_usage, 0); | 1160 | argc = parse_options(argc, argv, options, top_usage, 0); |
1051 | if (argc) | 1161 | if (argc) |
1052 | usage_with_options(top_usage, options); | 1162 | usage_with_options(top_usage, options); |
1053 | 1163 | ||
1054 | /* | 1164 | if (sort_order == default_sort_order) |
1055 | * XXX For now start disabled, only using TUI if explicitely asked for. | 1165 | sort_order = "dso,symbol"; |
1056 | * Change that when handle_keys equivalent gets written, live annotation | 1166 | |
1057 | * done, etc. | 1167 | setup_sorting(top_usage, options); |
1058 | */ | ||
1059 | use_browser = 0; | ||
1060 | 1168 | ||
1061 | if (use_stdio) | 1169 | if (use_stdio) |
1062 | use_browser = 0; | 1170 | use_browser = 0; |
@@ -1119,13 +1227,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1119 | 1227 | ||
1120 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | 1228 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
1121 | 1229 | ||
1122 | symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + | 1230 | symbol_conf.priv_size = sizeof(struct annotation); |
1123 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); | ||
1124 | 1231 | ||
1125 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1232 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1126 | if (symbol__init() < 0) | 1233 | if (symbol__init() < 0) |
1127 | return -1; | 1234 | return -1; |
1128 | 1235 | ||
1236 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
1237 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | ||
1238 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
1239 | |||
1240 | /* | ||
1241 | * Avoid annotation data structures overhead when symbols aren't on the | ||
1242 | * sort list. | ||
1243 | */ | ||
1244 | sort_has_symbols = sort_sym.list.next != NULL; | ||
1245 | |||
1129 | get_term_dimensions(&winsize); | 1246 | get_term_dimensions(&winsize); |
1130 | if (top.print_entries == 0) { | 1247 | if (top.print_entries == 0) { |
1131 | update_print_entries(&winsize); | 1248 | update_print_entries(&winsize); |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 4702e2443a8e..b382bd551aac 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -4,7 +4,6 @@ | |||
4 | #include "util/util.h" | 4 | #include "util/util.h" |
5 | #include "util/strbuf.h" | 5 | #include "util/strbuf.h" |
6 | 6 | ||
7 | extern const char perf_version_string[]; | ||
8 | extern const char perf_usage_string[]; | 7 | extern const char perf_usage_string[]; |
9 | extern const char perf_more_info_string[]; | 8 | extern const char perf_more_info_string[]; |
10 | 9 | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index ec635b7cc8ea..73d0cac8b67e 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -427,6 +427,24 @@ static void get_debugfs_mntpt(void) | |||
427 | debugfs_mntpt[0] = '\0'; | 427 | debugfs_mntpt[0] = '\0'; |
428 | } | 428 | } |
429 | 429 | ||
430 | static void pthread__block_sigwinch(void) | ||
431 | { | ||
432 | sigset_t set; | ||
433 | |||
434 | sigemptyset(&set); | ||
435 | sigaddset(&set, SIGWINCH); | ||
436 | pthread_sigmask(SIG_BLOCK, &set, NULL); | ||
437 | } | ||
438 | |||
439 | void pthread__unblock_sigwinch(void) | ||
440 | { | ||
441 | sigset_t set; | ||
442 | |||
443 | sigemptyset(&set); | ||
444 | sigaddset(&set, SIGWINCH); | ||
445 | pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||
446 | } | ||
447 | |||
430 | int main(int argc, const char **argv) | 448 | int main(int argc, const char **argv) |
431 | { | 449 | { |
432 | const char *cmd; | 450 | const char *cmd; |
@@ -480,6 +498,12 @@ int main(int argc, const char **argv) | |||
480 | * time. | 498 | * time. |
481 | */ | 499 | */ |
482 | setup_path(); | 500 | setup_path(); |
501 | /* | ||
502 | * Block SIGWINCH notifications so that the thread that wants it can | ||
503 | * unblock and get syscalls like select interrupted instead of waiting | ||
504 | * forever while the signal goes to some other non interested thread. | ||
505 | */ | ||
506 | pthread__block_sigwinch(); | ||
483 | 507 | ||
484 | while (1) { | 508 | while (1) { |
485 | static int done_help; | 509 | static int done_help; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a5fc660c1f12..914c895510f7 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws); | |||
9 | #include "../../arch/x86/include/asm/unistd.h" | 9 | #include "../../arch/x86/include/asm/unistd.h" |
10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
12 | #define CPUINFO_PROC "model name" | ||
12 | #endif | 13 | #endif |
13 | 14 | ||
14 | #if defined(__x86_64__) | 15 | #if defined(__x86_64__) |
15 | #include "../../arch/x86/include/asm/unistd.h" | 16 | #include "../../arch/x86/include/asm/unistd.h" |
16 | #define rmb() asm volatile("lfence" ::: "memory") | 17 | #define rmb() asm volatile("lfence" ::: "memory") |
17 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 18 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
19 | #define CPUINFO_PROC "model name" | ||
18 | #endif | 20 | #endif |
19 | 21 | ||
20 | #ifdef __powerpc__ | 22 | #ifdef __powerpc__ |
21 | #include "../../arch/powerpc/include/asm/unistd.h" | 23 | #include "../../arch/powerpc/include/asm/unistd.h" |
22 | #define rmb() asm volatile ("sync" ::: "memory") | 24 | #define rmb() asm volatile ("sync" ::: "memory") |
23 | #define cpu_relax() asm volatile ("" ::: "memory"); | 25 | #define cpu_relax() asm volatile ("" ::: "memory"); |
26 | #define CPUINFO_PROC "cpu" | ||
24 | #endif | 27 | #endif |
25 | 28 | ||
26 | #ifdef __s390__ | 29 | #ifdef __s390__ |
@@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws); | |||
37 | # define rmb() asm volatile("" ::: "memory") | 40 | # define rmb() asm volatile("" ::: "memory") |
38 | #endif | 41 | #endif |
39 | #define cpu_relax() asm volatile("" ::: "memory") | 42 | #define cpu_relax() asm volatile("" ::: "memory") |
43 | #define CPUINFO_PROC "cpu type" | ||
40 | #endif | 44 | #endif |
41 | 45 | ||
42 | #ifdef __hppa__ | 46 | #ifdef __hppa__ |
43 | #include "../../arch/parisc/include/asm/unistd.h" | 47 | #include "../../arch/parisc/include/asm/unistd.h" |
44 | #define rmb() asm volatile("" ::: "memory") | 48 | #define rmb() asm volatile("" ::: "memory") |
45 | #define cpu_relax() asm volatile("" ::: "memory"); | 49 | #define cpu_relax() asm volatile("" ::: "memory"); |
50 | #define CPUINFO_PROC "cpu" | ||
46 | #endif | 51 | #endif |
47 | 52 | ||
48 | #ifdef __sparc__ | 53 | #ifdef __sparc__ |
49 | #include "../../arch/sparc/include/asm/unistd.h" | 54 | #include "../../arch/sparc/include/asm/unistd.h" |
50 | #define rmb() asm volatile("":::"memory") | 55 | #define rmb() asm volatile("":::"memory") |
51 | #define cpu_relax() asm volatile("":::"memory") | 56 | #define cpu_relax() asm volatile("":::"memory") |
57 | #define CPUINFO_PROC "cpu" | ||
52 | #endif | 58 | #endif |
53 | 59 | ||
54 | #ifdef __alpha__ | 60 | #ifdef __alpha__ |
55 | #include "../../arch/alpha/include/asm/unistd.h" | 61 | #include "../../arch/alpha/include/asm/unistd.h" |
56 | #define rmb() asm volatile("mb" ::: "memory") | 62 | #define rmb() asm volatile("mb" ::: "memory") |
57 | #define cpu_relax() asm volatile("" ::: "memory") | 63 | #define cpu_relax() asm volatile("" ::: "memory") |
64 | #define CPUINFO_PROC "cpu model" | ||
58 | #endif | 65 | #endif |
59 | 66 | ||
60 | #ifdef __ia64__ | 67 | #ifdef __ia64__ |
61 | #include "../../arch/ia64/include/asm/unistd.h" | 68 | #include "../../arch/ia64/include/asm/unistd.h" |
62 | #define rmb() asm volatile ("mf" ::: "memory") | 69 | #define rmb() asm volatile ("mf" ::: "memory") |
63 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | 70 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") |
71 | #define CPUINFO_PROC "model name" | ||
64 | #endif | 72 | #endif |
65 | 73 | ||
66 | #ifdef __arm__ | 74 | #ifdef __arm__ |
@@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws); | |||
71 | */ | 79 | */ |
72 | #define rmb() ((void(*)(void))0xffff0fa0)() | 80 | #define rmb() ((void(*)(void))0xffff0fa0)() |
73 | #define cpu_relax() asm volatile("":::"memory") | 81 | #define cpu_relax() asm volatile("":::"memory") |
82 | #define CPUINFO_PROC "Processor" | ||
74 | #endif | 83 | #endif |
75 | 84 | ||
76 | #ifdef __mips__ | 85 | #ifdef __mips__ |
@@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws); | |||
83 | : /* no input */ \ | 92 | : /* no input */ \ |
84 | : "memory") | 93 | : "memory") |
85 | #define cpu_relax() asm volatile("" ::: "memory") | 94 | #define cpu_relax() asm volatile("" ::: "memory") |
95 | #define CPUINFO_PROC "cpu model" | ||
86 | #endif | 96 | #endif |
87 | 97 | ||
88 | #include <time.h> | 98 | #include <time.h> |
@@ -171,5 +181,8 @@ struct ip_callchain { | |||
171 | }; | 181 | }; |
172 | 182 | ||
173 | extern bool perf_host, perf_guest; | 183 | extern bool perf_host, perf_guest; |
184 | extern const char perf_version_string[]; | ||
185 | |||
186 | void pthread__unblock_sigwinch(void); | ||
174 | 187 | ||
175 | #endif | 188 | #endif |
diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-record b/tools/perf/scripts/python/bin/net_dropmonitor-record new file mode 100755 index 000000000000..423fb81dadae --- /dev/null +++ b/tools/perf/scripts/python/bin/net_dropmonitor-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -e skb:kfree_skb $@ | ||
diff --git a/tools/perf/scripts/python/bin/net_dropmonitor-report b/tools/perf/scripts/python/bin/net_dropmonitor-report new file mode 100755 index 000000000000..8d698f5a06aa --- /dev/null +++ b/tools/perf/scripts/python/bin/net_dropmonitor-report | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # description: display a table of dropped frames | ||
3 | |||
4 | perf script -s "$PERF_EXEC_PATH"/scripts/python/net_dropmonitor.py $@ | ||
diff --git a/tools/perf/scripts/python/net_dropmonitor.py b/tools/perf/scripts/python/net_dropmonitor.py new file mode 100755 index 000000000000..a4ffc9500023 --- /dev/null +++ b/tools/perf/scripts/python/net_dropmonitor.py | |||
@@ -0,0 +1,72 @@ | |||
1 | # Monitor the system for dropped packets and proudce a report of drop locations and counts | ||
2 | |||
3 | import os | ||
4 | import sys | ||
5 | |||
6 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
7 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
8 | |||
9 | from perf_trace_context import * | ||
10 | from Core import * | ||
11 | from Util import * | ||
12 | |||
13 | drop_log = {} | ||
14 | kallsyms = [] | ||
15 | |||
16 | def get_kallsyms_table(): | ||
17 | global kallsyms | ||
18 | try: | ||
19 | f = open("/proc/kallsyms", "r") | ||
20 | linecount = 0 | ||
21 | for line in f: | ||
22 | linecount = linecount+1 | ||
23 | f.seek(0) | ||
24 | except: | ||
25 | return | ||
26 | |||
27 | |||
28 | j = 0 | ||
29 | for line in f: | ||
30 | loc = int(line.split()[0], 16) | ||
31 | name = line.split()[2] | ||
32 | j = j +1 | ||
33 | if ((j % 100) == 0): | ||
34 | print "\r" + str(j) + "/" + str(linecount), | ||
35 | kallsyms.append({ 'loc': loc, 'name' : name}) | ||
36 | |||
37 | print "\r" + str(j) + "/" + str(linecount) | ||
38 | kallsyms.sort() | ||
39 | return | ||
40 | |||
41 | def get_sym(sloc): | ||
42 | loc = int(sloc) | ||
43 | for i in kallsyms: | ||
44 | if (i['loc'] >= loc): | ||
45 | return (i['name'], i['loc']-loc) | ||
46 | return (None, 0) | ||
47 | |||
48 | def print_drop_table(): | ||
49 | print "%25s %25s %25s" % ("LOCATION", "OFFSET", "COUNT") | ||
50 | for i in drop_log.keys(): | ||
51 | (sym, off) = get_sym(i) | ||
52 | if sym == None: | ||
53 | sym = i | ||
54 | print "%25s %25s %25s" % (sym, off, drop_log[i]) | ||
55 | |||
56 | |||
57 | def trace_begin(): | ||
58 | print "Starting trace (Ctrl-C to dump results)" | ||
59 | |||
60 | def trace_end(): | ||
61 | print "Gathering kallsyms data" | ||
62 | get_kallsyms_table() | ||
63 | print_drop_table() | ||
64 | |||
65 | # called from perf, when it finds a correspoinding event | ||
66 | def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, | ||
67 | skbaddr, protocol, location): | ||
68 | slocation = str(location) | ||
69 | try: | ||
70 | drop_log[slocation] = drop_log[slocation] + 1 | ||
71 | except: | ||
72 | drop_log[slocation] = 1 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e01af2b1a469..bc8f4773d4d8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | 18 | ||
19 | const char *disassembler_style; | ||
20 | |||
19 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | 21 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) |
20 | { | 22 | { |
21 | struct annotation *notes = symbol__annotation(sym); | 23 | struct annotation *notes = symbol__annotation(sym); |
@@ -323,10 +325,15 @@ fallback: | |||
323 | dso, dso->long_name, sym, sym->name); | 325 | dso, dso->long_name, sym, sym->name); |
324 | 326 | ||
325 | snprintf(command, sizeof(command), | 327 | snprintf(command, sizeof(command), |
326 | "objdump --start-address=0x%016" PRIx64 | 328 | "objdump %s%s --start-address=0x%016" PRIx64 |
327 | " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | 329 | " --stop-address=0x%016" PRIx64 |
330 | " -d %s %s -C %s|grep -v %s|expand", | ||
331 | disassembler_style ? "-M " : "", | ||
332 | disassembler_style ? disassembler_style : "", | ||
328 | map__rip_2objdump(map, sym->start), | 333 | map__rip_2objdump(map, sym->start), |
329 | map__rip_2objdump(map, sym->end), | 334 | map__rip_2objdump(map, sym->end), |
335 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | ||
336 | symbol_conf.annotate_src ? "-S" : "", | ||
330 | symfs_filename, filename); | 337 | symfs_filename, filename); |
331 | 338 | ||
332 | pr_debug("Executing: %s\n", command); | 339 | pr_debug("Executing: %s\n", command); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c2c286896801..d9072523d342 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -91,13 +91,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
91 | #ifdef NO_NEWT_SUPPORT | 91 | #ifdef NO_NEWT_SUPPORT |
92 | static inline int symbol__tui_annotate(struct symbol *sym __used, | 92 | static inline int symbol__tui_annotate(struct symbol *sym __used, |
93 | struct map *map __used, | 93 | struct map *map __used, |
94 | int evidx __used, int refresh __used) | 94 | int evidx __used, |
95 | void(*timer)(void *arg) __used, | ||
96 | void *arg __used, int delay_secs __used) | ||
95 | { | 97 | { |
96 | return 0; | 98 | return 0; |
97 | } | 99 | } |
98 | #else | 100 | #else |
99 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 101 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
100 | int refresh); | 102 | int nr_events, void(*timer)(void *arg), void *arg, |
103 | int delay_secs); | ||
101 | #endif | 104 | #endif |
102 | 105 | ||
106 | extern const char *disassembler_style; | ||
107 | |||
103 | #endif /* __PERF_ANNOTATE_H */ | 108 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e191eb9a667f..521c38a79190 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -200,7 +200,7 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, | |||
200 | * Auto-detect: | 200 | * Auto-detect: |
201 | */ | 201 | */ |
202 | if (perf_use_color_default < 0) { | 202 | if (perf_use_color_default < 0) { |
203 | if (isatty(1) || pager_in_use()) | 203 | if (isatty(fileno(fp)) || pager_in_use()) |
204 | perf_use_color_default = 1; | 204 | perf_use_color_default = 1; |
205 | else | 205 | else |
206 | perf_use_color_default = 0; | 206 | perf_use_color_default = 0; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 72e9f4886b6d..2f6bc89027da 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | |||
533 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 533 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
534 | return first->attr.sample_id_all; | 534 | return first->attr.sample_id_all; |
535 | } | 535 | } |
536 | |||
537 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
538 | struct perf_evsel *evsel) | ||
539 | { | ||
540 | evlist->selected = evsel; | ||
541 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f34915002745..6be71fc57794 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -25,6 +25,7 @@ struct perf_evlist { | |||
25 | struct pollfd *pollfd; | 25 | struct pollfd *pollfd; |
26 | struct thread_map *threads; | 26 | struct thread_map *threads; |
27 | struct cpu_map *cpus; | 27 | struct cpu_map *cpus; |
28 | struct perf_evsel *selected; | ||
28 | }; | 29 | }; |
29 | 30 | ||
30 | struct perf_evsel; | 31 | struct perf_evsel; |
@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist); | |||
56 | void perf_evlist__disable(struct perf_evlist *evlist); | 57 | void perf_evlist__disable(struct perf_evlist *evlist); |
57 | void perf_evlist__enable(struct perf_evlist *evlist); | 58 | void perf_evlist__enable(struct perf_evlist *evlist); |
58 | 59 | ||
60 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
61 | struct perf_evsel *evsel); | ||
62 | |||
59 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 63 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
60 | struct cpu_map *cpus, | 64 | struct cpu_map *cpus, |
61 | struct thread_map *threads) | 65 | struct thread_map *threads) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e389815078d3..b46f6e4bff3c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
39 | evsel->idx = idx; | 39 | evsel->idx = idx; |
40 | evsel->attr = *attr; | 40 | evsel->attr = *attr; |
41 | INIT_LIST_HEAD(&evsel->node); | 41 | INIT_LIST_HEAD(&evsel->node); |
42 | hists__init(&evsel->hists); | ||
42 | } | 43 | } |
43 | 44 | ||
44 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 45 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b6c1ad123ca9..76c0b2c49eb8 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | #include <sys/utsname.h> | ||
10 | 11 | ||
11 | #include "evlist.h" | 12 | #include "evlist.h" |
12 | #include "evsel.h" | 13 | #include "evsel.h" |
@@ -17,12 +18,19 @@ | |||
17 | #include "session.h" | 18 | #include "session.h" |
18 | #include "symbol.h" | 19 | #include "symbol.h" |
19 | #include "debug.h" | 20 | #include "debug.h" |
21 | #include "cpumap.h" | ||
20 | 22 | ||
21 | static bool no_buildid_cache = false; | 23 | static bool no_buildid_cache = false; |
22 | 24 | ||
23 | static int event_count; | 25 | static int event_count; |
24 | static struct perf_trace_event_type *events; | 26 | static struct perf_trace_event_type *events; |
25 | 27 | ||
28 | static u32 header_argc; | ||
29 | static const char **header_argv; | ||
30 | |||
31 | static int dsos__write_buildid_table(struct perf_header *header, int fd); | ||
32 | static int perf_session__cache_build_ids(struct perf_session *session); | ||
33 | |||
26 | int perf_header__push_event(u64 id, const char *name) | 34 | int perf_header__push_event(u64 id, const char *name) |
27 | { | 35 | { |
28 | if (strlen(name) > MAX_EVENT_NAME) | 36 | if (strlen(name) > MAX_EVENT_NAME) |
@@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count, | |||
110 | return err; | 118 | return err; |
111 | } | 119 | } |
112 | 120 | ||
121 | static int do_write_string(int fd, const char *str) | ||
122 | { | ||
123 | u32 len, olen; | ||
124 | int ret; | ||
125 | |||
126 | olen = strlen(str) + 1; | ||
127 | len = ALIGN(olen, NAME_ALIGN); | ||
128 | |||
129 | /* write len, incl. \0 */ | ||
130 | ret = do_write(fd, &len, sizeof(len)); | ||
131 | if (ret < 0) | ||
132 | return ret; | ||
133 | |||
134 | return write_padded(fd, str, olen, len); | ||
135 | } | ||
136 | |||
137 | static char *do_read_string(int fd, struct perf_header *ph) | ||
138 | { | ||
139 | ssize_t sz, ret; | ||
140 | u32 len; | ||
141 | char *buf; | ||
142 | |||
143 | sz = read(fd, &len, sizeof(len)); | ||
144 | if (sz < (ssize_t)sizeof(len)) | ||
145 | return NULL; | ||
146 | |||
147 | if (ph->needs_swap) | ||
148 | len = bswap_32(len); | ||
149 | |||
150 | buf = malloc(len); | ||
151 | if (!buf) | ||
152 | return NULL; | ||
153 | |||
154 | ret = read(fd, buf, len); | ||
155 | if (ret == (ssize_t)len) { | ||
156 | /* | ||
157 | * strings are padded by zeroes | ||
158 | * thus the actual strlen of buf | ||
159 | * may be less than len | ||
160 | */ | ||
161 | return buf; | ||
162 | } | ||
163 | |||
164 | free(buf); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | int | ||
169 | perf_header__set_cmdline(int argc, const char **argv) | ||
170 | { | ||
171 | int i; | ||
172 | |||
173 | header_argc = (u32)argc; | ||
174 | |||
175 | /* do not include NULL termination */ | ||
176 | header_argv = calloc(argc, sizeof(char *)); | ||
177 | if (!header_argv) | ||
178 | return -ENOMEM; | ||
179 | |||
180 | /* | ||
181 | * must copy argv contents because it gets moved | ||
182 | * around during option parsing | ||
183 | */ | ||
184 | for (i = 0; i < argc ; i++) | ||
185 | header_argv[i] = argv[i]; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int write_trace_info(int fd, struct perf_header *h __used, | ||
191 | struct perf_evlist *evlist) | ||
192 | { | ||
193 | return read_tracing_data(fd, &evlist->entries); | ||
194 | } | ||
195 | |||
196 | |||
197 | static int write_build_id(int fd, struct perf_header *h, | ||
198 | struct perf_evlist *evlist __used) | ||
199 | { | ||
200 | struct perf_session *session; | ||
201 | int err; | ||
202 | |||
203 | session = container_of(h, struct perf_session, header); | ||
204 | |||
205 | err = dsos__write_buildid_table(h, fd); | ||
206 | if (err < 0) { | ||
207 | pr_debug("failed to write buildid table\n"); | ||
208 | return err; | ||
209 | } | ||
210 | if (!no_buildid_cache) | ||
211 | perf_session__cache_build_ids(session); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int write_hostname(int fd, struct perf_header *h __used, | ||
217 | struct perf_evlist *evlist __used) | ||
218 | { | ||
219 | struct utsname uts; | ||
220 | int ret; | ||
221 | |||
222 | ret = uname(&uts); | ||
223 | if (ret < 0) | ||
224 | return -1; | ||
225 | |||
226 | return do_write_string(fd, uts.nodename); | ||
227 | } | ||
228 | |||
229 | static int write_osrelease(int fd, struct perf_header *h __used, | ||
230 | struct perf_evlist *evlist __used) | ||
231 | { | ||
232 | struct utsname uts; | ||
233 | int ret; | ||
234 | |||
235 | ret = uname(&uts); | ||
236 | if (ret < 0) | ||
237 | return -1; | ||
238 | |||
239 | return do_write_string(fd, uts.release); | ||
240 | } | ||
241 | |||
242 | static int write_arch(int fd, struct perf_header *h __used, | ||
243 | struct perf_evlist *evlist __used) | ||
244 | { | ||
245 | struct utsname uts; | ||
246 | int ret; | ||
247 | |||
248 | ret = uname(&uts); | ||
249 | if (ret < 0) | ||
250 | return -1; | ||
251 | |||
252 | return do_write_string(fd, uts.machine); | ||
253 | } | ||
254 | |||
255 | static int write_version(int fd, struct perf_header *h __used, | ||
256 | struct perf_evlist *evlist __used) | ||
257 | { | ||
258 | return do_write_string(fd, perf_version_string); | ||
259 | } | ||
260 | |||
261 | static int write_cpudesc(int fd, struct perf_header *h __used, | ||
262 | struct perf_evlist *evlist __used) | ||
263 | { | ||
264 | #ifndef CPUINFO_PROC | ||
265 | #define CPUINFO_PROC NULL | ||
266 | #endif | ||
267 | FILE *file; | ||
268 | char *buf = NULL; | ||
269 | char *s, *p; | ||
270 | const char *search = CPUINFO_PROC; | ||
271 | size_t len = 0; | ||
272 | int ret = -1; | ||
273 | |||
274 | if (!search) | ||
275 | return -1; | ||
276 | |||
277 | file = fopen("/proc/cpuinfo", "r"); | ||
278 | if (!file) | ||
279 | return -1; | ||
280 | |||
281 | while (getline(&buf, &len, file) > 0) { | ||
282 | ret = strncmp(buf, search, strlen(search)); | ||
283 | if (!ret) | ||
284 | break; | ||
285 | } | ||
286 | |||
287 | if (ret) | ||
288 | goto done; | ||
289 | |||
290 | s = buf; | ||
291 | |||
292 | p = strchr(buf, ':'); | ||
293 | if (p && *(p+1) == ' ' && *(p+2)) | ||
294 | s = p + 2; | ||
295 | p = strchr(s, '\n'); | ||
296 | if (p) | ||
297 | *p = '\0'; | ||
298 | |||
299 | /* squash extra space characters (branding string) */ | ||
300 | p = s; | ||
301 | while (*p) { | ||
302 | if (isspace(*p)) { | ||
303 | char *r = p + 1; | ||
304 | char *q = r; | ||
305 | *p = ' '; | ||
306 | while (*q && isspace(*q)) | ||
307 | q++; | ||
308 | if (q != (p+1)) | ||
309 | while ((*r++ = *q++)); | ||
310 | } | ||
311 | p++; | ||
312 | } | ||
313 | ret = do_write_string(fd, s); | ||
314 | done: | ||
315 | free(buf); | ||
316 | fclose(file); | ||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static int write_nrcpus(int fd, struct perf_header *h __used, | ||
321 | struct perf_evlist *evlist __used) | ||
322 | { | ||
323 | long nr; | ||
324 | u32 nrc, nra; | ||
325 | int ret; | ||
326 | |||
327 | nr = sysconf(_SC_NPROCESSORS_CONF); | ||
328 | if (nr < 0) | ||
329 | return -1; | ||
330 | |||
331 | nrc = (u32)(nr & UINT_MAX); | ||
332 | |||
333 | nr = sysconf(_SC_NPROCESSORS_ONLN); | ||
334 | if (nr < 0) | ||
335 | return -1; | ||
336 | |||
337 | nra = (u32)(nr & UINT_MAX); | ||
338 | |||
339 | ret = do_write(fd, &nrc, sizeof(nrc)); | ||
340 | if (ret < 0) | ||
341 | return ret; | ||
342 | |||
343 | return do_write(fd, &nra, sizeof(nra)); | ||
344 | } | ||
345 | |||
346 | static int write_event_desc(int fd, struct perf_header *h __used, | ||
347 | struct perf_evlist *evlist) | ||
348 | { | ||
349 | struct perf_evsel *attr; | ||
350 | u32 nre = 0, nri, sz; | ||
351 | int ret; | ||
352 | |||
353 | list_for_each_entry(attr, &evlist->entries, node) | ||
354 | nre++; | ||
355 | |||
356 | /* | ||
357 | * write number of events | ||
358 | */ | ||
359 | ret = do_write(fd, &nre, sizeof(nre)); | ||
360 | if (ret < 0) | ||
361 | return ret; | ||
362 | |||
363 | /* | ||
364 | * size of perf_event_attr struct | ||
365 | */ | ||
366 | sz = (u32)sizeof(attr->attr); | ||
367 | ret = do_write(fd, &sz, sizeof(sz)); | ||
368 | if (ret < 0) | ||
369 | return ret; | ||
370 | |||
371 | list_for_each_entry(attr, &evlist->entries, node) { | ||
372 | |||
373 | ret = do_write(fd, &attr->attr, sz); | ||
374 | if (ret < 0) | ||
375 | return ret; | ||
376 | /* | ||
377 | * write number of unique id per event | ||
378 | * there is one id per instance of an event | ||
379 | * | ||
380 | * copy into an nri to be independent of the | ||
381 | * type of ids, | ||
382 | */ | ||
383 | nri = attr->ids; | ||
384 | ret = do_write(fd, &nri, sizeof(nri)); | ||
385 | if (ret < 0) | ||
386 | return ret; | ||
387 | |||
388 | /* | ||
389 | * write event string as passed on cmdline | ||
390 | */ | ||
391 | ret = do_write_string(fd, attr->name); | ||
392 | if (ret < 0) | ||
393 | return ret; | ||
394 | /* | ||
395 | * write unique ids for this event | ||
396 | */ | ||
397 | ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); | ||
398 | if (ret < 0) | ||
399 | return ret; | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int write_cmdline(int fd, struct perf_header *h __used, | ||
405 | struct perf_evlist *evlist __used) | ||
406 | { | ||
407 | char buf[MAXPATHLEN]; | ||
408 | char proc[32]; | ||
409 | u32 i, n; | ||
410 | int ret; | ||
411 | |||
412 | /* | ||
413 | * actual atual path to perf binary | ||
414 | */ | ||
415 | sprintf(proc, "/proc/%d/exe", getpid()); | ||
416 | ret = readlink(proc, buf, sizeof(buf)); | ||
417 | if (ret <= 0) | ||
418 | return -1; | ||
419 | |||
420 | /* readlink() does not add null termination */ | ||
421 | buf[ret] = '\0'; | ||
422 | |||
423 | /* account for binary path */ | ||
424 | n = header_argc + 1; | ||
425 | |||
426 | ret = do_write(fd, &n, sizeof(n)); | ||
427 | if (ret < 0) | ||
428 | return ret; | ||
429 | |||
430 | ret = do_write_string(fd, buf); | ||
431 | if (ret < 0) | ||
432 | return ret; | ||
433 | |||
434 | for (i = 0 ; i < header_argc; i++) { | ||
435 | ret = do_write_string(fd, header_argv[i]); | ||
436 | if (ret < 0) | ||
437 | return ret; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | #define CORE_SIB_FMT \ | ||
443 | "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" | ||
444 | #define THRD_SIB_FMT \ | ||
445 | "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" | ||
446 | |||
447 | struct cpu_topo { | ||
448 | u32 core_sib; | ||
449 | u32 thread_sib; | ||
450 | char **core_siblings; | ||
451 | char **thread_siblings; | ||
452 | }; | ||
453 | |||
454 | static int build_cpu_topo(struct cpu_topo *tp, int cpu) | ||
455 | { | ||
456 | FILE *fp; | ||
457 | char filename[MAXPATHLEN]; | ||
458 | char *buf = NULL, *p; | ||
459 | size_t len = 0; | ||
460 | u32 i = 0; | ||
461 | int ret = -1; | ||
462 | |||
463 | sprintf(filename, CORE_SIB_FMT, cpu); | ||
464 | fp = fopen(filename, "r"); | ||
465 | if (!fp) | ||
466 | return -1; | ||
467 | |||
468 | if (getline(&buf, &len, fp) <= 0) | ||
469 | goto done; | ||
470 | |||
471 | fclose(fp); | ||
472 | |||
473 | p = strchr(buf, '\n'); | ||
474 | if (p) | ||
475 | *p = '\0'; | ||
476 | |||
477 | for (i = 0; i < tp->core_sib; i++) { | ||
478 | if (!strcmp(buf, tp->core_siblings[i])) | ||
479 | break; | ||
480 | } | ||
481 | if (i == tp->core_sib) { | ||
482 | tp->core_siblings[i] = buf; | ||
483 | tp->core_sib++; | ||
484 | buf = NULL; | ||
485 | len = 0; | ||
486 | } | ||
487 | |||
488 | sprintf(filename, THRD_SIB_FMT, cpu); | ||
489 | fp = fopen(filename, "r"); | ||
490 | if (!fp) | ||
491 | goto done; | ||
492 | |||
493 | if (getline(&buf, &len, fp) <= 0) | ||
494 | goto done; | ||
495 | |||
496 | p = strchr(buf, '\n'); | ||
497 | if (p) | ||
498 | *p = '\0'; | ||
499 | |||
500 | for (i = 0; i < tp->thread_sib; i++) { | ||
501 | if (!strcmp(buf, tp->thread_siblings[i])) | ||
502 | break; | ||
503 | } | ||
504 | if (i == tp->thread_sib) { | ||
505 | tp->thread_siblings[i] = buf; | ||
506 | tp->thread_sib++; | ||
507 | buf = NULL; | ||
508 | } | ||
509 | ret = 0; | ||
510 | done: | ||
511 | if(fp) | ||
512 | fclose(fp); | ||
513 | free(buf); | ||
514 | return ret; | ||
515 | } | ||
516 | |||
517 | static void free_cpu_topo(struct cpu_topo *tp) | ||
518 | { | ||
519 | u32 i; | ||
520 | |||
521 | if (!tp) | ||
522 | return; | ||
523 | |||
524 | for (i = 0 ; i < tp->core_sib; i++) | ||
525 | free(tp->core_siblings[i]); | ||
526 | |||
527 | for (i = 0 ; i < tp->thread_sib; i++) | ||
528 | free(tp->thread_siblings[i]); | ||
529 | |||
530 | free(tp); | ||
531 | } | ||
532 | |||
533 | static struct cpu_topo *build_cpu_topology(void) | ||
534 | { | ||
535 | struct cpu_topo *tp; | ||
536 | void *addr; | ||
537 | u32 nr, i; | ||
538 | size_t sz; | ||
539 | long ncpus; | ||
540 | int ret = -1; | ||
541 | |||
542 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
543 | if (ncpus < 0) | ||
544 | return NULL; | ||
545 | |||
546 | nr = (u32)(ncpus & UINT_MAX); | ||
547 | |||
548 | sz = nr * sizeof(char *); | ||
549 | |||
550 | addr = calloc(1, sizeof(*tp) + 2 * sz); | ||
551 | if (!addr) | ||
552 | return NULL; | ||
553 | |||
554 | tp = addr; | ||
555 | |||
556 | addr += sizeof(*tp); | ||
557 | tp->core_siblings = addr; | ||
558 | addr += sz; | ||
559 | tp->thread_siblings = addr; | ||
560 | |||
561 | for (i = 0; i < nr; i++) { | ||
562 | ret = build_cpu_topo(tp, i); | ||
563 | if (ret < 0) | ||
564 | break; | ||
565 | } | ||
566 | if (ret) { | ||
567 | free_cpu_topo(tp); | ||
568 | tp = NULL; | ||
569 | } | ||
570 | return tp; | ||
571 | } | ||
572 | |||
573 | static int write_cpu_topology(int fd, struct perf_header *h __used, | ||
574 | struct perf_evlist *evlist __used) | ||
575 | { | ||
576 | struct cpu_topo *tp; | ||
577 | u32 i; | ||
578 | int ret; | ||
579 | |||
580 | tp = build_cpu_topology(); | ||
581 | if (!tp) | ||
582 | return -1; | ||
583 | |||
584 | ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); | ||
585 | if (ret < 0) | ||
586 | goto done; | ||
587 | |||
588 | for (i = 0; i < tp->core_sib; i++) { | ||
589 | ret = do_write_string(fd, tp->core_siblings[i]); | ||
590 | if (ret < 0) | ||
591 | goto done; | ||
592 | } | ||
593 | ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); | ||
594 | if (ret < 0) | ||
595 | goto done; | ||
596 | |||
597 | for (i = 0; i < tp->thread_sib; i++) { | ||
598 | ret = do_write_string(fd, tp->thread_siblings[i]); | ||
599 | if (ret < 0) | ||
600 | break; | ||
601 | } | ||
602 | done: | ||
603 | free_cpu_topo(tp); | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | |||
608 | |||
609 | static int write_total_mem(int fd, struct perf_header *h __used, | ||
610 | struct perf_evlist *evlist __used) | ||
611 | { | ||
612 | char *buf = NULL; | ||
613 | FILE *fp; | ||
614 | size_t len = 0; | ||
615 | int ret = -1, n; | ||
616 | uint64_t mem; | ||
617 | |||
618 | fp = fopen("/proc/meminfo", "r"); | ||
619 | if (!fp) | ||
620 | return -1; | ||
621 | |||
622 | while (getline(&buf, &len, fp) > 0) { | ||
623 | ret = strncmp(buf, "MemTotal:", 9); | ||
624 | if (!ret) | ||
625 | break; | ||
626 | } | ||
627 | if (!ret) { | ||
628 | n = sscanf(buf, "%*s %"PRIu64, &mem); | ||
629 | if (n == 1) | ||
630 | ret = do_write(fd, &mem, sizeof(mem)); | ||
631 | } | ||
632 | free(buf); | ||
633 | fclose(fp); | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static int write_topo_node(int fd, int node) | ||
638 | { | ||
639 | char str[MAXPATHLEN]; | ||
640 | char field[32]; | ||
641 | char *buf = NULL, *p; | ||
642 | size_t len = 0; | ||
643 | FILE *fp; | ||
644 | u64 mem_total, mem_free, mem; | ||
645 | int ret = -1; | ||
646 | |||
647 | sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); | ||
648 | fp = fopen(str, "r"); | ||
649 | if (!fp) | ||
650 | return -1; | ||
651 | |||
652 | while (getline(&buf, &len, fp) > 0) { | ||
653 | /* skip over invalid lines */ | ||
654 | if (!strchr(buf, ':')) | ||
655 | continue; | ||
656 | if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) | ||
657 | goto done; | ||
658 | if (!strcmp(field, "MemTotal:")) | ||
659 | mem_total = mem; | ||
660 | if (!strcmp(field, "MemFree:")) | ||
661 | mem_free = mem; | ||
662 | } | ||
663 | |||
664 | fclose(fp); | ||
665 | |||
666 | ret = do_write(fd, &mem_total, sizeof(u64)); | ||
667 | if (ret) | ||
668 | goto done; | ||
669 | |||
670 | ret = do_write(fd, &mem_free, sizeof(u64)); | ||
671 | if (ret) | ||
672 | goto done; | ||
673 | |||
674 | ret = -1; | ||
675 | sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); | ||
676 | |||
677 | fp = fopen(str, "r"); | ||
678 | if (!fp) | ||
679 | goto done; | ||
680 | |||
681 | if (getline(&buf, &len, fp) <= 0) | ||
682 | goto done; | ||
683 | |||
684 | p = strchr(buf, '\n'); | ||
685 | if (p) | ||
686 | *p = '\0'; | ||
687 | |||
688 | ret = do_write_string(fd, buf); | ||
689 | done: | ||
690 | free(buf); | ||
691 | fclose(fp); | ||
692 | return ret; | ||
693 | } | ||
694 | |||
695 | static int write_numa_topology(int fd, struct perf_header *h __used, | ||
696 | struct perf_evlist *evlist __used) | ||
697 | { | ||
698 | char *buf = NULL; | ||
699 | size_t len = 0; | ||
700 | FILE *fp; | ||
701 | struct cpu_map *node_map = NULL; | ||
702 | char *c; | ||
703 | u32 nr, i, j; | ||
704 | int ret = -1; | ||
705 | |||
706 | fp = fopen("/sys/devices/system/node/online", "r"); | ||
707 | if (!fp) | ||
708 | return -1; | ||
709 | |||
710 | if (getline(&buf, &len, fp) <= 0) | ||
711 | goto done; | ||
712 | |||
713 | c = strchr(buf, '\n'); | ||
714 | if (c) | ||
715 | *c = '\0'; | ||
716 | |||
717 | node_map = cpu_map__new(buf); | ||
718 | if (!node_map) | ||
719 | goto done; | ||
720 | |||
721 | nr = (u32)node_map->nr; | ||
722 | |||
723 | ret = do_write(fd, &nr, sizeof(nr)); | ||
724 | if (ret < 0) | ||
725 | goto done; | ||
726 | |||
727 | for (i = 0; i < nr; i++) { | ||
728 | j = (u32)node_map->map[i]; | ||
729 | ret = do_write(fd, &j, sizeof(j)); | ||
730 | if (ret < 0) | ||
731 | break; | ||
732 | |||
733 | ret = write_topo_node(fd, i); | ||
734 | if (ret < 0) | ||
735 | break; | ||
736 | } | ||
737 | done: | ||
738 | free(buf); | ||
739 | fclose(fp); | ||
740 | free(node_map); | ||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * default get_cpuid(): nothing gets recorded | ||
746 | * actual implementation must be in arch/$(ARCH)/util/header.c | ||
747 | */ | ||
748 | int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) | ||
749 | { | ||
750 | return -1; | ||
751 | } | ||
752 | |||
753 | static int write_cpuid(int fd, struct perf_header *h __used, | ||
754 | struct perf_evlist *evlist __used) | ||
755 | { | ||
756 | char buffer[64]; | ||
757 | int ret; | ||
758 | |||
759 | ret = get_cpuid(buffer, sizeof(buffer)); | ||
760 | if (!ret) | ||
761 | goto write_it; | ||
762 | |||
763 | return -1; | ||
764 | write_it: | ||
765 | return do_write_string(fd, buffer); | ||
766 | } | ||
767 | |||
768 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | ||
769 | { | ||
770 | char *str = do_read_string(fd, ph); | ||
771 | fprintf(fp, "# hostname : %s\n", str); | ||
772 | free(str); | ||
773 | } | ||
774 | |||
775 | static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) | ||
776 | { | ||
777 | char *str = do_read_string(fd, ph); | ||
778 | fprintf(fp, "# os release : %s\n", str); | ||
779 | free(str); | ||
780 | } | ||
781 | |||
782 | static void print_arch(struct perf_header *ph, int fd, FILE *fp) | ||
783 | { | ||
784 | char *str = do_read_string(fd, ph); | ||
785 | fprintf(fp, "# arch : %s\n", str); | ||
786 | free(str); | ||
787 | } | ||
788 | |||
789 | static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) | ||
790 | { | ||
791 | char *str = do_read_string(fd, ph); | ||
792 | fprintf(fp, "# cpudesc : %s\n", str); | ||
793 | free(str); | ||
794 | } | ||
795 | |||
796 | static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) | ||
797 | { | ||
798 | ssize_t ret; | ||
799 | u32 nr; | ||
800 | |||
801 | ret = read(fd, &nr, sizeof(nr)); | ||
802 | if (ret != (ssize_t)sizeof(nr)) | ||
803 | nr = -1; /* interpreted as error */ | ||
804 | |||
805 | if (ph->needs_swap) | ||
806 | nr = bswap_32(nr); | ||
807 | |||
808 | fprintf(fp, "# nrcpus online : %u\n", nr); | ||
809 | |||
810 | ret = read(fd, &nr, sizeof(nr)); | ||
811 | if (ret != (ssize_t)sizeof(nr)) | ||
812 | nr = -1; /* interpreted as error */ | ||
813 | |||
814 | if (ph->needs_swap) | ||
815 | nr = bswap_32(nr); | ||
816 | |||
817 | fprintf(fp, "# nrcpus avail : %u\n", nr); | ||
818 | } | ||
819 | |||
820 | static void print_version(struct perf_header *ph, int fd, FILE *fp) | ||
821 | { | ||
822 | char *str = do_read_string(fd, ph); | ||
823 | fprintf(fp, "# perf version : %s\n", str); | ||
824 | free(str); | ||
825 | } | ||
826 | |||
827 | static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) | ||
828 | { | ||
829 | ssize_t ret; | ||
830 | char *str; | ||
831 | u32 nr, i; | ||
832 | |||
833 | ret = read(fd, &nr, sizeof(nr)); | ||
834 | if (ret != (ssize_t)sizeof(nr)) | ||
835 | return; | ||
836 | |||
837 | if (ph->needs_swap) | ||
838 | nr = bswap_32(nr); | ||
839 | |||
840 | fprintf(fp, "# cmdline : "); | ||
841 | |||
842 | for (i = 0; i < nr; i++) { | ||
843 | str = do_read_string(fd, ph); | ||
844 | fprintf(fp, "%s ", str); | ||
845 | free(str); | ||
846 | } | ||
847 | fputc('\n', fp); | ||
848 | } | ||
849 | |||
850 | static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | ||
851 | { | ||
852 | ssize_t ret; | ||
853 | u32 nr, i; | ||
854 | char *str; | ||
855 | |||
856 | ret = read(fd, &nr, sizeof(nr)); | ||
857 | if (ret != (ssize_t)sizeof(nr)) | ||
858 | return; | ||
859 | |||
860 | if (ph->needs_swap) | ||
861 | nr = bswap_32(nr); | ||
862 | |||
863 | for (i = 0; i < nr; i++) { | ||
864 | str = do_read_string(fd, ph); | ||
865 | fprintf(fp, "# sibling cores : %s\n", str); | ||
866 | free(str); | ||
867 | } | ||
868 | |||
869 | ret = read(fd, &nr, sizeof(nr)); | ||
870 | if (ret != (ssize_t)sizeof(nr)) | ||
871 | return; | ||
872 | |||
873 | if (ph->needs_swap) | ||
874 | nr = bswap_32(nr); | ||
875 | |||
876 | for (i = 0; i < nr; i++) { | ||
877 | str = do_read_string(fd, ph); | ||
878 | fprintf(fp, "# sibling threads : %s\n", str); | ||
879 | free(str); | ||
880 | } | ||
881 | } | ||
882 | |||
883 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
884 | { | ||
885 | struct perf_event_attr attr; | ||
886 | uint64_t id; | ||
887 | void *buf = NULL; | ||
888 | char *str; | ||
889 | u32 nre, sz, nr, i, j, msz; | ||
890 | int ret; | ||
891 | |||
892 | /* number of events */ | ||
893 | ret = read(fd, &nre, sizeof(nre)); | ||
894 | if (ret != (ssize_t)sizeof(nre)) | ||
895 | goto error; | ||
896 | |||
897 | if (ph->needs_swap) | ||
898 | nre = bswap_32(nre); | ||
899 | |||
900 | ret = read(fd, &sz, sizeof(sz)); | ||
901 | if (ret != (ssize_t)sizeof(sz)) | ||
902 | goto error; | ||
903 | |||
904 | if (ph->needs_swap) | ||
905 | sz = bswap_32(sz); | ||
906 | |||
907 | /* | ||
908 | * ensure it is at least to our ABI rev | ||
909 | */ | ||
910 | if (sz < (u32)sizeof(attr)) | ||
911 | goto error; | ||
912 | |||
913 | memset(&attr, 0, sizeof(attr)); | ||
914 | |||
915 | /* read entire region to sync up to next field */ | ||
916 | buf = malloc(sz); | ||
917 | if (!buf) | ||
918 | goto error; | ||
919 | |||
920 | msz = sizeof(attr); | ||
921 | if (sz < msz) | ||
922 | msz = sz; | ||
923 | |||
924 | for (i = 0 ; i < nre; i++) { | ||
925 | |||
926 | ret = read(fd, buf, sz); | ||
927 | if (ret != (ssize_t)sz) | ||
928 | goto error; | ||
929 | |||
930 | if (ph->needs_swap) | ||
931 | perf_event__attr_swap(buf); | ||
932 | |||
933 | memcpy(&attr, buf, msz); | ||
934 | |||
935 | ret = read(fd, &nr, sizeof(nr)); | ||
936 | if (ret != (ssize_t)sizeof(nr)) | ||
937 | goto error; | ||
938 | |||
939 | if (ph->needs_swap) | ||
940 | nr = bswap_32(nr); | ||
941 | |||
942 | str = do_read_string(fd, ph); | ||
943 | fprintf(fp, "# event : name = %s, ", str); | ||
944 | free(str); | ||
945 | |||
946 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | ||
947 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | ||
948 | attr.type, | ||
949 | (u64)attr.config, | ||
950 | (u64)attr.config1, | ||
951 | (u64)attr.config2); | ||
952 | |||
953 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | ||
954 | attr.exclude_user, | ||
955 | attr.exclude_kernel); | ||
956 | |||
957 | if (nr) | ||
958 | fprintf(fp, ", id = {"); | ||
959 | |||
960 | for (j = 0 ; j < nr; j++) { | ||
961 | ret = read(fd, &id, sizeof(id)); | ||
962 | if (ret != (ssize_t)sizeof(id)) | ||
963 | goto error; | ||
964 | |||
965 | if (ph->needs_swap) | ||
966 | id = bswap_64(id); | ||
967 | |||
968 | if (j) | ||
969 | fputc(',', fp); | ||
970 | |||
971 | fprintf(fp, " %"PRIu64, id); | ||
972 | } | ||
973 | if (nr && j == nr) | ||
974 | fprintf(fp, " }"); | ||
975 | fputc('\n', fp); | ||
976 | } | ||
977 | free(buf); | ||
978 | return; | ||
979 | error: | ||
980 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
981 | } | ||
982 | |||
983 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | ||
984 | { | ||
985 | uint64_t mem; | ||
986 | ssize_t ret; | ||
987 | |||
988 | ret = read(fd, &mem, sizeof(mem)); | ||
989 | if (ret != sizeof(mem)) | ||
990 | goto error; | ||
991 | |||
992 | if (h->needs_swap) | ||
993 | mem = bswap_64(mem); | ||
994 | |||
995 | fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); | ||
996 | return; | ||
997 | error: | ||
998 | fprintf(fp, "# total memory : unknown\n"); | ||
999 | } | ||
1000 | |||
1001 | static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) | ||
1002 | { | ||
1003 | ssize_t ret; | ||
1004 | u32 nr, c, i; | ||
1005 | char *str; | ||
1006 | uint64_t mem_total, mem_free; | ||
1007 | |||
1008 | /* nr nodes */ | ||
1009 | ret = read(fd, &nr, sizeof(nr)); | ||
1010 | if (ret != (ssize_t)sizeof(nr)) | ||
1011 | goto error; | ||
1012 | |||
1013 | if (h->needs_swap) | ||
1014 | nr = bswap_32(nr); | ||
1015 | |||
1016 | for (i = 0; i < nr; i++) { | ||
1017 | |||
1018 | /* node number */ | ||
1019 | ret = read(fd, &c, sizeof(c)); | ||
1020 | if (ret != (ssize_t)sizeof(c)) | ||
1021 | goto error; | ||
1022 | |||
1023 | if (h->needs_swap) | ||
1024 | c = bswap_32(c); | ||
1025 | |||
1026 | ret = read(fd, &mem_total, sizeof(u64)); | ||
1027 | if (ret != sizeof(u64)) | ||
1028 | goto error; | ||
1029 | |||
1030 | ret = read(fd, &mem_free, sizeof(u64)); | ||
1031 | if (ret != sizeof(u64)) | ||
1032 | goto error; | ||
1033 | |||
1034 | if (h->needs_swap) { | ||
1035 | mem_total = bswap_64(mem_total); | ||
1036 | mem_free = bswap_64(mem_free); | ||
1037 | } | ||
1038 | |||
1039 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," | ||
1040 | " free = %"PRIu64" kB\n", | ||
1041 | c, | ||
1042 | mem_total, | ||
1043 | mem_free); | ||
1044 | |||
1045 | str = do_read_string(fd, h); | ||
1046 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | ||
1047 | free(str); | ||
1048 | } | ||
1049 | return; | ||
1050 | error: | ||
1051 | fprintf(fp, "# numa topology : not available\n"); | ||
1052 | } | ||
1053 | |||
1054 | static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | ||
1055 | { | ||
1056 | char *str = do_read_string(fd, ph); | ||
1057 | fprintf(fp, "# cpuid : %s\n", str); | ||
1058 | free(str); | ||
1059 | } | ||
1060 | |||
1061 | struct feature_ops { | ||
1062 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | ||
1063 | void (*print)(struct perf_header *h, int fd, FILE *fp); | ||
1064 | const char *name; | ||
1065 | bool full_only; | ||
1066 | }; | ||
1067 | |||
1068 | #define FEAT_OPA(n, w, p) \ | ||
1069 | [n] = { .name = #n, .write = w, .print = p } | ||
1070 | #define FEAT_OPF(n, w, p) \ | ||
1071 | [n] = { .name = #n, .write = w, .print = p, .full_only = true } | ||
1072 | |||
1073 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | ||
1074 | FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL), | ||
1075 | FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL), | ||
1076 | FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname), | ||
1077 | FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease), | ||
1078 | FEAT_OPA(HEADER_VERSION, write_version, print_version), | ||
1079 | FEAT_OPA(HEADER_ARCH, write_arch, print_arch), | ||
1080 | FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus), | ||
1081 | FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc), | ||
1082 | FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid), | ||
1083 | FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem), | ||
1084 | FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc), | ||
1085 | FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline), | ||
1086 | FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology), | ||
1087 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology), | ||
1088 | }; | ||
1089 | |||
1090 | struct header_print_data { | ||
1091 | FILE *fp; | ||
1092 | bool full; /* extended list of headers */ | ||
1093 | }; | ||
1094 | |||
1095 | static int perf_file_section__fprintf_info(struct perf_file_section *section, | ||
1096 | struct perf_header *ph, | ||
1097 | int feat, int fd, void *data) | ||
1098 | { | ||
1099 | struct header_print_data *hd = data; | ||
1100 | |||
1101 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | ||
1102 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | ||
1103 | "%d, continuing...\n", section->offset, feat); | ||
1104 | return 0; | ||
1105 | } | ||
1106 | if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) { | ||
1107 | pr_warning("unknown feature %d\n", feat); | ||
1108 | return -1; | ||
1109 | } | ||
1110 | if (!feat_ops[feat].print) | ||
1111 | return 0; | ||
1112 | |||
1113 | if (!feat_ops[feat].full_only || hd->full) | ||
1114 | feat_ops[feat].print(ph, fd, hd->fp); | ||
1115 | else | ||
1116 | fprintf(hd->fp, "# %s info available, use -I to display\n", | ||
1117 | feat_ops[feat].name); | ||
1118 | |||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | ||
1123 | { | ||
1124 | struct header_print_data hd; | ||
1125 | struct perf_header *header = &session->header; | ||
1126 | int fd = session->fd; | ||
1127 | hd.fp = fp; | ||
1128 | hd.full = full; | ||
1129 | |||
1130 | perf_header__process_sections(header, fd, &hd, | ||
1131 | perf_file_section__fprintf_info); | ||
1132 | return 0; | ||
1133 | } | ||
1134 | |||
113 | #define dsos__for_each_with_build_id(pos, head) \ | 1135 | #define dsos__for_each_with_build_id(pos, head) \ |
114 | list_for_each_entry(pos, head, node) \ | 1136 | list_for_each_entry(pos, head, node) \ |
115 | if (!pos->has_build_id) \ | 1137 | if (!pos->has_build_id) \ |
@@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | |||
267 | if (access(linkname, F_OK)) | 1289 | if (access(linkname, F_OK)) |
268 | goto out_free; | 1290 | goto out_free; |
269 | 1291 | ||
270 | if (readlink(linkname, filename, size) < 0) | 1292 | if (readlink(linkname, filename, size - 1) < 0) |
271 | goto out_free; | 1293 | goto out_free; |
272 | 1294 | ||
273 | if (unlink(linkname)) | 1295 | if (unlink(linkname)) |
@@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with | |||
356 | return ret; | 1378 | return ret; |
357 | } | 1379 | } |
358 | 1380 | ||
1381 | static int do_write_feat(int fd, struct perf_header *h, int type, | ||
1382 | struct perf_file_section **p, | ||
1383 | struct perf_evlist *evlist) | ||
1384 | { | ||
1385 | int err; | ||
1386 | int ret = 0; | ||
1387 | |||
1388 | if (perf_header__has_feat(h, type)) { | ||
1389 | |||
1390 | (*p)->offset = lseek(fd, 0, SEEK_CUR); | ||
1391 | |||
1392 | err = feat_ops[type].write(fd, h, evlist); | ||
1393 | if (err < 0) { | ||
1394 | pr_debug("failed to write feature %d\n", type); | ||
1395 | |||
1396 | /* undo anything written */ | ||
1397 | lseek(fd, (*p)->offset, SEEK_SET); | ||
1398 | |||
1399 | return -1; | ||
1400 | } | ||
1401 | (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; | ||
1402 | (*p)++; | ||
1403 | } | ||
1404 | return ret; | ||
1405 | } | ||
1406 | |||
359 | static int perf_header__adds_write(struct perf_header *header, | 1407 | static int perf_header__adds_write(struct perf_header *header, |
360 | struct perf_evlist *evlist, int fd) | 1408 | struct perf_evlist *evlist, int fd) |
361 | { | 1409 | { |
362 | int nr_sections; | 1410 | int nr_sections; |
363 | struct perf_session *session; | 1411 | struct perf_session *session; |
364 | struct perf_file_section *feat_sec; | 1412 | struct perf_file_section *feat_sec, *p; |
365 | int sec_size; | 1413 | int sec_size; |
366 | u64 sec_start; | 1414 | u64 sec_start; |
367 | int idx = 0, err; | 1415 | int err; |
368 | 1416 | ||
369 | session = container_of(header, struct perf_session, header); | 1417 | session = container_of(header, struct perf_session, header); |
370 | 1418 | ||
@@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
376 | if (!nr_sections) | 1424 | if (!nr_sections) |
377 | return 0; | 1425 | return 0; |
378 | 1426 | ||
379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 1427 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); |
380 | if (feat_sec == NULL) | 1428 | if (feat_sec == NULL) |
381 | return -ENOMEM; | 1429 | return -ENOMEM; |
382 | 1430 | ||
@@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header, | |||
385 | sec_start = header->data_offset + header->data_size; | 1433 | sec_start = header->data_offset + header->data_size; |
386 | lseek(fd, sec_start + sec_size, SEEK_SET); | 1434 | lseek(fd, sec_start + sec_size, SEEK_SET); |
387 | 1435 | ||
388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { | 1436 | err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist); |
389 | struct perf_file_section *trace_sec; | 1437 | if (err) |
390 | 1438 | goto out_free; | |
391 | trace_sec = &feat_sec[idx++]; | ||
392 | 1439 | ||
393 | /* Write trace info */ | 1440 | err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist); |
394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 1441 | if (err) { |
395 | read_tracing_data(fd, &evlist->entries); | 1442 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 1443 | goto out_free; |
397 | } | 1444 | } |
398 | 1445 | ||
399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { | 1446 | err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist); |
400 | struct perf_file_section *buildid_sec; | 1447 | if (err) |
1448 | perf_header__clear_feat(header, HEADER_HOSTNAME); | ||
401 | 1449 | ||
402 | buildid_sec = &feat_sec[idx++]; | 1450 | err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist); |
1451 | if (err) | ||
1452 | perf_header__clear_feat(header, HEADER_OSRELEASE); | ||
403 | 1453 | ||
404 | /* Write build-ids */ | 1454 | err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist); |
405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 1455 | if (err) |
406 | err = dsos__write_buildid_table(header, fd); | 1456 | perf_header__clear_feat(header, HEADER_VERSION); |
407 | if (err < 0) { | 1457 | |
408 | pr_debug("failed to write buildid table\n"); | 1458 | err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist); |
409 | goto out_free; | 1459 | if (err) |
410 | } | 1460 | perf_header__clear_feat(header, HEADER_ARCH); |
411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 1461 | |
412 | buildid_sec->offset; | 1462 | err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist); |
413 | if (!no_buildid_cache) | 1463 | if (err) |
414 | perf_session__cache_build_ids(session); | 1464 | perf_header__clear_feat(header, HEADER_NRCPUS); |
415 | } | 1465 | |
1466 | err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist); | ||
1467 | if (err) | ||
1468 | perf_header__clear_feat(header, HEADER_CPUDESC); | ||
1469 | |||
1470 | err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist); | ||
1471 | if (err) | ||
1472 | perf_header__clear_feat(header, HEADER_CPUID); | ||
1473 | |||
1474 | err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist); | ||
1475 | if (err) | ||
1476 | perf_header__clear_feat(header, HEADER_TOTAL_MEM); | ||
1477 | |||
1478 | err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist); | ||
1479 | if (err) | ||
1480 | perf_header__clear_feat(header, HEADER_CMDLINE); | ||
1481 | |||
1482 | err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist); | ||
1483 | if (err) | ||
1484 | perf_header__clear_feat(header, HEADER_EVENT_DESC); | ||
1485 | |||
1486 | err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist); | ||
1487 | if (err) | ||
1488 | perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY); | ||
1489 | |||
1490 | err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist); | ||
1491 | if (err) | ||
1492 | perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY); | ||
416 | 1493 | ||
417 | lseek(fd, sec_start, SEEK_SET); | 1494 | lseek(fd, sec_start, SEEK_SET); |
1495 | /* | ||
1496 | * may write more than needed due to dropped feature, but | ||
1497 | * this is okay, reader will skip the mising entries | ||
1498 | */ | ||
418 | err = do_write(fd, feat_sec, sec_size); | 1499 | err = do_write(fd, feat_sec, sec_size); |
419 | if (err < 0) | 1500 | if (err < 0) |
420 | pr_debug("failed to write feature section\n"); | 1501 | pr_debug("failed to write feature section\n"); |
@@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header, | |||
554 | } | 1635 | } |
555 | 1636 | ||
556 | int perf_header__process_sections(struct perf_header *header, int fd, | 1637 | int perf_header__process_sections(struct perf_header *header, int fd, |
1638 | void *data, | ||
557 | int (*process)(struct perf_file_section *section, | 1639 | int (*process)(struct perf_file_section *section, |
558 | struct perf_header *ph, | 1640 | struct perf_header *ph, |
559 | int feat, int fd)) | 1641 | int feat, int fd, void *data)) |
560 | { | 1642 | { |
561 | struct perf_file_section *feat_sec; | 1643 | struct perf_file_section *feat_sec; |
562 | int nr_sections; | 1644 | int nr_sections; |
@@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
584 | if (perf_header__has_feat(header, feat)) { | 1666 | if (perf_header__has_feat(header, feat)) { |
585 | struct perf_file_section *sec = &feat_sec[idx++]; | 1667 | struct perf_file_section *sec = &feat_sec[idx++]; |
586 | 1668 | ||
587 | err = process(sec, header, feat, fd); | 1669 | err = process(sec, header, feat, fd, data); |
588 | if (err < 0) | 1670 | if (err < 0) |
589 | break; | 1671 | break; |
590 | } | 1672 | } |
@@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header, | |||
621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | 1703 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
622 | else | 1704 | else |
623 | return -1; | 1705 | return -1; |
1706 | } else if (ph->needs_swap) { | ||
1707 | unsigned int i; | ||
1708 | /* | ||
1709 | * feature bitmap is declared as an array of unsigned longs -- | ||
1710 | * not good since its size can differ between the host that | ||
1711 | * generated the data file and the host analyzing the file. | ||
1712 | * | ||
1713 | * We need to handle endianness, but we don't know the size of | ||
1714 | * the unsigned long where the file was generated. Take a best | ||
1715 | * guess at determining it: try 64-bit swap first (ie., file | ||
1716 | * created on a 64-bit host), and check if the hostname feature | ||
1717 | * bit is set (this feature bit is forced on as of fbe96f2). | ||
1718 | * If the bit is not, undo the 64-bit swap and try a 32-bit | ||
1719 | * swap. If the hostname bit is still not set (e.g., older data | ||
1720 | * file), punt and fallback to the original behavior -- | ||
1721 | * clearing all feature bits and setting buildid. | ||
1722 | */ | ||
1723 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) | ||
1724 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
1725 | |||
1726 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
1727 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { | ||
1728 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
1729 | header->adds_features[i] = bswap_32(header->adds_features[i]); | ||
1730 | } | ||
1731 | } | ||
1732 | |||
1733 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
1734 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | ||
1735 | set_bit(HEADER_BUILD_ID, header->adds_features); | ||
1736 | } | ||
624 | } | 1737 | } |
625 | 1738 | ||
626 | memcpy(&ph->adds_features, &header->adds_features, | 1739 | memcpy(&ph->adds_features, &header->adds_features, |
627 | sizeof(ph->adds_features)); | 1740 | sizeof(ph->adds_features)); |
628 | /* | ||
629 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
630 | * may be coming from an arch with a different word-size, ergo different | ||
631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
632 | * safe to assume that we have a build-id section. Trace files probably | ||
633 | * have several other issues in this realm anyway... | ||
634 | */ | ||
635 | if (ph->needs_swap) { | ||
636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
637 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
638 | } | ||
639 | 1741 | ||
640 | ph->event_offset = header->event_types.offset; | 1742 | ph->event_offset = header->event_types.offset; |
641 | ph->event_size = header->event_types.size; | 1743 | ph->event_size = header->event_types.size; |
@@ -796,7 +1898,7 @@ out: | |||
796 | 1898 | ||
797 | static int perf_file_section__process(struct perf_file_section *section, | 1899 | static int perf_file_section__process(struct perf_file_section *section, |
798 | struct perf_header *ph, | 1900 | struct perf_header *ph, |
799 | int feat, int fd) | 1901 | int feat, int fd, void *data __used) |
800 | { | 1902 | { |
801 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 1903 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
802 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 1904 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
@@ -813,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
813 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 1915 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
814 | pr_debug("Failed to read buildids, continuing...\n"); | 1916 | pr_debug("Failed to read buildids, continuing...\n"); |
815 | break; | 1917 | break; |
1918 | |||
1919 | case HEADER_HOSTNAME: | ||
1920 | case HEADER_OSRELEASE: | ||
1921 | case HEADER_VERSION: | ||
1922 | case HEADER_ARCH: | ||
1923 | case HEADER_NRCPUS: | ||
1924 | case HEADER_CPUDESC: | ||
1925 | case HEADER_CPUID: | ||
1926 | case HEADER_TOTAL_MEM: | ||
1927 | case HEADER_CMDLINE: | ||
1928 | case HEADER_EVENT_DESC: | ||
1929 | case HEADER_CPU_TOPOLOGY: | ||
1930 | case HEADER_NUMA_TOPOLOGY: | ||
1931 | break; | ||
1932 | |||
816 | default: | 1933 | default: |
817 | pr_debug("unknown feature %d, continuing...\n", feat); | 1934 | pr_debug("unknown feature %d, continuing...\n", feat); |
818 | } | 1935 | } |
@@ -935,7 +2052,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
935 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 2052 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
936 | } | 2053 | } |
937 | 2054 | ||
938 | perf_header__process_sections(header, fd, perf_file_section__process); | 2055 | perf_header__process_sections(header, fd, NULL, |
2056 | perf_file_section__process); | ||
939 | 2057 | ||
940 | lseek(fd, header->data_offset, SEEK_SET); | 2058 | lseek(fd, header->data_offset, SEEK_SET); |
941 | 2059 | ||
@@ -1100,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
1100 | struct perf_session *session __unused) | 2218 | struct perf_session *session __unused) |
1101 | { | 2219 | { |
1102 | union perf_event ev; | 2220 | union perf_event ev; |
2221 | struct tracing_data *tdata; | ||
1103 | ssize_t size = 0, aligned_size = 0, padding; | 2222 | ssize_t size = 0, aligned_size = 0, padding; |
1104 | int err __used = 0; | 2223 | int err __used = 0; |
1105 | 2224 | ||
2225 | /* | ||
2226 | * We are going to store the size of the data followed | ||
2227 | * by the data contents. Since the fd descriptor is a pipe, | ||
2228 | * we cannot seek back to store the size of the data once | ||
2229 | * we know it. Instead we: | ||
2230 | * | ||
2231 | * - write the tracing data to the temp file | ||
2232 | * - get/write the data size to pipe | ||
2233 | * - write the tracing data from the temp file | ||
2234 | * to the pipe | ||
2235 | */ | ||
2236 | tdata = tracing_data_get(&evlist->entries, fd, true); | ||
2237 | if (!tdata) | ||
2238 | return -1; | ||
2239 | |||
1106 | memset(&ev, 0, sizeof(ev)); | 2240 | memset(&ev, 0, sizeof(ev)); |
1107 | 2241 | ||
1108 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 2242 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1109 | size = read_tracing_data_size(fd, &evlist->entries); | 2243 | size = tdata->size; |
1110 | if (size <= 0) | ||
1111 | return size; | ||
1112 | aligned_size = ALIGN(size, sizeof(u64)); | 2244 | aligned_size = ALIGN(size, sizeof(u64)); |
1113 | padding = aligned_size - size; | 2245 | padding = aligned_size - size; |
1114 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 2246 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
@@ -1116,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
1116 | 2248 | ||
1117 | process(&ev, NULL, session); | 2249 | process(&ev, NULL, session); |
1118 | 2250 | ||
1119 | err = read_tracing_data(fd, &evlist->entries); | 2251 | /* |
2252 | * The put function will copy all the tracing data | ||
2253 | * stored in temp file to the pipe. | ||
2254 | */ | ||
2255 | tracing_data_put(tdata); | ||
2256 | |||
1120 | write_padded(fd, NULL, 0, padding); | 2257 | write_padded(fd, NULL, 0, padding); |
1121 | 2258 | ||
1122 | return aligned_size; | 2259 | return aligned_size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1886256768a1..3d5a742f4a2a 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -12,6 +12,20 @@ | |||
12 | enum { | 12 | enum { |
13 | HEADER_TRACE_INFO = 1, | 13 | HEADER_TRACE_INFO = 1, |
14 | HEADER_BUILD_ID, | 14 | HEADER_BUILD_ID, |
15 | |||
16 | HEADER_HOSTNAME, | ||
17 | HEADER_OSRELEASE, | ||
18 | HEADER_VERSION, | ||
19 | HEADER_ARCH, | ||
20 | HEADER_NRCPUS, | ||
21 | HEADER_CPUDESC, | ||
22 | HEADER_CPUID, | ||
23 | HEADER_TOTAL_MEM, | ||
24 | HEADER_CMDLINE, | ||
25 | HEADER_EVENT_DESC, | ||
26 | HEADER_CPU_TOPOLOGY, | ||
27 | HEADER_NUMA_TOPOLOGY, | ||
28 | |||
15 | HEADER_LAST_FEATURE, | 29 | HEADER_LAST_FEATURE, |
16 | }; | 30 | }; |
17 | 31 | ||
@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat); | |||
68 | void perf_header__clear_feat(struct perf_header *header, int feat); | 82 | void perf_header__clear_feat(struct perf_header *header, int feat); |
69 | bool perf_header__has_feat(const struct perf_header *header, int feat); | 83 | bool perf_header__has_feat(const struct perf_header *header, int feat); |
70 | 84 | ||
85 | int perf_header__set_cmdline(int argc, const char **argv); | ||
86 | |||
71 | int perf_header__process_sections(struct perf_header *header, int fd, | 87 | int perf_header__process_sections(struct perf_header *header, int fd, |
88 | void *data, | ||
72 | int (*process)(struct perf_file_section *section, | 89 | int (*process)(struct perf_file_section *section, |
73 | struct perf_header *ph, | 90 | struct perf_header *ph, |
74 | int feat, int fd)); | 91 | int feat, int fd, void *data)); |
92 | |||
93 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
75 | 94 | ||
76 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 95 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
77 | const char *name, bool is_kallsyms); | 96 | const char *name, bool is_kallsyms); |
@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | |||
104 | struct perf_session *session); | 123 | struct perf_session *session); |
105 | int perf_event__process_build_id(union perf_event *event, | 124 | int perf_event__process_build_id(union perf_event *event, |
106 | struct perf_session *session); | 125 | struct perf_session *session); |
126 | |||
127 | /* | ||
128 | * arch specific callback | ||
129 | */ | ||
130 | int get_cpuid(char *buffer, size_t sz); | ||
131 | |||
107 | #endif /* __PERF_HEADER_H */ | 132 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 677e1da6bb3e..f6a993963a1e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -6,6 +6,11 @@ | |||
6 | #include "sort.h" | 6 | #include "sort.h" |
7 | #include <math.h> | 7 | #include <math.h> |
8 | 8 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
10 | struct hist_entry *he); | ||
11 | static bool hists__filter_entry_by_thread(struct hists *hists, | ||
12 | struct hist_entry *he); | ||
13 | |||
9 | enum hist_filter { | 14 | enum hist_filter { |
10 | HIST_FILTER__DSO, | 15 | HIST_FILTER__DSO, |
11 | HIST_FILTER__THREAD, | 16 | HIST_FILTER__THREAD, |
@@ -18,56 +23,56 @@ struct callchain_param callchain_param = { | |||
18 | .order = ORDER_CALLEE | 23 | .order = ORDER_CALLEE |
19 | }; | 24 | }; |
20 | 25 | ||
21 | u16 hists__col_len(struct hists *self, enum hist_column col) | 26 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
22 | { | 27 | { |
23 | return self->col_len[col]; | 28 | return hists->col_len[col]; |
24 | } | 29 | } |
25 | 30 | ||
26 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | 31 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len) |
27 | { | 32 | { |
28 | self->col_len[col] = len; | 33 | hists->col_len[col] = len; |
29 | } | 34 | } |
30 | 35 | ||
31 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | 36 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) |
32 | { | 37 | { |
33 | if (len > hists__col_len(self, col)) { | 38 | if (len > hists__col_len(hists, col)) { |
34 | hists__set_col_len(self, col, len); | 39 | hists__set_col_len(hists, col, len); |
35 | return true; | 40 | return true; |
36 | } | 41 | } |
37 | return false; | 42 | return false; |
38 | } | 43 | } |
39 | 44 | ||
40 | static void hists__reset_col_len(struct hists *self) | 45 | static void hists__reset_col_len(struct hists *hists) |
41 | { | 46 | { |
42 | enum hist_column col; | 47 | enum hist_column col; |
43 | 48 | ||
44 | for (col = 0; col < HISTC_NR_COLS; ++col) | 49 | for (col = 0; col < HISTC_NR_COLS; ++col) |
45 | hists__set_col_len(self, col, 0); | 50 | hists__set_col_len(hists, col, 0); |
46 | } | 51 | } |
47 | 52 | ||
48 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | 53 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
49 | { | 54 | { |
50 | u16 len; | 55 | u16 len; |
51 | 56 | ||
52 | if (h->ms.sym) | 57 | if (h->ms.sym) |
53 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 58 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); |
54 | else { | 59 | else { |
55 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 60 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
56 | 61 | ||
57 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | 62 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && |
58 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 63 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
59 | !symbol_conf.dso_list) | 64 | !symbol_conf.dso_list) |
60 | hists__set_col_len(self, HISTC_DSO, | 65 | hists__set_col_len(hists, HISTC_DSO, |
61 | unresolved_col_width); | 66 | unresolved_col_width); |
62 | } | 67 | } |
63 | 68 | ||
64 | len = thread__comm_len(h->thread); | 69 | len = thread__comm_len(h->thread); |
65 | if (hists__new_col_len(self, HISTC_COMM, len)) | 70 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
66 | hists__set_col_len(self, HISTC_THREAD, len + 6); | 71 | hists__set_col_len(hists, HISTC_THREAD, len + 6); |
67 | 72 | ||
68 | if (h->ms.map) { | 73 | if (h->ms.map) { |
69 | len = dso__name_len(h->ms.map->dso); | 74 | len = dso__name_len(h->ms.map->dso); |
70 | hists__new_col_len(self, HISTC_DSO, len); | 75 | hists__new_col_len(hists, HISTC_DSO, len); |
71 | } | 76 | } |
72 | } | 77 | } |
73 | 78 | ||
@@ -92,6 +97,67 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, | |||
92 | } | 97 | } |
93 | } | 98 | } |
94 | 99 | ||
100 | static void hist_entry__decay(struct hist_entry *he) | ||
101 | { | ||
102 | he->period = (he->period * 7) / 8; | ||
103 | he->nr_events = (he->nr_events * 7) / 8; | ||
104 | } | ||
105 | |||
106 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | ||
107 | { | ||
108 | u64 prev_period = he->period; | ||
109 | |||
110 | if (prev_period == 0) | ||
111 | return true; | ||
112 | |||
113 | hist_entry__decay(he); | ||
114 | |||
115 | if (!he->filtered) | ||
116 | hists->stats.total_period -= prev_period - he->period; | ||
117 | |||
118 | return he->period == 0; | ||
119 | } | ||
120 | |||
121 | static void __hists__decay_entries(struct hists *hists, bool zap_user, | ||
122 | bool zap_kernel, bool threaded) | ||
123 | { | ||
124 | struct rb_node *next = rb_first(&hists->entries); | ||
125 | struct hist_entry *n; | ||
126 | |||
127 | while (next) { | ||
128 | n = rb_entry(next, struct hist_entry, rb_node); | ||
129 | next = rb_next(&n->rb_node); | ||
130 | /* | ||
131 | * We may be annotating this, for instance, so keep it here in | ||
132 | * case some it gets new samples, we'll eventually free it when | ||
133 | * the user stops browsing and it agains gets fully decayed. | ||
134 | */ | ||
135 | if (((zap_user && n->level == '.') || | ||
136 | (zap_kernel && n->level != '.') || | ||
137 | hists__decay_entry(hists, n)) && | ||
138 | !n->used) { | ||
139 | rb_erase(&n->rb_node, &hists->entries); | ||
140 | |||
141 | if (sort__need_collapse || threaded) | ||
142 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
143 | |||
144 | hist_entry__free(n); | ||
145 | --hists->nr_entries; | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | ||
151 | { | ||
152 | return __hists__decay_entries(hists, zap_user, zap_kernel, false); | ||
153 | } | ||
154 | |||
155 | void hists__decay_entries_threaded(struct hists *hists, | ||
156 | bool zap_user, bool zap_kernel) | ||
157 | { | ||
158 | return __hists__decay_entries(hists, zap_user, zap_kernel, true); | ||
159 | } | ||
160 | |||
95 | /* | 161 | /* |
96 | * histogram, sorted on item, collects periods | 162 | * histogram, sorted on item, collects periods |
97 | */ | 163 | */ |
@@ -113,11 +179,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
113 | return self; | 179 | return self; |
114 | } | 180 | } |
115 | 181 | ||
116 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) | 182 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
117 | { | 183 | { |
118 | if (!h->filtered) { | 184 | if (!h->filtered) { |
119 | hists__calc_col_len(self, h); | 185 | hists__calc_col_len(hists, h); |
120 | ++self->nr_entries; | 186 | ++hists->nr_entries; |
187 | hists->stats.total_period += h->period; | ||
121 | } | 188 | } |
122 | } | 189 | } |
123 | 190 | ||
@@ -128,11 +195,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
128 | return 0; | 195 | return 0; |
129 | } | 196 | } |
130 | 197 | ||
131 | struct hist_entry *__hists__add_entry(struct hists *self, | 198 | struct hist_entry *__hists__add_entry(struct hists *hists, |
132 | struct addr_location *al, | 199 | struct addr_location *al, |
133 | struct symbol *sym_parent, u64 period) | 200 | struct symbol *sym_parent, u64 period) |
134 | { | 201 | { |
135 | struct rb_node **p = &self->entries.rb_node; | 202 | struct rb_node **p; |
136 | struct rb_node *parent = NULL; | 203 | struct rb_node *parent = NULL; |
137 | struct hist_entry *he; | 204 | struct hist_entry *he; |
138 | struct hist_entry entry = { | 205 | struct hist_entry entry = { |
@@ -150,9 +217,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
150 | }; | 217 | }; |
151 | int cmp; | 218 | int cmp; |
152 | 219 | ||
220 | pthread_mutex_lock(&hists->lock); | ||
221 | |||
222 | p = &hists->entries_in->rb_node; | ||
223 | |||
153 | while (*p != NULL) { | 224 | while (*p != NULL) { |
154 | parent = *p; | 225 | parent = *p; |
155 | he = rb_entry(parent, struct hist_entry, rb_node); | 226 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
156 | 227 | ||
157 | cmp = hist_entry__cmp(&entry, he); | 228 | cmp = hist_entry__cmp(&entry, he); |
158 | 229 | ||
@@ -170,12 +241,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
170 | 241 | ||
171 | he = hist_entry__new(&entry); | 242 | he = hist_entry__new(&entry); |
172 | if (!he) | 243 | if (!he) |
173 | return NULL; | 244 | goto out_unlock; |
174 | rb_link_node(&he->rb_node, parent, p); | 245 | |
175 | rb_insert_color(&he->rb_node, &self->entries); | 246 | rb_link_node(&he->rb_node_in, parent, p); |
176 | hists__inc_nr_entries(self, he); | 247 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
177 | out: | 248 | out: |
178 | hist_entry__add_cpumode_period(he, al->cpumode, period); | 249 | hist_entry__add_cpumode_period(he, al->cpumode, period); |
250 | out_unlock: | ||
251 | pthread_mutex_unlock(&hists->lock); | ||
179 | return he; | 252 | return he; |
180 | } | 253 | } |
181 | 254 | ||
@@ -222,7 +295,7 @@ void hist_entry__free(struct hist_entry *he) | |||
222 | * collapse the histogram | 295 | * collapse the histogram |
223 | */ | 296 | */ |
224 | 297 | ||
225 | static bool hists__collapse_insert_entry(struct hists *self, | 298 | static bool hists__collapse_insert_entry(struct hists *hists, |
226 | struct rb_root *root, | 299 | struct rb_root *root, |
227 | struct hist_entry *he) | 300 | struct hist_entry *he) |
228 | { | 301 | { |
@@ -233,15 +306,16 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
233 | 306 | ||
234 | while (*p != NULL) { | 307 | while (*p != NULL) { |
235 | parent = *p; | 308 | parent = *p; |
236 | iter = rb_entry(parent, struct hist_entry, rb_node); | 309 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
237 | 310 | ||
238 | cmp = hist_entry__collapse(iter, he); | 311 | cmp = hist_entry__collapse(iter, he); |
239 | 312 | ||
240 | if (!cmp) { | 313 | if (!cmp) { |
241 | iter->period += he->period; | 314 | iter->period += he->period; |
315 | iter->nr_events += he->nr_events; | ||
242 | if (symbol_conf.use_callchain) { | 316 | if (symbol_conf.use_callchain) { |
243 | callchain_cursor_reset(&self->callchain_cursor); | 317 | callchain_cursor_reset(&hists->callchain_cursor); |
244 | callchain_merge(&self->callchain_cursor, iter->callchain, | 318 | callchain_merge(&hists->callchain_cursor, iter->callchain, |
245 | he->callchain); | 319 | he->callchain); |
246 | } | 320 | } |
247 | hist_entry__free(he); | 321 | hist_entry__free(he); |
@@ -254,35 +328,70 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
254 | p = &(*p)->rb_right; | 328 | p = &(*p)->rb_right; |
255 | } | 329 | } |
256 | 330 | ||
257 | rb_link_node(&he->rb_node, parent, p); | 331 | rb_link_node(&he->rb_node_in, parent, p); |
258 | rb_insert_color(&he->rb_node, root); | 332 | rb_insert_color(&he->rb_node_in, root); |
259 | return true; | 333 | return true; |
260 | } | 334 | } |
261 | 335 | ||
262 | void hists__collapse_resort(struct hists *self) | 336 | static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
337 | { | ||
338 | struct rb_root *root; | ||
339 | |||
340 | pthread_mutex_lock(&hists->lock); | ||
341 | |||
342 | root = hists->entries_in; | ||
343 | if (++hists->entries_in > &hists->entries_in_array[1]) | ||
344 | hists->entries_in = &hists->entries_in_array[0]; | ||
345 | |||
346 | pthread_mutex_unlock(&hists->lock); | ||
347 | |||
348 | return root; | ||
349 | } | ||
350 | |||
351 | static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | ||
352 | { | ||
353 | hists__filter_entry_by_dso(hists, he); | ||
354 | hists__filter_entry_by_thread(hists, he); | ||
355 | } | ||
356 | |||
357 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | ||
263 | { | 358 | { |
264 | struct rb_root tmp; | 359 | struct rb_root *root; |
265 | struct rb_node *next; | 360 | struct rb_node *next; |
266 | struct hist_entry *n; | 361 | struct hist_entry *n; |
267 | 362 | ||
268 | if (!sort__need_collapse) | 363 | if (!sort__need_collapse && !threaded) |
269 | return; | 364 | return; |
270 | 365 | ||
271 | tmp = RB_ROOT; | 366 | root = hists__get_rotate_entries_in(hists); |
272 | next = rb_first(&self->entries); | 367 | next = rb_first(root); |
273 | self->nr_entries = 0; | 368 | hists->stats.total_period = 0; |
274 | hists__reset_col_len(self); | ||
275 | 369 | ||
276 | while (next) { | 370 | while (next) { |
277 | n = rb_entry(next, struct hist_entry, rb_node); | 371 | n = rb_entry(next, struct hist_entry, rb_node_in); |
278 | next = rb_next(&n->rb_node); | 372 | next = rb_next(&n->rb_node_in); |
279 | 373 | ||
280 | rb_erase(&n->rb_node, &self->entries); | 374 | rb_erase(&n->rb_node_in, root); |
281 | if (hists__collapse_insert_entry(self, &tmp, n)) | 375 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { |
282 | hists__inc_nr_entries(self, n); | 376 | /* |
377 | * If it wasn't combined with one of the entries already | ||
378 | * collapsed, we need to apply the filters that may have | ||
379 | * been set by, say, the hist_browser. | ||
380 | */ | ||
381 | hists__apply_filters(hists, n); | ||
382 | hists__inc_nr_entries(hists, n); | ||
383 | } | ||
283 | } | 384 | } |
385 | } | ||
284 | 386 | ||
285 | self->entries = tmp; | 387 | void hists__collapse_resort(struct hists *hists) |
388 | { | ||
389 | return __hists__collapse_resort(hists, false); | ||
390 | } | ||
391 | |||
392 | void hists__collapse_resort_threaded(struct hists *hists) | ||
393 | { | ||
394 | return __hists__collapse_resort(hists, true); | ||
286 | } | 395 | } |
287 | 396 | ||
288 | /* | 397 | /* |
@@ -315,31 +424,43 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
315 | rb_insert_color(&he->rb_node, entries); | 424 | rb_insert_color(&he->rb_node, entries); |
316 | } | 425 | } |
317 | 426 | ||
318 | void hists__output_resort(struct hists *self) | 427 | static void __hists__output_resort(struct hists *hists, bool threaded) |
319 | { | 428 | { |
320 | struct rb_root tmp; | 429 | struct rb_root *root; |
321 | struct rb_node *next; | 430 | struct rb_node *next; |
322 | struct hist_entry *n; | 431 | struct hist_entry *n; |
323 | u64 min_callchain_hits; | 432 | u64 min_callchain_hits; |
324 | 433 | ||
325 | min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); | 434 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); |
435 | |||
436 | if (sort__need_collapse || threaded) | ||
437 | root = &hists->entries_collapsed; | ||
438 | else | ||
439 | root = hists->entries_in; | ||
326 | 440 | ||
327 | tmp = RB_ROOT; | 441 | next = rb_first(root); |
328 | next = rb_first(&self->entries); | 442 | hists->entries = RB_ROOT; |
329 | 443 | ||
330 | self->nr_entries = 0; | 444 | hists->nr_entries = 0; |
331 | hists__reset_col_len(self); | 445 | hists__reset_col_len(hists); |
332 | 446 | ||
333 | while (next) { | 447 | while (next) { |
334 | n = rb_entry(next, struct hist_entry, rb_node); | 448 | n = rb_entry(next, struct hist_entry, rb_node_in); |
335 | next = rb_next(&n->rb_node); | 449 | next = rb_next(&n->rb_node_in); |
336 | 450 | ||
337 | rb_erase(&n->rb_node, &self->entries); | 451 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
338 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); | 452 | hists__inc_nr_entries(hists, n); |
339 | hists__inc_nr_entries(self, n); | ||
340 | } | 453 | } |
454 | } | ||
341 | 455 | ||
342 | self->entries = tmp; | 456 | void hists__output_resort(struct hists *hists) |
457 | { | ||
458 | return __hists__output_resort(hists, false); | ||
459 | } | ||
460 | |||
461 | void hists__output_resort_threaded(struct hists *hists) | ||
462 | { | ||
463 | return __hists__output_resort(hists, true); | ||
343 | } | 464 | } |
344 | 465 | ||
345 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 466 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
@@ -594,12 +715,27 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
594 | return ret; | 715 | return ret; |
595 | } | 716 | } |
596 | 717 | ||
597 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 718 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
598 | struct hists *hists, struct hists *pair_hists, | 719 | { |
599 | bool show_displacement, long displacement, | 720 | struct rb_node *next = rb_first(&hists->entries); |
600 | bool color, u64 session_total) | 721 | struct hist_entry *n; |
722 | int row = 0; | ||
723 | |||
724 | hists__reset_col_len(hists); | ||
725 | |||
726 | while (next && row++ < max_rows) { | ||
727 | n = rb_entry(next, struct hist_entry, rb_node); | ||
728 | if (!n->filtered) | ||
729 | hists__calc_col_len(hists, n); | ||
730 | next = rb_next(&n->rb_node); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | ||
735 | size_t size, struct hists *pair_hists, | ||
736 | bool show_displacement, long displacement, | ||
737 | bool color, u64 session_total) | ||
601 | { | 738 | { |
602 | struct sort_entry *se; | ||
603 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 739 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
604 | u64 nr_events; | 740 | u64 nr_events; |
605 | const char *sep = symbol_conf.field_sep; | 741 | const char *sep = symbol_conf.field_sep; |
@@ -664,6 +800,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
664 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); | 800 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
665 | } | 801 | } |
666 | 802 | ||
803 | if (symbol_conf.show_total_period) { | ||
804 | if (sep) | ||
805 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | ||
806 | else | ||
807 | ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); | ||
808 | } | ||
809 | |||
667 | if (pair_hists) { | 810 | if (pair_hists) { |
668 | char bf[32]; | 811 | char bf[32]; |
669 | double old_percent = 0, new_percent = 0, diff; | 812 | double old_percent = 0, new_percent = 0, diff; |
@@ -698,26 +841,42 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
698 | } | 841 | } |
699 | } | 842 | } |
700 | 843 | ||
844 | return ret; | ||
845 | } | ||
846 | |||
847 | int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | ||
848 | struct hists *hists) | ||
849 | { | ||
850 | const char *sep = symbol_conf.field_sep; | ||
851 | struct sort_entry *se; | ||
852 | int ret = 0; | ||
853 | |||
701 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 854 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
702 | if (se->elide) | 855 | if (se->elide) |
703 | continue; | 856 | continue; |
704 | 857 | ||
705 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 858 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
706 | ret += se->se_snprintf(self, s + ret, size - ret, | 859 | ret += se->se_snprintf(he, s + ret, size - ret, |
707 | hists__col_len(hists, se->se_width_idx)); | 860 | hists__col_len(hists, se->se_width_idx)); |
708 | } | 861 | } |
709 | 862 | ||
710 | return ret; | 863 | return ret; |
711 | } | 864 | } |
712 | 865 | ||
713 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 866 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
714 | struct hists *pair_hists, bool show_displacement, | 867 | struct hists *pair_hists, bool show_displacement, |
715 | long displacement, FILE *fp, u64 session_total) | 868 | long displacement, FILE *fp, u64 session_total) |
716 | { | 869 | { |
717 | char bf[512]; | 870 | char bf[512]; |
718 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, | 871 | int ret; |
719 | show_displacement, displacement, | 872 | |
720 | true, session_total); | 873 | if (size == 0 || size > sizeof(bf)) |
874 | size = sizeof(bf); | ||
875 | |||
876 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, | ||
877 | show_displacement, displacement, | ||
878 | true, session_total); | ||
879 | hist_entry__snprintf(he, bf + ret, size - ret, hists); | ||
721 | return fprintf(fp, "%s\n", bf); | 880 | return fprintf(fp, "%s\n", bf); |
722 | } | 881 | } |
723 | 882 | ||
@@ -738,8 +897,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | |||
738 | left_margin); | 897 | left_margin); |
739 | } | 898 | } |
740 | 899 | ||
741 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 900 | size_t hists__fprintf(struct hists *hists, struct hists *pair, |
742 | bool show_displacement, FILE *fp) | 901 | bool show_displacement, bool show_header, int max_rows, |
902 | int max_cols, FILE *fp) | ||
743 | { | 903 | { |
744 | struct sort_entry *se; | 904 | struct sort_entry *se; |
745 | struct rb_node *nd; | 905 | struct rb_node *nd; |
@@ -749,9 +909,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
749 | unsigned int width; | 909 | unsigned int width; |
750 | const char *sep = symbol_conf.field_sep; | 910 | const char *sep = symbol_conf.field_sep; |
751 | const char *col_width = symbol_conf.col_width_list_str; | 911 | const char *col_width = symbol_conf.col_width_list_str; |
912 | int nr_rows = 0; | ||
752 | 913 | ||
753 | init_rem_hits(); | 914 | init_rem_hits(); |
754 | 915 | ||
916 | if (!show_header) | ||
917 | goto print_entries; | ||
918 | |||
755 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | 919 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
756 | 920 | ||
757 | if (symbol_conf.show_nr_samples) { | 921 | if (symbol_conf.show_nr_samples) { |
@@ -761,6 +925,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
761 | fputs(" Samples ", fp); | 925 | fputs(" Samples ", fp); |
762 | } | 926 | } |
763 | 927 | ||
928 | if (symbol_conf.show_total_period) { | ||
929 | if (sep) | ||
930 | ret += fprintf(fp, "%cPeriod", *sep); | ||
931 | else | ||
932 | ret += fprintf(fp, " Period "); | ||
933 | } | ||
934 | |||
764 | if (symbol_conf.show_cpu_utilization) { | 935 | if (symbol_conf.show_cpu_utilization) { |
765 | if (sep) { | 936 | if (sep) { |
766 | ret += fprintf(fp, "%csys", *sep); | 937 | ret += fprintf(fp, "%csys", *sep); |
@@ -803,18 +974,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
803 | width = strlen(se->se_header); | 974 | width = strlen(se->se_header); |
804 | if (symbol_conf.col_width_list_str) { | 975 | if (symbol_conf.col_width_list_str) { |
805 | if (col_width) { | 976 | if (col_width) { |
806 | hists__set_col_len(self, se->se_width_idx, | 977 | hists__set_col_len(hists, se->se_width_idx, |
807 | atoi(col_width)); | 978 | atoi(col_width)); |
808 | col_width = strchr(col_width, ','); | 979 | col_width = strchr(col_width, ','); |
809 | if (col_width) | 980 | if (col_width) |
810 | ++col_width; | 981 | ++col_width; |
811 | } | 982 | } |
812 | } | 983 | } |
813 | if (!hists__new_col_len(self, se->se_width_idx, width)) | 984 | if (!hists__new_col_len(hists, se->se_width_idx, width)) |
814 | width = hists__col_len(self, se->se_width_idx); | 985 | width = hists__col_len(hists, se->se_width_idx); |
815 | fprintf(fp, " %*s", width, se->se_header); | 986 | fprintf(fp, " %*s", width, se->se_header); |
816 | } | 987 | } |
988 | |||
817 | fprintf(fp, "\n"); | 989 | fprintf(fp, "\n"); |
990 | if (max_rows && ++nr_rows >= max_rows) | ||
991 | goto out; | ||
818 | 992 | ||
819 | if (sep) | 993 | if (sep) |
820 | goto print_entries; | 994 | goto print_entries; |
@@ -822,6 +996,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
822 | fprintf(fp, "# ........"); | 996 | fprintf(fp, "# ........"); |
823 | if (symbol_conf.show_nr_samples) | 997 | if (symbol_conf.show_nr_samples) |
824 | fprintf(fp, " .........."); | 998 | fprintf(fp, " .........."); |
999 | if (symbol_conf.show_total_period) | ||
1000 | fprintf(fp, " ............"); | ||
825 | if (pair) { | 1001 | if (pair) { |
826 | fprintf(fp, " .........."); | 1002 | fprintf(fp, " .........."); |
827 | if (show_displacement) | 1003 | if (show_displacement) |
@@ -834,17 +1010,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
834 | continue; | 1010 | continue; |
835 | 1011 | ||
836 | fprintf(fp, " "); | 1012 | fprintf(fp, " "); |
837 | width = hists__col_len(self, se->se_width_idx); | 1013 | width = hists__col_len(hists, se->se_width_idx); |
838 | if (width == 0) | 1014 | if (width == 0) |
839 | width = strlen(se->se_header); | 1015 | width = strlen(se->se_header); |
840 | for (i = 0; i < width; i++) | 1016 | for (i = 0; i < width; i++) |
841 | fprintf(fp, "."); | 1017 | fprintf(fp, "."); |
842 | } | 1018 | } |
843 | 1019 | ||
844 | fprintf(fp, "\n#\n"); | 1020 | fprintf(fp, "\n"); |
1021 | if (max_rows && ++nr_rows >= max_rows) | ||
1022 | goto out; | ||
1023 | |||
1024 | fprintf(fp, "#\n"); | ||
1025 | if (max_rows && ++nr_rows >= max_rows) | ||
1026 | goto out; | ||
845 | 1027 | ||
846 | print_entries: | 1028 | print_entries: |
847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1029 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1030 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
849 | 1031 | ||
850 | if (h->filtered) | 1032 | if (h->filtered) |
@@ -858,19 +1040,22 @@ print_entries: | |||
858 | displacement = 0; | 1040 | displacement = 0; |
859 | ++position; | 1041 | ++position; |
860 | } | 1042 | } |
861 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | 1043 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, |
862 | displacement, fp, self->stats.total_period); | 1044 | displacement, fp, hists->stats.total_period); |
863 | 1045 | ||
864 | if (symbol_conf.use_callchain) | 1046 | if (symbol_conf.use_callchain) |
865 | ret += hist_entry__fprintf_callchain(h, self, fp, | 1047 | ret += hist_entry__fprintf_callchain(h, hists, fp, |
866 | self->stats.total_period); | 1048 | hists->stats.total_period); |
1049 | if (max_rows && ++nr_rows >= max_rows) | ||
1050 | goto out; | ||
1051 | |||
867 | if (h->ms.map == NULL && verbose > 1) { | 1052 | if (h->ms.map == NULL && verbose > 1) { |
868 | __map_groups__fprintf_maps(&h->thread->mg, | 1053 | __map_groups__fprintf_maps(&h->thread->mg, |
869 | MAP__FUNCTION, verbose, fp); | 1054 | MAP__FUNCTION, verbose, fp); |
870 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 1055 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
871 | } | 1056 | } |
872 | } | 1057 | } |
873 | 1058 | out: | |
874 | free(rem_sq_bracket); | 1059 | free(rem_sq_bracket); |
875 | 1060 | ||
876 | return ret; | 1061 | return ret; |
@@ -879,7 +1064,7 @@ print_entries: | |||
879 | /* | 1064 | /* |
880 | * See hists__fprintf to match the column widths | 1065 | * See hists__fprintf to match the column widths |
881 | */ | 1066 | */ |
882 | unsigned int hists__sort_list_width(struct hists *self) | 1067 | unsigned int hists__sort_list_width(struct hists *hists) |
883 | { | 1068 | { |
884 | struct sort_entry *se; | 1069 | struct sort_entry *se; |
885 | int ret = 9; /* total % */ | 1070 | int ret = 9; /* total % */ |
@@ -896,9 +1081,12 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
896 | if (symbol_conf.show_nr_samples) | 1081 | if (symbol_conf.show_nr_samples) |
897 | ret += 11; | 1082 | ret += 11; |
898 | 1083 | ||
1084 | if (symbol_conf.show_total_period) | ||
1085 | ret += 13; | ||
1086 | |||
899 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1087 | list_for_each_entry(se, &hist_entry__sort_list, list) |
900 | if (!se->elide) | 1088 | if (!se->elide) |
901 | ret += 2 + hists__col_len(self, se->se_width_idx); | 1089 | ret += 2 + hists__col_len(hists, se->se_width_idx); |
902 | 1090 | ||
903 | if (verbose) /* Addr + origin */ | 1091 | if (verbose) /* Addr + origin */ |
904 | ret += 3 + BITS_PER_LONG / 4; | 1092 | ret += 3 + BITS_PER_LONG / 4; |
@@ -906,63 +1094,84 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
906 | return ret; | 1094 | return ret; |
907 | } | 1095 | } |
908 | 1096 | ||
909 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | 1097 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
910 | enum hist_filter filter) | 1098 | enum hist_filter filter) |
911 | { | 1099 | { |
912 | h->filtered &= ~(1 << filter); | 1100 | h->filtered &= ~(1 << filter); |
913 | if (h->filtered) | 1101 | if (h->filtered) |
914 | return; | 1102 | return; |
915 | 1103 | ||
916 | ++self->nr_entries; | 1104 | ++hists->nr_entries; |
917 | if (h->ms.unfolded) | 1105 | if (h->ms.unfolded) |
918 | self->nr_entries += h->nr_rows; | 1106 | hists->nr_entries += h->nr_rows; |
919 | h->row_offset = 0; | 1107 | h->row_offset = 0; |
920 | self->stats.total_period += h->period; | 1108 | hists->stats.total_period += h->period; |
921 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 1109 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; |
922 | 1110 | ||
923 | hists__calc_col_len(self, h); | 1111 | hists__calc_col_len(hists, h); |
924 | } | 1112 | } |
925 | 1113 | ||
926 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 1114 | |
1115 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
1116 | struct hist_entry *he) | ||
1117 | { | ||
1118 | if (hists->dso_filter != NULL && | ||
1119 | (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) { | ||
1120 | he->filtered |= (1 << HIST_FILTER__DSO); | ||
1121 | return true; | ||
1122 | } | ||
1123 | |||
1124 | return false; | ||
1125 | } | ||
1126 | |||
1127 | void hists__filter_by_dso(struct hists *hists) | ||
927 | { | 1128 | { |
928 | struct rb_node *nd; | 1129 | struct rb_node *nd; |
929 | 1130 | ||
930 | self->nr_entries = self->stats.total_period = 0; | 1131 | hists->nr_entries = hists->stats.total_period = 0; |
931 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1132 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
932 | hists__reset_col_len(self); | 1133 | hists__reset_col_len(hists); |
933 | 1134 | ||
934 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1135 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
935 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1136 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
936 | 1137 | ||
937 | if (symbol_conf.exclude_other && !h->parent) | 1138 | if (symbol_conf.exclude_other && !h->parent) |
938 | continue; | 1139 | continue; |
939 | 1140 | ||
940 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | 1141 | if (hists__filter_entry_by_dso(hists, h)) |
941 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
942 | continue; | 1142 | continue; |
943 | } | ||
944 | 1143 | ||
945 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); | 1144 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); |
946 | } | 1145 | } |
947 | } | 1146 | } |
948 | 1147 | ||
949 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | 1148 | static bool hists__filter_entry_by_thread(struct hists *hists, |
1149 | struct hist_entry *he) | ||
1150 | { | ||
1151 | if (hists->thread_filter != NULL && | ||
1152 | he->thread != hists->thread_filter) { | ||
1153 | he->filtered |= (1 << HIST_FILTER__THREAD); | ||
1154 | return true; | ||
1155 | } | ||
1156 | |||
1157 | return false; | ||
1158 | } | ||
1159 | |||
1160 | void hists__filter_by_thread(struct hists *hists) | ||
950 | { | 1161 | { |
951 | struct rb_node *nd; | 1162 | struct rb_node *nd; |
952 | 1163 | ||
953 | self->nr_entries = self->stats.total_period = 0; | 1164 | hists->nr_entries = hists->stats.total_period = 0; |
954 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1165 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
955 | hists__reset_col_len(self); | 1166 | hists__reset_col_len(hists); |
956 | 1167 | ||
957 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1168 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
958 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1169 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
959 | 1170 | ||
960 | if (thread != NULL && h->thread != thread) { | 1171 | if (hists__filter_entry_by_thread(hists, h)) |
961 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
962 | continue; | 1172 | continue; |
963 | } | ||
964 | 1173 | ||
965 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); | 1174 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); |
966 | } | 1175 | } |
967 | } | 1176 | } |
968 | 1177 | ||
@@ -976,13 +1185,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
976 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 1185 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
977 | } | 1186 | } |
978 | 1187 | ||
979 | void hists__inc_nr_events(struct hists *self, u32 type) | 1188 | void hists__inc_nr_events(struct hists *hists, u32 type) |
980 | { | 1189 | { |
981 | ++self->stats.nr_events[0]; | 1190 | ++hists->stats.nr_events[0]; |
982 | ++self->stats.nr_events[type]; | 1191 | ++hists->stats.nr_events[type]; |
983 | } | 1192 | } |
984 | 1193 | ||
985 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | 1194 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) |
986 | { | 1195 | { |
987 | int i; | 1196 | int i; |
988 | size_t ret = 0; | 1197 | size_t ret = 0; |
@@ -990,7 +1199,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
990 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 1199 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
991 | const char *name; | 1200 | const char *name; |
992 | 1201 | ||
993 | if (self->stats.nr_events[i] == 0) | 1202 | if (hists->stats.nr_events[i] == 0) |
994 | continue; | 1203 | continue; |
995 | 1204 | ||
996 | name = perf_event__name(i); | 1205 | name = perf_event__name(i); |
@@ -998,8 +1207,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
998 | continue; | 1207 | continue; |
999 | 1208 | ||
1000 | ret += fprintf(fp, "%16s events: %10d\n", name, | 1209 | ret += fprintf(fp, "%16s events: %10d\n", name, |
1001 | self->stats.nr_events[i]); | 1210 | hists->stats.nr_events[i]); |
1002 | } | 1211 | } |
1003 | 1212 | ||
1004 | return ret; | 1213 | return ret; |
1005 | } | 1214 | } |
1215 | |||
1216 | void hists__init(struct hists *hists) | ||
1217 | { | ||
1218 | memset(hists, 0, sizeof(*hists)); | ||
1219 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
1220 | hists->entries_in = &hists->entries_in_array[0]; | ||
1221 | hists->entries_collapsed = RB_ROOT; | ||
1222 | hists->entries = RB_ROOT; | ||
1223 | pthread_mutex_init(&hists->lock, NULL); | ||
1224 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3beb97c4d822..ff93ddc91c5c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_HIST_H | 2 | #define __PERF_HIST_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | ||
5 | #include "callchain.h" | 6 | #include "callchain.h" |
6 | 7 | ||
7 | extern struct callchain_param callchain_param; | 8 | extern struct callchain_param callchain_param; |
@@ -42,9 +43,18 @@ enum hist_column { | |||
42 | HISTC_NR_COLS, /* Last entry */ | 43 | HISTC_NR_COLS, /* Last entry */ |
43 | }; | 44 | }; |
44 | 45 | ||
46 | struct thread; | ||
47 | struct dso; | ||
48 | |||
45 | struct hists { | 49 | struct hists { |
50 | struct rb_root entries_in_array[2]; | ||
51 | struct rb_root *entries_in; | ||
46 | struct rb_root entries; | 52 | struct rb_root entries; |
53 | struct rb_root entries_collapsed; | ||
47 | u64 nr_entries; | 54 | u64 nr_entries; |
55 | const struct thread *thread_filter; | ||
56 | const struct dso *dso_filter; | ||
57 | pthread_mutex_t lock; | ||
48 | struct events_stats stats; | 58 | struct events_stats stats; |
49 | u64 event_stream; | 59 | u64 event_stream; |
50 | u16 col_len[HISTC_NR_COLS]; | 60 | u16 col_len[HISTC_NR_COLS]; |
@@ -52,34 +62,42 @@ struct hists { | |||
52 | struct callchain_cursor callchain_cursor; | 62 | struct callchain_cursor callchain_cursor; |
53 | }; | 63 | }; |
54 | 64 | ||
65 | void hists__init(struct hists *hists); | ||
66 | |||
55 | struct hist_entry *__hists__add_entry(struct hists *self, | 67 | struct hist_entry *__hists__add_entry(struct hists *self, |
56 | struct addr_location *al, | 68 | struct addr_location *al, |
57 | struct symbol *parent, u64 period); | 69 | struct symbol *parent, u64 period); |
58 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 70 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
59 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 71 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
60 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 72 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
61 | struct hists *pair_hists, bool show_displacement, | 73 | struct hists *pair_hists, bool show_displacement, |
62 | long displacement, FILE *fp, u64 total); | 74 | long displacement, FILE *fp, u64 session_total); |
63 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 75 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
64 | struct hists *hists, struct hists *pair_hists, | 76 | struct hists *hists); |
65 | bool show_displacement, long displacement, | ||
66 | bool color, u64 total); | ||
67 | void hist_entry__free(struct hist_entry *); | 77 | void hist_entry__free(struct hist_entry *); |
68 | 78 | ||
69 | void hists__output_resort(struct hists *self); | 79 | void hists__output_resort(struct hists *self); |
80 | void hists__output_resort_threaded(struct hists *hists); | ||
70 | void hists__collapse_resort(struct hists *self); | 81 | void hists__collapse_resort(struct hists *self); |
82 | void hists__collapse_resort_threaded(struct hists *hists); | ||
83 | |||
84 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | ||
85 | void hists__decay_entries_threaded(struct hists *hists, bool zap_user, | ||
86 | bool zap_kernel); | ||
87 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | ||
71 | 88 | ||
72 | void hists__inc_nr_events(struct hists *self, u32 type); | 89 | void hists__inc_nr_events(struct hists *self, u32 type); |
73 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 90 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); |
74 | 91 | ||
75 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 92 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
76 | bool show_displacement, FILE *fp); | 93 | bool show_displacement, bool show_header, |
94 | int max_rows, int max_cols, FILE *fp); | ||
77 | 95 | ||
78 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 96 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
79 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 97 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
80 | 98 | ||
81 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 99 | void hists__filter_by_dso(struct hists *hists); |
82 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 100 | void hists__filter_by_thread(struct hists *hists); |
83 | 101 | ||
84 | u16 hists__col_len(struct hists *self, enum hist_column col); | 102 | u16 hists__col_len(struct hists *self, enum hist_column col); |
85 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 103 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
@@ -90,26 +108,33 @@ struct perf_evlist; | |||
90 | #ifdef NO_NEWT_SUPPORT | 108 | #ifdef NO_NEWT_SUPPORT |
91 | static inline | 109 | static inline |
92 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 110 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, |
93 | const char *help __used) | 111 | const char *help __used, |
112 | void(*timer)(void *arg) __used, | ||
113 | void *arg __used, | ||
114 | int refresh __used) | ||
94 | { | 115 | { |
95 | return 0; | 116 | return 0; |
96 | } | 117 | } |
97 | 118 | ||
98 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 119 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
99 | int evidx __used) | 120 | int evidx __used, |
121 | int nr_events __used, | ||
122 | void(*timer)(void *arg) __used, | ||
123 | void *arg __used, | ||
124 | int delay_secs __used) | ||
100 | { | 125 | { |
101 | return 0; | 126 | return 0; |
102 | } | 127 | } |
103 | #define KEY_LEFT -1 | 128 | #define K_LEFT -1 |
104 | #define KEY_RIGHT -2 | 129 | #define K_RIGHT -2 |
105 | #else | 130 | #else |
106 | #include <newt.h> | 131 | #include "ui/keysyms.h" |
107 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | 132 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
108 | 133 | void(*timer)(void *arg), void *arg, int delay_secs); | |
109 | #define KEY_LEFT NEWT_KEY_LEFT | ||
110 | #define KEY_RIGHT NEWT_KEY_RIGHT | ||
111 | 134 | ||
112 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); | 135 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
136 | void(*timer)(void *arg), void *arg, | ||
137 | int refresh); | ||
113 | #endif | 138 | #endif |
114 | 139 | ||
115 | unsigned int hists__sort_list_width(struct hists *self); | 140 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a16ecab5229d..78284b13e808 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename) | |||
18 | return strcmp(filename, "//anon") == 0; | 18 | return strcmp(filename, "//anon") == 0; |
19 | } | 19 | } |
20 | 20 | ||
21 | static inline int is_no_dso_memory(const char *filename) | ||
22 | { | ||
23 | return !strcmp(filename, "[stack]") || | ||
24 | !strcmp(filename, "[vdso]") || | ||
25 | !strcmp(filename, "[heap]"); | ||
26 | } | ||
27 | |||
21 | void map__init(struct map *self, enum map_type type, | 28 | void map__init(struct map *self, enum map_type type, |
22 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 29 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
23 | { | 30 | { |
@@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
42 | if (self != NULL) { | 49 | if (self != NULL) { |
43 | char newfilename[PATH_MAX]; | 50 | char newfilename[PATH_MAX]; |
44 | struct dso *dso; | 51 | struct dso *dso; |
45 | int anon; | 52 | int anon, no_dso; |
46 | 53 | ||
47 | anon = is_anon_memory(filename); | 54 | anon = is_anon_memory(filename); |
55 | no_dso = is_no_dso_memory(filename); | ||
48 | 56 | ||
49 | if (anon) { | 57 | if (anon) { |
50 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 58 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
@@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
57 | 65 | ||
58 | map__init(self, type, start, start + len, pgoff, dso); | 66 | map__init(self, type, start, start + len, pgoff, dso); |
59 | 67 | ||
60 | if (anon) { | 68 | if (anon || no_dso) { |
61 | set_identity: | ||
62 | self->map_ip = self->unmap_ip = identity__map_ip; | 69 | self->map_ip = self->unmap_ip = identity__map_ip; |
63 | } else if (strcmp(filename, "[vdso]") == 0) { | 70 | |
64 | dso__set_loaded(dso, self->type); | 71 | /* |
65 | goto set_identity; | 72 | * Set memory without DSO as loaded. All map__find_* |
73 | * functions still return NULL, and we avoid the | ||
74 | * unnecessary map__load warning. | ||
75 | */ | ||
76 | if (no_dso) | ||
77 | dso__set_loaded(dso, self->type); | ||
66 | } | 78 | } |
67 | } | 79 | } |
68 | return self; | 80 | return self; |
@@ -127,8 +139,8 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
127 | 139 | ||
128 | if (len > sizeof(DSO__DELETED) && | 140 | if (len > sizeof(DSO__DELETED) && |
129 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 141 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
130 | pr_warning("%.*s was updated, restart the long " | 142 | pr_warning("%.*s was updated (is prelink enabled?). " |
131 | "running apps that use it!\n", | 143 | "Restart the long running apps that use it!\n", |
132 | (int)real_len, name); | 144 | (int)real_len, name); |
133 | } else { | 145 | } else { |
134 | pr_warning("no symbols found in %s, maybe install " | 146 | pr_warning("no symbols found in %s, maybe install " |
@@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr) | |||
220 | return ip; | 232 | return ip; |
221 | } | 233 | } |
222 | 234 | ||
223 | void map_groups__init(struct map_groups *self) | 235 | void map_groups__init(struct map_groups *mg) |
224 | { | 236 | { |
225 | int i; | 237 | int i; |
226 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 238 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
227 | self->maps[i] = RB_ROOT; | 239 | mg->maps[i] = RB_ROOT; |
228 | INIT_LIST_HEAD(&self->removed_maps[i]); | 240 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
229 | } | 241 | } |
230 | self->machine = NULL; | 242 | mg->machine = NULL; |
231 | } | 243 | } |
232 | 244 | ||
233 | static void maps__delete(struct rb_root *self) | 245 | static void maps__delete(struct rb_root *maps) |
234 | { | 246 | { |
235 | struct rb_node *next = rb_first(self); | 247 | struct rb_node *next = rb_first(maps); |
236 | 248 | ||
237 | while (next) { | 249 | while (next) { |
238 | struct map *pos = rb_entry(next, struct map, rb_node); | 250 | struct map *pos = rb_entry(next, struct map, rb_node); |
239 | 251 | ||
240 | next = rb_next(&pos->rb_node); | 252 | next = rb_next(&pos->rb_node); |
241 | rb_erase(&pos->rb_node, self); | 253 | rb_erase(&pos->rb_node, maps); |
242 | map__delete(pos); | 254 | map__delete(pos); |
243 | } | 255 | } |
244 | } | 256 | } |
245 | 257 | ||
246 | static void maps__delete_removed(struct list_head *self) | 258 | static void maps__delete_removed(struct list_head *maps) |
247 | { | 259 | { |
248 | struct map *pos, *n; | 260 | struct map *pos, *n; |
249 | 261 | ||
250 | list_for_each_entry_safe(pos, n, self, node) { | 262 | list_for_each_entry_safe(pos, n, maps, node) { |
251 | list_del(&pos->node); | 263 | list_del(&pos->node); |
252 | map__delete(pos); | 264 | map__delete(pos); |
253 | } | 265 | } |
254 | } | 266 | } |
255 | 267 | ||
256 | void map_groups__exit(struct map_groups *self) | 268 | void map_groups__exit(struct map_groups *mg) |
257 | { | 269 | { |
258 | int i; | 270 | int i; |
259 | 271 | ||
260 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 272 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
261 | maps__delete(&self->maps[i]); | 273 | maps__delete(&mg->maps[i]); |
262 | maps__delete_removed(&self->removed_maps[i]); | 274 | maps__delete_removed(&mg->removed_maps[i]); |
263 | } | 275 | } |
264 | } | 276 | } |
265 | 277 | ||
266 | void map_groups__flush(struct map_groups *self) | 278 | void map_groups__flush(struct map_groups *mg) |
267 | { | 279 | { |
268 | int type; | 280 | int type; |
269 | 281 | ||
270 | for (type = 0; type < MAP__NR_TYPES; type++) { | 282 | for (type = 0; type < MAP__NR_TYPES; type++) { |
271 | struct rb_root *root = &self->maps[type]; | 283 | struct rb_root *root = &mg->maps[type]; |
272 | struct rb_node *next = rb_first(root); | 284 | struct rb_node *next = rb_first(root); |
273 | 285 | ||
274 | while (next) { | 286 | while (next) { |
@@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self) | |||
280 | * instance in some hist_entry instances, so | 292 | * instance in some hist_entry instances, so |
281 | * just move them to a separate list. | 293 | * just move them to a separate list. |
282 | */ | 294 | */ |
283 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | 295 | list_add_tail(&pos->node, &mg->removed_maps[pos->type]); |
284 | } | 296 | } |
285 | } | 297 | } |
286 | } | 298 | } |
287 | 299 | ||
288 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 300 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
289 | enum map_type type, u64 addr, | 301 | enum map_type type, u64 addr, |
290 | struct map **mapp, | 302 | struct map **mapp, |
291 | symbol_filter_t filter) | 303 | symbol_filter_t filter) |
292 | { | 304 | { |
293 | struct map *map = map_groups__find(self, type, addr); | 305 | struct map *map = map_groups__find(mg, type, addr); |
294 | 306 | ||
295 | if (map != NULL) { | 307 | if (map != NULL) { |
296 | if (mapp != NULL) | 308 | if (mapp != NULL) |
@@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, | |||
301 | return NULL; | 313 | return NULL; |
302 | } | 314 | } |
303 | 315 | ||
304 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 316 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
305 | enum map_type type, | 317 | enum map_type type, |
306 | const char *name, | 318 | const char *name, |
307 | struct map **mapp, | 319 | struct map **mapp, |
@@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
309 | { | 321 | { |
310 | struct rb_node *nd; | 322 | struct rb_node *nd; |
311 | 323 | ||
312 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 324 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
313 | struct map *pos = rb_entry(nd, struct map, rb_node); | 325 | struct map *pos = rb_entry(nd, struct map, rb_node); |
314 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); | 326 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); |
315 | 327 | ||
@@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
323 | return NULL; | 335 | return NULL; |
324 | } | 336 | } |
325 | 337 | ||
326 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 338 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
327 | enum map_type type, int verbose, FILE *fp) | 339 | enum map_type type, int verbose, FILE *fp) |
328 | { | 340 | { |
329 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 341 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
330 | struct rb_node *nd; | 342 | struct rb_node *nd; |
331 | 343 | ||
332 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 344 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
333 | struct map *pos = rb_entry(nd, struct map, rb_node); | 345 | struct map *pos = rb_entry(nd, struct map, rb_node); |
334 | printed += fprintf(fp, "Map:"); | 346 | printed += fprintf(fp, "Map:"); |
335 | printed += map__fprintf(pos, fp); | 347 | printed += map__fprintf(pos, fp); |
@@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, | |||
342 | return printed; | 354 | return printed; |
343 | } | 355 | } |
344 | 356 | ||
345 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) | 357 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) |
346 | { | 358 | { |
347 | size_t printed = 0, i; | 359 | size_t printed = 0, i; |
348 | for (i = 0; i < MAP__NR_TYPES; ++i) | 360 | for (i = 0; i < MAP__NR_TYPES; ++i) |
349 | printed += __map_groups__fprintf_maps(self, i, verbose, fp); | 361 | printed += __map_groups__fprintf_maps(mg, i, verbose, fp); |
350 | return printed; | 362 | return printed; |
351 | } | 363 | } |
352 | 364 | ||
353 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | 365 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, |
354 | enum map_type type, | 366 | enum map_type type, |
355 | int verbose, FILE *fp) | 367 | int verbose, FILE *fp) |
356 | { | 368 | { |
357 | struct map *pos; | 369 | struct map *pos; |
358 | size_t printed = 0; | 370 | size_t printed = 0; |
359 | 371 | ||
360 | list_for_each_entry(pos, &self->removed_maps[type], node) { | 372 | list_for_each_entry(pos, &mg->removed_maps[type], node) { |
361 | printed += fprintf(fp, "Map:"); | 373 | printed += fprintf(fp, "Map:"); |
362 | printed += map__fprintf(pos, fp); | 374 | printed += map__fprintf(pos, fp); |
363 | if (verbose > 1) { | 375 | if (verbose > 1) { |
@@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | |||
368 | return printed; | 380 | return printed; |
369 | } | 381 | } |
370 | 382 | ||
371 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, | 383 | static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, |
372 | int verbose, FILE *fp) | 384 | int verbose, FILE *fp) |
373 | { | 385 | { |
374 | size_t printed = 0, i; | 386 | size_t printed = 0, i; |
375 | for (i = 0; i < MAP__NR_TYPES; ++i) | 387 | for (i = 0; i < MAP__NR_TYPES; ++i) |
376 | printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); | 388 | printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); |
377 | return printed; | 389 | return printed; |
378 | } | 390 | } |
379 | 391 | ||
380 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) | 392 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) |
381 | { | 393 | { |
382 | size_t printed = map_groups__fprintf_maps(self, verbose, fp); | 394 | size_t printed = map_groups__fprintf_maps(mg, verbose, fp); |
383 | printed += fprintf(fp, "Removed maps:\n"); | 395 | printed += fprintf(fp, "Removed maps:\n"); |
384 | return printed + map_groups__fprintf_removed_maps(self, verbose, fp); | 396 | return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); |
385 | } | 397 | } |
386 | 398 | ||
387 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 399 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
388 | int verbose, FILE *fp) | 400 | int verbose, FILE *fp) |
389 | { | 401 | { |
390 | struct rb_root *root = &self->maps[map->type]; | 402 | struct rb_root *root = &mg->maps[map->type]; |
391 | struct rb_node *next = rb_first(root); | 403 | struct rb_node *next = rb_first(root); |
392 | int err = 0; | 404 | int err = 0; |
393 | 405 | ||
@@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
418 | } | 430 | } |
419 | 431 | ||
420 | before->end = map->start - 1; | 432 | before->end = map->start - 1; |
421 | map_groups__insert(self, before); | 433 | map_groups__insert(mg, before); |
422 | if (verbose >= 2) | 434 | if (verbose >= 2) |
423 | map__fprintf(before, fp); | 435 | map__fprintf(before, fp); |
424 | } | 436 | } |
@@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
432 | } | 444 | } |
433 | 445 | ||
434 | after->start = map->end + 1; | 446 | after->start = map->end + 1; |
435 | map_groups__insert(self, after); | 447 | map_groups__insert(mg, after); |
436 | if (verbose >= 2) | 448 | if (verbose >= 2) |
437 | map__fprintf(after, fp); | 449 | map__fprintf(after, fp); |
438 | } | 450 | } |
@@ -441,7 +453,7 @@ move_map: | |||
441 | * If we have references, just move them to a separate list. | 453 | * If we have references, just move them to a separate list. |
442 | */ | 454 | */ |
443 | if (pos->referenced) | 455 | if (pos->referenced) |
444 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | 456 | list_add_tail(&pos->node, &mg->removed_maps[map->type]); |
445 | else | 457 | else |
446 | map__delete(pos); | 458 | map__delete(pos); |
447 | 459 | ||
@@ -455,7 +467,7 @@ move_map: | |||
455 | /* | 467 | /* |
456 | * XXX This should not really _copy_ te maps, but refcount them. | 468 | * XXX This should not really _copy_ te maps, but refcount them. |
457 | */ | 469 | */ |
458 | int map_groups__clone(struct map_groups *self, | 470 | int map_groups__clone(struct map_groups *mg, |
459 | struct map_groups *parent, enum map_type type) | 471 | struct map_groups *parent, enum map_type type) |
460 | { | 472 | { |
461 | struct rb_node *nd; | 473 | struct rb_node *nd; |
@@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self, | |||
464 | struct map *new = map__clone(map); | 476 | struct map *new = map__clone(map); |
465 | if (new == NULL) | 477 | if (new == NULL) |
466 | return -ENOMEM; | 478 | return -ENOMEM; |
467 | map_groups__insert(self, new); | 479 | map_groups__insert(mg, new); |
468 | } | 480 | } |
469 | return 0; | 481 | return 0; |
470 | } | 482 | } |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b397c0383728..890d85545d0f 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -123,17 +123,17 @@ void map__fixup_end(struct map *self); | |||
123 | 123 | ||
124 | void map__reloc_vmlinux(struct map *self); | 124 | void map__reloc_vmlinux(struct map *self); |
125 | 125 | ||
126 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 126 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
127 | enum map_type type, int verbose, FILE *fp); | 127 | enum map_type type, int verbose, FILE *fp); |
128 | void maps__insert(struct rb_root *maps, struct map *map); | 128 | void maps__insert(struct rb_root *maps, struct map *map); |
129 | void maps__remove(struct rb_root *self, struct map *map); | 129 | void maps__remove(struct rb_root *maps, struct map *map); |
130 | struct map *maps__find(struct rb_root *maps, u64 addr); | 130 | struct map *maps__find(struct rb_root *maps, u64 addr); |
131 | void map_groups__init(struct map_groups *self); | 131 | void map_groups__init(struct map_groups *mg); |
132 | void map_groups__exit(struct map_groups *self); | 132 | void map_groups__exit(struct map_groups *mg); |
133 | int map_groups__clone(struct map_groups *self, | 133 | int map_groups__clone(struct map_groups *mg, |
134 | struct map_groups *parent, enum map_type type); | 134 | struct map_groups *parent, enum map_type type); |
135 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 135 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); |
136 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); | 136 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); |
137 | 137 | ||
138 | typedef void (*machine__process_t)(struct machine *self, void *data); | 138 | typedef void (*machine__process_t)(struct machine *self, void *data); |
139 | 139 | ||
@@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self) | |||
162 | return self ? self->pid == HOST_KERNEL_ID : false; | 162 | return self ? self->pid == HOST_KERNEL_ID : false; |
163 | } | 163 | } |
164 | 164 | ||
165 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | 165 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
166 | { | 166 | { |
167 | maps__insert(&self->maps[map->type], map); | 167 | maps__insert(&mg->maps[map->type], map); |
168 | map->groups = self; | 168 | map->groups = mg; |
169 | } | 169 | } |
170 | 170 | ||
171 | static inline void map_groups__remove(struct map_groups *self, struct map *map) | 171 | static inline void map_groups__remove(struct map_groups *mg, struct map *map) |
172 | { | 172 | { |
173 | maps__remove(&self->maps[map->type], map); | 173 | maps__remove(&mg->maps[map->type], map); |
174 | } | 174 | } |
175 | 175 | ||
176 | static inline struct map *map_groups__find(struct map_groups *self, | 176 | static inline struct map *map_groups__find(struct map_groups *mg, |
177 | enum map_type type, u64 addr) | 177 | enum map_type type, u64 addr) |
178 | { | 178 | { |
179 | return maps__find(&self->maps[type], addr); | 179 | return maps__find(&mg->maps[type], addr); |
180 | } | 180 | } |
181 | 181 | ||
182 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 182 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
183 | enum map_type type, u64 addr, | 183 | enum map_type type, u64 addr, |
184 | struct map **mapp, | 184 | struct map **mapp, |
185 | symbol_filter_t filter); | 185 | symbol_filter_t filter); |
186 | 186 | ||
187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
188 | enum map_type type, | 188 | enum map_type type, |
189 | const char *name, | 189 | const char *name, |
190 | struct map **mapp, | 190 | struct map **mapp, |
@@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, | |||
208 | } | 208 | } |
209 | 209 | ||
210 | static inline | 210 | static inline |
211 | struct symbol *map_groups__find_function_by_name(struct map_groups *self, | 211 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
212 | const char *name, struct map **mapp, | 212 | const char *name, struct map **mapp, |
213 | symbol_filter_t filter) | 213 | symbol_filter_t filter) |
214 | { | 214 | { |
215 | return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); | 215 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
216 | } | 216 | } |
217 | 217 | ||
218 | static inline | 218 | static inline |
@@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self, | |||
225 | filter); | 225 | filter); |
226 | } | 226 | } |
227 | 227 | ||
228 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 228 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
229 | int verbose, FILE *fp); | 229 | int verbose, FILE *fp); |
230 | 230 | ||
231 | struct map *map_groups__find_by_name(struct map_groups *self, | 231 | struct map *map_groups__find_by_name(struct map_groups *mg, |
232 | enum map_type type, const char *name); | 232 | enum map_type type, const char *name); |
233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); | 233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); |
234 | 234 | ||
235 | void map_groups__flush(struct map_groups *self); | 235 | void map_groups__flush(struct map_groups *mg); |
236 | 236 | ||
237 | #endif /* __PERF_MAP_H */ | 237 | #endif /* __PERF_MAP_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c7bfa5fe0a8..eb25900e2211 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -1956,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
1956 | 1956 | ||
1957 | pr_debug("Writing event: %s\n", buf); | 1957 | pr_debug("Writing event: %s\n", buf); |
1958 | ret = write(fd, buf, strlen(buf)); | 1958 | ret = write(fd, buf, strlen(buf)); |
1959 | if (ret < 0) | 1959 | if (ret < 0) { |
1960 | ret = -errno; | ||
1960 | goto error; | 1961 | goto error; |
1962 | } | ||
1961 | 1963 | ||
1962 | printf("Remove event: %s\n", ent->s); | 1964 | printf("Remove event: %s\n", ent->s); |
1963 | return 0; | 1965 | return 0; |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72458d9da5b1..20e011c99a94 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session, | |||
1326 | 1326 | ||
1327 | return 0; | 1327 | return 0; |
1328 | } | 1328 | } |
1329 | |||
1330 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | ||
1331 | bool full) | ||
1332 | { | ||
1333 | struct stat st; | ||
1334 | int ret; | ||
1335 | |||
1336 | if (session == NULL || fp == NULL) | ||
1337 | return; | ||
1338 | |||
1339 | ret = fstat(session->fd, &st); | ||
1340 | if (ret == -1) | ||
1341 | return; | ||
1342 | |||
1343 | fprintf(fp, "# ========\n"); | ||
1344 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
1345 | perf_header__fprintf_info(session, fp, full); | ||
1346 | fprintf(fp, "# ========\n#\n"); | ||
1347 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 974d0cbee5e9..514b06d41f05 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event, | |||
177 | int perf_session__cpu_bitmap(struct perf_session *session, | 177 | int perf_session__cpu_bitmap(struct perf_session *session, |
178 | const char *cpu_list, unsigned long *cpu_bitmap); | 178 | const char *cpu_list, unsigned long *cpu_bitmap); |
179 | 179 | ||
180 | void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
180 | #endif /* __PERF_SESSION_H */ | 181 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1ee8f1e40f18..16da30d8d765 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -177,7 +177,9 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
177 | BITS_PER_LONG / 4, self->ip, o); | 177 | BITS_PER_LONG / 4, self->ip, o); |
178 | } | 178 | } |
179 | 179 | ||
180 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | 180 | if (!sort_dso.elide) |
181 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | ||
182 | |||
181 | if (self->ms.sym) | 183 | if (self->ms.sym) |
182 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | 184 | ret += repsep_snprintf(bf + ret, size - ret, "%s", |
183 | self->ms.sym->name); | 185 | self->ms.sym->name); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 77d0388ad415..3f67ae395752 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension; | |||
45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding | 45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding |
46 | */ | 46 | */ |
47 | struct hist_entry { | 47 | struct hist_entry { |
48 | struct rb_node rb_node_in; | ||
48 | struct rb_node rb_node; | 49 | struct rb_node rb_node; |
49 | u64 period; | 50 | u64 period; |
50 | u64 period_sys; | 51 | u64 period_sys; |
@@ -63,6 +64,7 @@ struct hist_entry { | |||
63 | 64 | ||
64 | bool init_have_children; | 65 | bool init_have_children; |
65 | char level; | 66 | char level; |
67 | bool used; | ||
66 | u8 filtered; | 68 | u8 filtered; |
67 | struct symbol *parent; | 69 | struct symbol *parent; |
68 | union { | 70 | union { |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 40eeaf07725b..632b50c7bc26 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <sys/utsname.h> | 24 | #include <sys/utsname.h> |
25 | 25 | ||
26 | #ifndef KSYM_NAME_LEN | 26 | #ifndef KSYM_NAME_LEN |
27 | #define KSYM_NAME_LEN 128 | 27 | #define KSYM_NAME_LEN 256 |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #ifndef NT_GNU_BUILD_ID | 30 | #ifndef NT_GNU_BUILD_ID |
@@ -46,6 +46,7 @@ struct symbol_conf symbol_conf = { | |||
46 | .exclude_other = true, | 46 | .exclude_other = true, |
47 | .use_modules = true, | 47 | .use_modules = true, |
48 | .try_vmlinux_path = true, | 48 | .try_vmlinux_path = true, |
49 | .annotate_src = true, | ||
49 | .symfs = "", | 50 | .symfs = "", |
50 | }; | 51 | }; |
51 | 52 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 4f377d92e75a..29f8d742e92f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -72,11 +72,14 @@ struct symbol_conf { | |||
72 | use_modules, | 72 | use_modules, |
73 | sort_by_name, | 73 | sort_by_name, |
74 | show_nr_samples, | 74 | show_nr_samples, |
75 | show_total_period, | ||
75 | use_callchain, | 76 | use_callchain, |
76 | exclude_other, | 77 | exclude_other, |
77 | show_cpu_utilization, | 78 | show_cpu_utilization, |
78 | initialized, | 79 | initialized, |
79 | kptr_restrict; | 80 | kptr_restrict, |
81 | annotate_asm_raw, | ||
82 | annotate_src; | ||
80 | const char *vmlinux_name, | 83 | const char *vmlinux_name, |
81 | *kallsyms_name, | 84 | *kallsyms_name, |
82 | *source_prefix, | 85 | *source_prefix, |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index a11f60735a18..500471dffa4f 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -15,52 +15,6 @@ | |||
15 | #include "top.h" | 15 | #include "top.h" |
16 | #include <inttypes.h> | 16 | #include <inttypes.h> |
17 | 17 | ||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | #define SNPRINTF(buf, size, fmt, args...) \ | 18 | #define SNPRINTF(buf, size, fmt, args...) \ |
65 | ({ \ | 19 | ({ \ |
66 | size_t r = snprintf(buf, size, fmt, ## args); \ | 20 | size_t r = snprintf(buf, size, fmt, ## args); \ |
@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | |||
69 | 23 | ||
70 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | 24 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) |
71 | { | 25 | { |
72 | struct perf_evsel *counter; | ||
73 | float samples_per_sec = top->samples / top->delay_secs; | 26 | float samples_per_sec = top->samples / top->delay_secs; |
74 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | 27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; |
75 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | 28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; |
@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
104 | esamples_percent); | 57 | esamples_percent); |
105 | } | 58 | } |
106 | 59 | ||
107 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | 60 | if (top->evlist->nr_entries == 1) { |
108 | struct perf_evsel *first; | 61 | struct perf_evsel *first; |
109 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | 62 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); |
110 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 63 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
112 | top->freq ? "Hz" : ""); | 65 | top->freq ? "Hz" : ""); |
113 | } | 66 | } |
114 | 67 | ||
115 | if (!top->display_weighted) { | 68 | ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel)); |
116 | ret += SNPRINTF(bf + ret, size - ret, "%s", | ||
117 | event_name(top->sym_evsel)); | ||
118 | } else { | ||
119 | /* | ||
120 | * Don't let events eat all the space. Leaving 30 bytes | ||
121 | * for the rest should be enough. | ||
122 | */ | ||
123 | size_t last_pos = size - 30; | ||
124 | |||
125 | list_for_each_entry(counter, &top->evlist->entries, node) { | ||
126 | ret += SNPRINTF(bf + ret, size - ret, "%s%s", | ||
127 | counter->idx ? "/" : "", | ||
128 | event_name(counter)); | ||
129 | if (ret > last_pos) { | ||
130 | sprintf(bf + last_pos - 3, ".."); | ||
131 | ret = last_pos - 1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | 69 | ||
137 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
138 | 71 | ||
@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top) | |||
166 | top->exact_samples = top->guest_kernel_samples = | 99 | top->exact_samples = top->guest_kernel_samples = |
167 | top->guest_us_samples = 0; | 100 | top->guest_us_samples = 0; |
168 | } | 101 | } |
169 | |||
170 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
171 | { | ||
172 | struct sym_entry *syme, *n; | ||
173 | float sum_ksamples = 0.0; | ||
174 | int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; | ||
175 | |||
176 | /* Sort the active symbols */ | ||
177 | pthread_mutex_lock(&top->active_symbols_lock); | ||
178 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
179 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
180 | |||
181 | top->rb_entries = 0; | ||
182 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
183 | syme->snap_count = syme->count[snap]; | ||
184 | if (syme->snap_count != 0) { | ||
185 | |||
186 | if ((top->hide_user_symbols && | ||
187 | syme->map->dso->kernel == DSO_TYPE_USER) || | ||
188 | (top->hide_kernel_symbols && | ||
189 | syme->map->dso->kernel == DSO_TYPE_KERNEL)) { | ||
190 | perf_top__remove_active_sym(top, syme); | ||
191 | continue; | ||
192 | } | ||
193 | syme->weight = sym_weight(syme, top); | ||
194 | |||
195 | if ((int)syme->snap_count >= top->count_filter) { | ||
196 | rb_insert_active_sym(root, syme); | ||
197 | ++top->rb_entries; | ||
198 | } | ||
199 | sum_ksamples += syme->snap_count; | ||
200 | |||
201 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
202 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
203 | } else | ||
204 | perf_top__remove_active_sym(top, syme); | ||
205 | } | ||
206 | |||
207 | return sum_ksamples; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the longest symbol name that will be displayed | ||
212 | */ | ||
213 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
214 | int *dso_width, int *dso_short_width, int *sym_width) | ||
215 | { | ||
216 | struct rb_node *nd; | ||
217 | int printed = 0; | ||
218 | |||
219 | *sym_width = *dso_width = *dso_short_width = 0; | ||
220 | |||
221 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
222 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
223 | struct symbol *sym = sym_entry__symbol(syme); | ||
224 | |||
225 | if (++printed > top->print_entries || | ||
226 | (int)syme->snap_count < top->count_filter) | ||
227 | continue; | ||
228 | |||
229 | if (syme->map->dso->long_name_len > *dso_width) | ||
230 | *dso_width = syme->map->dso->long_name_len; | ||
231 | |||
232 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
233 | *dso_short_width = syme->map->dso->short_name_len; | ||
234 | |||
235 | if (sym->namelen > *sym_width) | ||
236 | *sym_width = sym->namelen; | ||
237 | } | ||
238 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index bfbf95bcc603..01d1057f3074 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -4,26 +4,10 @@ | |||
4 | #include "types.h" | 4 | #include "types.h" |
5 | #include "../perf.h" | 5 | #include "../perf.h" |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | 7 | ||
11 | struct perf_evlist; | 8 | struct perf_evlist; |
12 | struct perf_evsel; | 9 | struct perf_evsel; |
13 | 10 | struct perf_session; | |
14 | struct sym_entry { | ||
15 | struct rb_node rb_node; | ||
16 | struct list_head node; | ||
17 | unsigned long snap_count; | ||
18 | double weight; | ||
19 | struct map *map; | ||
20 | unsigned long count[0]; | ||
21 | }; | ||
22 | |||
23 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
24 | { | ||
25 | return ((void *)self) + symbol_conf.priv_size; | ||
26 | } | ||
27 | 11 | ||
28 | struct perf_top { | 12 | struct perf_top { |
29 | struct perf_evlist *evlist; | 13 | struct perf_evlist *evlist; |
@@ -31,34 +15,21 @@ struct perf_top { | |||
31 | * Symbols will be added here in perf_event__process_sample and will | 15 | * Symbols will be added here in perf_event__process_sample and will |
32 | * get out after decayed. | 16 | * get out after decayed. |
33 | */ | 17 | */ |
34 | struct list_head active_symbols; | ||
35 | pthread_mutex_t active_symbols_lock; | ||
36 | pthread_cond_t active_symbols_cond; | ||
37 | u64 samples; | 18 | u64 samples; |
38 | u64 kernel_samples, us_samples; | 19 | u64 kernel_samples, us_samples; |
39 | u64 exact_samples; | 20 | u64 exact_samples; |
40 | u64 guest_us_samples, guest_kernel_samples; | 21 | u64 guest_us_samples, guest_kernel_samples; |
22 | u64 total_lost_warned; | ||
41 | int print_entries, count_filter, delay_secs; | 23 | int print_entries, count_filter, delay_secs; |
42 | int display_weighted, freq, rb_entries; | 24 | int freq; |
43 | pid_t target_pid, target_tid; | 25 | pid_t target_pid, target_tid; |
44 | bool hide_kernel_symbols, hide_user_symbols, zero; | 26 | bool hide_kernel_symbols, hide_user_symbols, zero; |
45 | const char *cpu_list; | 27 | const char *cpu_list; |
46 | struct sym_entry *sym_filter_entry; | 28 | struct hist_entry *sym_filter_entry; |
47 | struct perf_evsel *sym_evsel; | 29 | struct perf_evsel *sym_evsel; |
30 | struct perf_session *session; | ||
48 | }; | 31 | }; |
49 | 32 | ||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 33 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
51 | void perf_top__reset_sample_counters(struct perf_top *top); | 34 | void perf_top__reset_sample_counters(struct perf_top *top); |
52 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
53 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
54 | int *dso_width, int *dso_short_width, int *sym_width); | ||
55 | |||
56 | #ifdef NO_NEWT_SUPPORT | ||
57 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | #else | ||
62 | int perf_top__tui_browser(struct perf_top *top); | ||
63 | #endif | ||
64 | #endif /* __PERF_TOP_H */ | 35 | #endif /* __PERF_TOP_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 3403f814ad72..2d530cf74f43 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -196,7 +196,8 @@ static void record_file(const char *file, size_t hdr_sz) | |||
196 | die("Can't read '%s'", file); | 196 | die("Can't read '%s'", file); |
197 | 197 | ||
198 | /* put in zeros for file size, then fill true size later */ | 198 | /* put in zeros for file size, then fill true size later */ |
199 | write_or_die(&size, hdr_sz); | 199 | if (hdr_sz) |
200 | write_or_die(&size, hdr_sz); | ||
200 | 201 | ||
201 | do { | 202 | do { |
202 | r = read(fd, buf, BUFSIZ); | 203 | r = read(fd, buf, BUFSIZ); |
@@ -212,7 +213,7 @@ static void record_file(const char *file, size_t hdr_sz) | |||
212 | if (bigendian()) | 213 | if (bigendian()) |
213 | sizep += sizeof(u64) - hdr_sz; | 214 | sizep += sizeof(u64) - hdr_sz; |
214 | 215 | ||
215 | if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | 216 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) |
216 | die("writing to %s", output_file); | 217 | die("writing to %s", output_file); |
217 | } | 218 | } |
218 | 219 | ||
@@ -428,6 +429,19 @@ get_tracepoints_path(struct list_head *pattrs) | |||
428 | return nr_tracepoints > 0 ? path.next : NULL; | 429 | return nr_tracepoints > 0 ? path.next : NULL; |
429 | } | 430 | } |
430 | 431 | ||
432 | static void | ||
433 | put_tracepoints_path(struct tracepoint_path *tps) | ||
434 | { | ||
435 | while (tps) { | ||
436 | struct tracepoint_path *t = tps; | ||
437 | |||
438 | tps = tps->next; | ||
439 | free(t->name); | ||
440 | free(t->system); | ||
441 | free(t); | ||
442 | } | ||
443 | } | ||
444 | |||
431 | bool have_tracepoints(struct list_head *pattrs) | 445 | bool have_tracepoints(struct list_head *pattrs) |
432 | { | 446 | { |
433 | struct perf_evsel *pos; | 447 | struct perf_evsel *pos; |
@@ -439,19 +453,11 @@ bool have_tracepoints(struct list_head *pattrs) | |||
439 | return false; | 453 | return false; |
440 | } | 454 | } |
441 | 455 | ||
442 | int read_tracing_data(int fd, struct list_head *pattrs) | 456 | static void tracing_data_header(void) |
443 | { | 457 | { |
444 | char buf[BUFSIZ]; | 458 | char buf[20]; |
445 | struct tracepoint_path *tps = get_tracepoints_path(pattrs); | ||
446 | |||
447 | /* | ||
448 | * What? No tracepoints? No sense writing anything here, bail out. | ||
449 | */ | ||
450 | if (tps == NULL) | ||
451 | return -1; | ||
452 | |||
453 | output_fd = fd; | ||
454 | 459 | ||
460 | /* just guessing this is someone's birthday.. ;) */ | ||
455 | buf[0] = 23; | 461 | buf[0] = 23; |
456 | buf[1] = 8; | 462 | buf[1] = 8; |
457 | buf[2] = 68; | 463 | buf[2] = 68; |
@@ -476,28 +482,86 @@ int read_tracing_data(int fd, struct list_head *pattrs) | |||
476 | /* save page_size */ | 482 | /* save page_size */ |
477 | page_size = sysconf(_SC_PAGESIZE); | 483 | page_size = sysconf(_SC_PAGESIZE); |
478 | write_or_die(&page_size, 4); | 484 | write_or_die(&page_size, 4); |
485 | } | ||
486 | |||
487 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
488 | int fd, bool temp) | ||
489 | { | ||
490 | struct tracepoint_path *tps; | ||
491 | struct tracing_data *tdata; | ||
492 | |||
493 | output_fd = fd; | ||
494 | |||
495 | tps = get_tracepoints_path(pattrs); | ||
496 | if (!tps) | ||
497 | return NULL; | ||
479 | 498 | ||
499 | tdata = malloc_or_die(sizeof(*tdata)); | ||
500 | tdata->temp = temp; | ||
501 | tdata->size = 0; | ||
502 | |||
503 | if (temp) { | ||
504 | int temp_fd; | ||
505 | |||
506 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), | ||
507 | "/tmp/perf-XXXXXX"); | ||
508 | if (!mkstemp(tdata->temp_file)) | ||
509 | die("Can't make temp file"); | ||
510 | |||
511 | temp_fd = open(tdata->temp_file, O_RDWR); | ||
512 | if (temp_fd < 0) | ||
513 | die("Can't read '%s'", tdata->temp_file); | ||
514 | |||
515 | /* | ||
516 | * Set the temp file the default output, so all the | ||
517 | * tracing data are stored into it. | ||
518 | */ | ||
519 | output_fd = temp_fd; | ||
520 | } | ||
521 | |||
522 | tracing_data_header(); | ||
480 | read_header_files(); | 523 | read_header_files(); |
481 | read_ftrace_files(tps); | 524 | read_ftrace_files(tps); |
482 | read_event_files(tps); | 525 | read_event_files(tps); |
483 | read_proc_kallsyms(); | 526 | read_proc_kallsyms(); |
484 | read_ftrace_printk(); | 527 | read_ftrace_printk(); |
485 | 528 | ||
486 | return 0; | 529 | /* |
530 | * All tracing data are stored by now, we can restore | ||
531 | * the default output file in case we used temp file. | ||
532 | */ | ||
533 | if (temp) { | ||
534 | tdata->size = lseek(output_fd, 0, SEEK_CUR); | ||
535 | close(output_fd); | ||
536 | output_fd = fd; | ||
537 | } | ||
538 | |||
539 | put_tracepoints_path(tps); | ||
540 | return tdata; | ||
487 | } | 541 | } |
488 | 542 | ||
489 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) | 543 | void tracing_data_put(struct tracing_data *tdata) |
490 | { | 544 | { |
491 | ssize_t size; | 545 | if (tdata->temp) { |
492 | int err = 0; | 546 | record_file(tdata->temp_file, 0); |
547 | unlink(tdata->temp_file); | ||
548 | } | ||
493 | 549 | ||
494 | calc_data_size = 1; | 550 | free(tdata); |
495 | err = read_tracing_data(fd, pattrs); | 551 | } |
496 | size = calc_data_size - 1; | ||
497 | calc_data_size = 0; | ||
498 | 552 | ||
499 | if (err < 0) | 553 | int read_tracing_data(int fd, struct list_head *pattrs) |
500 | return err; | 554 | { |
555 | struct tracing_data *tdata; | ||
501 | 556 | ||
502 | return size; | 557 | /* |
558 | * We work over the real file, so we can write data | ||
559 | * directly, no temp file is needed. | ||
560 | */ | ||
561 | tdata = tracing_data_get(pattrs, fd, false); | ||
562 | if (!tdata) | ||
563 | return -ENOMEM; | ||
564 | |||
565 | tracing_data_put(tdata); | ||
566 | return 0; | ||
503 | } | 567 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index f674dda3363b..a84100817649 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -263,7 +263,18 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); | |||
263 | unsigned long long eval_flag(const char *flag); | 263 | unsigned long long eval_flag(const char *flag); |
264 | 264 | ||
265 | int read_tracing_data(int fd, struct list_head *pattrs); | 265 | int read_tracing_data(int fd, struct list_head *pattrs); |
266 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); | 266 | |
267 | struct tracing_data { | ||
268 | /* size is only valid if temp is 'true' */ | ||
269 | ssize_t size; | ||
270 | bool temp; | ||
271 | char temp_file[50]; | ||
272 | }; | ||
273 | |||
274 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
275 | int fd, bool temp); | ||
276 | void tracing_data_put(struct tracing_data *tdata); | ||
277 | |||
267 | 278 | ||
268 | /* taken from kernel/trace/trace.h */ | 279 | /* taken from kernel/trace/trace.h */ |
269 | enum trace_flag_type { | 280 | enum trace_flag_type { |
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 611219f80680..5359f371d30a 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
@@ -1,4 +1,8 @@ | |||
1 | #include "../util.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../../perf.h" | ||
1 | #include "libslang.h" | 4 | #include "libslang.h" |
5 | #include <newt.h> | ||
2 | #include "ui.h" | 6 | #include "ui.h" |
3 | #include <linux/compiler.h> | 7 | #include <linux/compiler.h> |
4 | #include <linux/list.h> | 8 | #include <linux/list.h> |
@@ -7,13 +11,13 @@ | |||
7 | #include <sys/ttydefaults.h> | 11 | #include <sys/ttydefaults.h> |
8 | #include "browser.h" | 12 | #include "browser.h" |
9 | #include "helpline.h" | 13 | #include "helpline.h" |
14 | #include "keysyms.h" | ||
10 | #include "../color.h" | 15 | #include "../color.h" |
11 | #include "../util.h" | ||
12 | #include <stdio.h> | ||
13 | 16 | ||
14 | static int ui_browser__percent_color(double percent, bool current) | 17 | static int ui_browser__percent_color(struct ui_browser *browser, |
18 | double percent, bool current) | ||
15 | { | 19 | { |
16 | if (current) | 20 | if (current && (!browser->use_navkeypressed || browser->navkeypressed)) |
17 | return HE_COLORSET_SELECTED; | 21 | return HE_COLORSET_SELECTED; |
18 | if (percent >= MIN_RED) | 22 | if (percent >= MIN_RED) |
19 | return HE_COLORSET_TOP; | 23 | return HE_COLORSET_TOP; |
@@ -30,7 +34,7 @@ void ui_browser__set_color(struct ui_browser *self __used, int color) | |||
30 | void ui_browser__set_percent_color(struct ui_browser *self, | 34 | void ui_browser__set_percent_color(struct ui_browser *self, |
31 | double percent, bool current) | 35 | double percent, bool current) |
32 | { | 36 | { |
33 | int color = ui_browser__percent_color(percent, current); | 37 | int color = ui_browser__percent_color(self, percent, current); |
34 | ui_browser__set_color(self, color); | 38 | ui_browser__set_color(self, color); |
35 | } | 39 | } |
36 | 40 | ||
@@ -39,31 +43,62 @@ void ui_browser__gotorc(struct ui_browser *self, int y, int x) | |||
39 | SLsmg_gotorc(self->y + y, self->x + x); | 43 | SLsmg_gotorc(self->y + y, self->x + x); |
40 | } | 44 | } |
41 | 45 | ||
46 | static struct list_head * | ||
47 | ui_browser__list_head_filter_entries(struct ui_browser *browser, | ||
48 | struct list_head *pos) | ||
49 | { | ||
50 | do { | ||
51 | if (!browser->filter || !browser->filter(browser, pos)) | ||
52 | return pos; | ||
53 | pos = pos->next; | ||
54 | } while (pos != browser->entries); | ||
55 | |||
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | static struct list_head * | ||
60 | ui_browser__list_head_filter_prev_entries(struct ui_browser *browser, | ||
61 | struct list_head *pos) | ||
62 | { | ||
63 | do { | ||
64 | if (!browser->filter || !browser->filter(browser, pos)) | ||
65 | return pos; | ||
66 | pos = pos->prev; | ||
67 | } while (pos != browser->entries); | ||
68 | |||
69 | return NULL; | ||
70 | } | ||
71 | |||
42 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) | 72 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) |
43 | { | 73 | { |
44 | struct list_head *head = self->entries; | 74 | struct list_head *head = self->entries; |
45 | struct list_head *pos; | 75 | struct list_head *pos; |
46 | 76 | ||
77 | if (self->nr_entries == 0) | ||
78 | return; | ||
79 | |||
47 | switch (whence) { | 80 | switch (whence) { |
48 | case SEEK_SET: | 81 | case SEEK_SET: |
49 | pos = head->next; | 82 | pos = ui_browser__list_head_filter_entries(self, head->next); |
50 | break; | 83 | break; |
51 | case SEEK_CUR: | 84 | case SEEK_CUR: |
52 | pos = self->top; | 85 | pos = self->top; |
53 | break; | 86 | break; |
54 | case SEEK_END: | 87 | case SEEK_END: |
55 | pos = head->prev; | 88 | pos = ui_browser__list_head_filter_prev_entries(self, head->prev); |
56 | break; | 89 | break; |
57 | default: | 90 | default: |
58 | return; | 91 | return; |
59 | } | 92 | } |
60 | 93 | ||
94 | assert(pos != NULL); | ||
95 | |||
61 | if (offset > 0) { | 96 | if (offset > 0) { |
62 | while (offset-- != 0) | 97 | while (offset-- != 0) |
63 | pos = pos->next; | 98 | pos = ui_browser__list_head_filter_entries(self, pos->next); |
64 | } else { | 99 | } else { |
65 | while (offset++ != 0) | 100 | while (offset++ != 0) |
66 | pos = pos->prev; | 101 | pos = ui_browser__list_head_filter_prev_entries(self, pos->prev); |
67 | } | 102 | } |
68 | 103 | ||
69 | self->top = pos; | 104 | self->top = pos; |
@@ -127,11 +162,8 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | |||
127 | 162 | ||
128 | void ui_browser__refresh_dimensions(struct ui_browser *self) | 163 | void ui_browser__refresh_dimensions(struct ui_browser *self) |
129 | { | 164 | { |
130 | int cols, rows; | 165 | self->width = SLtt_Screen_Cols - 1; |
131 | newtGetScreenSize(&cols, &rows); | 166 | self->height = SLtt_Screen_Rows - 2; |
132 | |||
133 | self->width = cols - 1; | ||
134 | self->height = rows - 2; | ||
135 | self->y = 1; | 167 | self->y = 1; |
136 | self->x = 0; | 168 | self->x = 0; |
137 | } | 169 | } |
@@ -142,26 +174,11 @@ void ui_browser__reset_index(struct ui_browser *self) | |||
142 | self->seek(self, 0, SEEK_SET); | 174 | self->seek(self, 0, SEEK_SET); |
143 | } | 175 | } |
144 | 176 | ||
145 | void ui_browser__add_exit_key(struct ui_browser *self, int key) | ||
146 | { | ||
147 | newtFormAddHotKey(self->form, key); | ||
148 | } | ||
149 | |||
150 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | ||
151 | { | ||
152 | int i = 0; | ||
153 | |||
154 | while (keys[i] && i < 64) { | ||
155 | ui_browser__add_exit_key(self, keys[i]); | ||
156 | ++i; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | 177 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
161 | { | 178 | { |
162 | SLsmg_gotorc(0, 0); | 179 | SLsmg_gotorc(0, 0); |
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | 180 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); |
164 | slsmg_write_nstring(title, browser->width); | 181 | slsmg_write_nstring(title, browser->width + 1); |
165 | } | 182 | } |
166 | 183 | ||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | 184 | void ui_browser__show_title(struct ui_browser *browser, const char *title) |
@@ -174,78 +191,189 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title) | |||
174 | int ui_browser__show(struct ui_browser *self, const char *title, | 191 | int ui_browser__show(struct ui_browser *self, const char *title, |
175 | const char *helpline, ...) | 192 | const char *helpline, ...) |
176 | { | 193 | { |
194 | int err; | ||
177 | va_list ap; | 195 | va_list ap; |
178 | int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, | ||
179 | NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', | ||
180 | NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; | ||
181 | |||
182 | if (self->form != NULL) | ||
183 | newtFormDestroy(self->form); | ||
184 | 196 | ||
185 | ui_browser__refresh_dimensions(self); | 197 | ui_browser__refresh_dimensions(self); |
186 | self->form = newtForm(NULL, NULL, 0); | ||
187 | if (self->form == NULL) | ||
188 | return -1; | ||
189 | |||
190 | self->sb = newtVerticalScrollbar(self->width, 1, self->height, | ||
191 | HE_COLORSET_NORMAL, | ||
192 | HE_COLORSET_SELECTED); | ||
193 | if (self->sb == NULL) | ||
194 | return -1; | ||
195 | 198 | ||
196 | pthread_mutex_lock(&ui__lock); | 199 | pthread_mutex_lock(&ui__lock); |
197 | __ui_browser__show_title(self, title); | 200 | __ui_browser__show_title(self, title); |
198 | 201 | ||
199 | ui_browser__add_exit_keys(self, keys); | 202 | self->title = title; |
200 | newtFormAddComponent(self->form, self->sb); | 203 | free(self->helpline); |
204 | self->helpline = NULL; | ||
201 | 205 | ||
202 | va_start(ap, helpline); | 206 | va_start(ap, helpline); |
203 | ui_helpline__vpush(helpline, ap); | 207 | err = vasprintf(&self->helpline, helpline, ap); |
204 | va_end(ap); | 208 | va_end(ap); |
209 | if (err > 0) | ||
210 | ui_helpline__push(self->helpline); | ||
205 | pthread_mutex_unlock(&ui__lock); | 211 | pthread_mutex_unlock(&ui__lock); |
206 | return 0; | 212 | return err ? 0 : -1; |
207 | } | 213 | } |
208 | 214 | ||
209 | void ui_browser__hide(struct ui_browser *self) | 215 | void ui_browser__hide(struct ui_browser *browser __used) |
210 | { | 216 | { |
211 | pthread_mutex_lock(&ui__lock); | 217 | pthread_mutex_lock(&ui__lock); |
212 | newtFormDestroy(self->form); | ||
213 | self->form = NULL; | ||
214 | ui_helpline__pop(); | 218 | ui_helpline__pop(); |
215 | pthread_mutex_unlock(&ui__lock); | 219 | pthread_mutex_unlock(&ui__lock); |
216 | } | 220 | } |
217 | 221 | ||
218 | int ui_browser__refresh(struct ui_browser *self) | 222 | static void ui_browser__scrollbar_set(struct ui_browser *browser) |
223 | { | ||
224 | int height = browser->height, h = 0, pct = 0, | ||
225 | col = browser->width, | ||
226 | row = browser->y - 1; | ||
227 | |||
228 | if (browser->nr_entries > 1) { | ||
229 | pct = ((browser->index * (browser->height - 1)) / | ||
230 | (browser->nr_entries - 1)); | ||
231 | } | ||
232 | |||
233 | while (h < height) { | ||
234 | ui_browser__gotorc(browser, row++, col); | ||
235 | SLsmg_set_char_set(1); | ||
236 | SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR); | ||
237 | SLsmg_set_char_set(0); | ||
238 | ++h; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | static int __ui_browser__refresh(struct ui_browser *browser) | ||
219 | { | 243 | { |
220 | int row; | 244 | int row; |
245 | int width = browser->width; | ||
246 | |||
247 | row = browser->refresh(browser); | ||
248 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); | ||
249 | |||
250 | if (!browser->use_navkeypressed || browser->navkeypressed) | ||
251 | ui_browser__scrollbar_set(browser); | ||
252 | else | ||
253 | width += 1; | ||
221 | 254 | ||
255 | SLsmg_fill_region(browser->y + row, browser->x, | ||
256 | browser->height - row, width, ' '); | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | int ui_browser__refresh(struct ui_browser *browser) | ||
262 | { | ||
222 | pthread_mutex_lock(&ui__lock); | 263 | pthread_mutex_lock(&ui__lock); |
223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 264 | __ui_browser__refresh(browser); |
224 | row = self->refresh(self); | ||
225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); | ||
226 | SLsmg_fill_region(self->y + row, self->x, | ||
227 | self->height - row, self->width, ' '); | ||
228 | pthread_mutex_unlock(&ui__lock); | 265 | pthread_mutex_unlock(&ui__lock); |
229 | 266 | ||
230 | return 0; | 267 | return 0; |
231 | } | 268 | } |
232 | 269 | ||
233 | int ui_browser__run(struct ui_browser *self) | 270 | /* |
271 | * Here we're updating nr_entries _after_ we started browsing, i.e. we have to | ||
272 | * forget about any reference to any entry in the underlying data structure, | ||
273 | * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser | ||
274 | * after an output_resort and hist decay. | ||
275 | */ | ||
276 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) | ||
234 | { | 277 | { |
235 | struct newtExitStruct es; | 278 | off_t offset = nr_entries - browser->nr_entries; |
279 | |||
280 | browser->nr_entries = nr_entries; | ||
236 | 281 | ||
237 | if (ui_browser__refresh(self) < 0) | 282 | if (offset < 0) { |
238 | return -1; | 283 | if (browser->top_idx < (u64)-offset) |
284 | offset = -browser->top_idx; | ||
285 | |||
286 | browser->index += offset; | ||
287 | browser->top_idx += offset; | ||
288 | } | ||
289 | |||
290 | browser->top = NULL; | ||
291 | browser->seek(browser, browser->top_idx, SEEK_SET); | ||
292 | } | ||
293 | |||
294 | static int ui__getch(int delay_secs) | ||
295 | { | ||
296 | struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; | ||
297 | fd_set read_set; | ||
298 | int err, key; | ||
299 | |||
300 | FD_ZERO(&read_set); | ||
301 | FD_SET(0, &read_set); | ||
302 | |||
303 | if (delay_secs) { | ||
304 | timeout.tv_sec = delay_secs; | ||
305 | timeout.tv_usec = 0; | ||
306 | } | ||
307 | |||
308 | err = select(1, &read_set, NULL, NULL, ptimeout); | ||
309 | |||
310 | if (err == 0) | ||
311 | return K_TIMER; | ||
312 | |||
313 | if (err == -1) { | ||
314 | if (errno == EINTR) | ||
315 | return K_RESIZE; | ||
316 | return K_ERROR; | ||
317 | } | ||
318 | |||
319 | key = SLang_getkey(); | ||
320 | if (key != K_ESC) | ||
321 | return key; | ||
322 | |||
323 | FD_ZERO(&read_set); | ||
324 | FD_SET(0, &read_set); | ||
325 | timeout.tv_sec = 0; | ||
326 | timeout.tv_usec = 20; | ||
327 | err = select(1, &read_set, NULL, NULL, &timeout); | ||
328 | if (err == 0) | ||
329 | return K_ESC; | ||
330 | |||
331 | SLang_ungetkey(key); | ||
332 | return SLkp_getkey(); | ||
333 | } | ||
334 | |||
335 | int ui_browser__run(struct ui_browser *self, int delay_secs) | ||
336 | { | ||
337 | int err, key; | ||
338 | |||
339 | pthread__unblock_sigwinch(); | ||
239 | 340 | ||
240 | while (1) { | 341 | while (1) { |
241 | off_t offset; | 342 | off_t offset; |
242 | 343 | ||
243 | newtFormRun(self->form, &es); | 344 | pthread_mutex_lock(&ui__lock); |
244 | 345 | err = __ui_browser__refresh(self); | |
245 | if (es.reason != NEWT_EXIT_HOTKEY) | 346 | SLsmg_refresh(); |
347 | pthread_mutex_unlock(&ui__lock); | ||
348 | if (err < 0) | ||
246 | break; | 349 | break; |
247 | switch (es.u.key) { | 350 | |
248 | case NEWT_KEY_DOWN: | 351 | key = ui__getch(delay_secs); |
352 | |||
353 | if (key == K_RESIZE) { | ||
354 | pthread_mutex_lock(&ui__lock); | ||
355 | SLtt_get_screen_size(); | ||
356 | SLsmg_reinit_smg(); | ||
357 | pthread_mutex_unlock(&ui__lock); | ||
358 | ui_browser__refresh_dimensions(self); | ||
359 | __ui_browser__show_title(self, self->title); | ||
360 | ui_helpline__puts(self->helpline); | ||
361 | continue; | ||
362 | } | ||
363 | |||
364 | if (self->use_navkeypressed && !self->navkeypressed) { | ||
365 | if (key == K_DOWN || key == K_UP || | ||
366 | key == K_PGDN || key == K_PGUP || | ||
367 | key == K_HOME || key == K_END || | ||
368 | key == ' ') { | ||
369 | self->navkeypressed = true; | ||
370 | continue; | ||
371 | } else | ||
372 | return key; | ||
373 | } | ||
374 | |||
375 | switch (key) { | ||
376 | case K_DOWN: | ||
249 | if (self->index == self->nr_entries - 1) | 377 | if (self->index == self->nr_entries - 1) |
250 | break; | 378 | break; |
251 | ++self->index; | 379 | ++self->index; |
@@ -254,7 +382,7 @@ int ui_browser__run(struct ui_browser *self) | |||
254 | self->seek(self, +1, SEEK_CUR); | 382 | self->seek(self, +1, SEEK_CUR); |
255 | } | 383 | } |
256 | break; | 384 | break; |
257 | case NEWT_KEY_UP: | 385 | case K_UP: |
258 | if (self->index == 0) | 386 | if (self->index == 0) |
259 | break; | 387 | break; |
260 | --self->index; | 388 | --self->index; |
@@ -263,7 +391,7 @@ int ui_browser__run(struct ui_browser *self) | |||
263 | self->seek(self, -1, SEEK_CUR); | 391 | self->seek(self, -1, SEEK_CUR); |
264 | } | 392 | } |
265 | break; | 393 | break; |
266 | case NEWT_KEY_PGDN: | 394 | case K_PGDN: |
267 | case ' ': | 395 | case ' ': |
268 | if (self->top_idx + self->height > self->nr_entries - 1) | 396 | if (self->top_idx + self->height > self->nr_entries - 1) |
269 | break; | 397 | break; |
@@ -275,7 +403,7 @@ int ui_browser__run(struct ui_browser *self) | |||
275 | self->top_idx += offset; | 403 | self->top_idx += offset; |
276 | self->seek(self, +offset, SEEK_CUR); | 404 | self->seek(self, +offset, SEEK_CUR); |
277 | break; | 405 | break; |
278 | case NEWT_KEY_PGUP: | 406 | case K_PGUP: |
279 | if (self->top_idx == 0) | 407 | if (self->top_idx == 0) |
280 | break; | 408 | break; |
281 | 409 | ||
@@ -288,10 +416,10 @@ int ui_browser__run(struct ui_browser *self) | |||
288 | self->top_idx -= offset; | 416 | self->top_idx -= offset; |
289 | self->seek(self, -offset, SEEK_CUR); | 417 | self->seek(self, -offset, SEEK_CUR); |
290 | break; | 418 | break; |
291 | case NEWT_KEY_HOME: | 419 | case K_HOME: |
292 | ui_browser__reset_index(self); | 420 | ui_browser__reset_index(self); |
293 | break; | 421 | break; |
294 | case NEWT_KEY_END: | 422 | case K_END: |
295 | offset = self->height - 1; | 423 | offset = self->height - 1; |
296 | if (offset >= self->nr_entries) | 424 | if (offset >= self->nr_entries) |
297 | offset = self->nr_entries - 1; | 425 | offset = self->nr_entries - 1; |
@@ -301,10 +429,8 @@ int ui_browser__run(struct ui_browser *self) | |||
301 | self->seek(self, -offset, SEEK_END); | 429 | self->seek(self, -offset, SEEK_END); |
302 | break; | 430 | break; |
303 | default: | 431 | default: |
304 | return es.u.key; | 432 | return key; |
305 | } | 433 | } |
306 | if (ui_browser__refresh(self) < 0) | ||
307 | return -1; | ||
308 | } | 434 | } |
309 | return -1; | 435 | return -1; |
310 | } | 436 | } |
@@ -316,41 +442,105 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |||
316 | int row = 0; | 442 | int row = 0; |
317 | 443 | ||
318 | if (self->top == NULL || self->top == self->entries) | 444 | if (self->top == NULL || self->top == self->entries) |
319 | self->top = head->next; | 445 | self->top = ui_browser__list_head_filter_entries(self, head->next); |
320 | 446 | ||
321 | pos = self->top; | 447 | pos = self->top; |
322 | 448 | ||
323 | list_for_each_from(pos, head) { | 449 | list_for_each_from(pos, head) { |
324 | ui_browser__gotorc(self, row, 0); | 450 | if (!self->filter || !self->filter(self, pos)) { |
325 | self->write(self, pos, row); | 451 | ui_browser__gotorc(self, row, 0); |
326 | if (++row == self->height) | 452 | self->write(self, pos, row); |
327 | break; | 453 | if (++row == self->height) |
454 | break; | ||
455 | } | ||
328 | } | 456 | } |
329 | 457 | ||
330 | return row; | 458 | return row; |
331 | } | 459 | } |
332 | 460 | ||
333 | static struct newtPercentTreeColors { | 461 | static struct ui_browser__colorset { |
334 | const char *topColorFg, *topColorBg; | 462 | const char *name, *fg, *bg; |
335 | const char *mediumColorFg, *mediumColorBg; | 463 | int colorset; |
336 | const char *normalColorFg, *normalColorBg; | 464 | } ui_browser__colorsets[] = { |
337 | const char *selColorFg, *selColorBg; | 465 | { |
338 | const char *codeColorFg, *codeColorBg; | 466 | .colorset = HE_COLORSET_TOP, |
339 | } defaultPercentTreeColors = { | 467 | .name = "top", |
340 | "red", "lightgray", | 468 | .fg = "red", |
341 | "green", "lightgray", | 469 | .bg = "default", |
342 | "black", "lightgray", | 470 | }, |
343 | "lightgray", "magenta", | 471 | { |
344 | "blue", "lightgray", | 472 | .colorset = HE_COLORSET_MEDIUM, |
473 | .name = "medium", | ||
474 | .fg = "green", | ||
475 | .bg = "default", | ||
476 | }, | ||
477 | { | ||
478 | .colorset = HE_COLORSET_NORMAL, | ||
479 | .name = "normal", | ||
480 | .fg = "default", | ||
481 | .bg = "default", | ||
482 | }, | ||
483 | { | ||
484 | .colorset = HE_COLORSET_SELECTED, | ||
485 | .name = "selected", | ||
486 | .fg = "black", | ||
487 | .bg = "lightgray", | ||
488 | }, | ||
489 | { | ||
490 | .colorset = HE_COLORSET_CODE, | ||
491 | .name = "code", | ||
492 | .fg = "blue", | ||
493 | .bg = "default", | ||
494 | }, | ||
495 | { | ||
496 | .name = NULL, | ||
497 | } | ||
345 | }; | 498 | }; |
346 | 499 | ||
500 | |||
501 | static int ui_browser__color_config(const char *var, const char *value, | ||
502 | void *data __used) | ||
503 | { | ||
504 | char *fg = NULL, *bg; | ||
505 | int i; | ||
506 | |||
507 | /* same dir for all commands */ | ||
508 | if (prefixcmp(var, "colors.") != 0) | ||
509 | return 0; | ||
510 | |||
511 | for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { | ||
512 | const char *name = var + 7; | ||
513 | |||
514 | if (strcmp(ui_browser__colorsets[i].name, name) != 0) | ||
515 | continue; | ||
516 | |||
517 | fg = strdup(value); | ||
518 | if (fg == NULL) | ||
519 | break; | ||
520 | |||
521 | bg = strchr(fg, ','); | ||
522 | if (bg == NULL) | ||
523 | break; | ||
524 | |||
525 | *bg = '\0'; | ||
526 | while (isspace(*++bg)); | ||
527 | ui_browser__colorsets[i].bg = bg; | ||
528 | ui_browser__colorsets[i].fg = fg; | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | free(fg); | ||
533 | return -1; | ||
534 | } | ||
535 | |||
347 | void ui_browser__init(void) | 536 | void ui_browser__init(void) |
348 | { | 537 | { |
349 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 538 | int i = 0; |
350 | 539 | ||
351 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 540 | perf_config(ui_browser__color_config, NULL); |
352 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 541 | |
353 | sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | 542 | while (ui_browser__colorsets[i].name) { |
354 | sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | 543 | struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; |
355 | sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | 544 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); |
545 | } | ||
356 | } | 546 | } |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index fc63dda10910..a2c707d33c5e 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -2,7 +2,6 @@ | |||
2 | #define _PERF_UI_BROWSER_H_ 1 | 2 | #define _PERF_UI_BROWSER_H_ 1 |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <newt.h> | ||
6 | #include <sys/types.h> | 5 | #include <sys/types.h> |
7 | #include "../types.h" | 6 | #include "../types.h" |
8 | 7 | ||
@@ -13,15 +12,19 @@ | |||
13 | #define HE_COLORSET_CODE 54 | 12 | #define HE_COLORSET_CODE 54 |
14 | 13 | ||
15 | struct ui_browser { | 14 | struct ui_browser { |
16 | newtComponent form, sb; | ||
17 | u64 index, top_idx; | 15 | u64 index, top_idx; |
18 | void *top, *entries; | 16 | void *top, *entries; |
19 | u16 y, x, width, height; | 17 | u16 y, x, width, height; |
20 | void *priv; | 18 | void *priv; |
19 | const char *title; | ||
20 | char *helpline; | ||
21 | unsigned int (*refresh)(struct ui_browser *self); | 21 | unsigned int (*refresh)(struct ui_browser *self); |
22 | void (*write)(struct ui_browser *self, void *entry, int row); | 22 | void (*write)(struct ui_browser *self, void *entry, int row); |
23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); | 23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); |
24 | bool (*filter)(struct ui_browser *self, void *entry); | ||
24 | u32 nr_entries; | 25 | u32 nr_entries; |
26 | bool navkeypressed; | ||
27 | bool use_navkeypressed; | ||
25 | }; | 28 | }; |
26 | 29 | ||
27 | void ui_browser__set_color(struct ui_browser *self, int color); | 30 | void ui_browser__set_color(struct ui_browser *self, int color); |
@@ -32,15 +35,14 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); | |||
32 | void ui_browser__reset_index(struct ui_browser *self); | 35 | void ui_browser__reset_index(struct ui_browser *self); |
33 | 36 | ||
34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | 37 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); |
35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | ||
36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | ||
37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | 38 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); |
38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | 39 | void ui_browser__show_title(struct ui_browser *browser, const char *title); |
39 | int ui_browser__show(struct ui_browser *self, const char *title, | 40 | int ui_browser__show(struct ui_browser *self, const char *title, |
40 | const char *helpline, ...); | 41 | const char *helpline, ...); |
41 | void ui_browser__hide(struct ui_browser *self); | 42 | void ui_browser__hide(struct ui_browser *self); |
42 | int ui_browser__refresh(struct ui_browser *self); | 43 | int ui_browser__refresh(struct ui_browser *self); |
43 | int ui_browser__run(struct ui_browser *self); | 44 | int ui_browser__run(struct ui_browser *browser, int delay_secs); |
45 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); | ||
44 | 46 | ||
45 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); | 47 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); |
46 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); | 48 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb3..4e0cb7fea7d9 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "../../sort.h" | 6 | #include "../../sort.h" |
7 | #include "../../symbol.h" | 7 | #include "../../symbol.h" |
8 | #include <pthread.h> | 8 | #include <pthread.h> |
9 | #include <newt.h> | ||
9 | 10 | ||
10 | static void ui__error_window(const char *fmt, ...) | 11 | static void ui__error_window(const char *fmt, ...) |
11 | { | 12 | { |
@@ -20,12 +21,17 @@ struct annotate_browser { | |||
20 | struct ui_browser b; | 21 | struct ui_browser b; |
21 | struct rb_root entries; | 22 | struct rb_root entries; |
22 | struct rb_node *curr_hot; | 23 | struct rb_node *curr_hot; |
24 | struct objdump_line *selection; | ||
25 | int nr_asm_entries; | ||
26 | int nr_entries; | ||
27 | bool hide_src_code; | ||
23 | }; | 28 | }; |
24 | 29 | ||
25 | struct objdump_line_rb_node { | 30 | struct objdump_line_rb_node { |
26 | struct rb_node rb_node; | 31 | struct rb_node rb_node; |
27 | double percent; | 32 | double percent; |
28 | u32 idx; | 33 | u32 idx; |
34 | int idx_asm; | ||
29 | }; | 35 | }; |
30 | 36 | ||
31 | static inline | 37 | static inline |
@@ -34,9 +40,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) | |||
34 | return (struct objdump_line_rb_node *)(self + 1); | 40 | return (struct objdump_line_rb_node *)(self + 1); |
35 | } | 41 | } |
36 | 42 | ||
43 | static bool objdump_line__filter(struct ui_browser *browser, void *entry) | ||
44 | { | ||
45 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
46 | |||
47 | if (ab->hide_src_code) { | ||
48 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
49 | return ol->offset == -1; | ||
50 | } | ||
51 | |||
52 | return false; | ||
53 | } | ||
54 | |||
37 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) | 55 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) |
38 | { | 56 | { |
39 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); | 57 | struct annotate_browser *ab = container_of(self, struct annotate_browser, b); |
58 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
40 | bool current_entry = ui_browser__is_current_entry(self, row); | 59 | bool current_entry = ui_browser__is_current_entry(self, row); |
41 | int width = self->width; | 60 | int width = self->width; |
42 | 61 | ||
@@ -51,6 +70,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
51 | 70 | ||
52 | SLsmg_write_char(':'); | 71 | SLsmg_write_char(':'); |
53 | slsmg_write_nstring(" ", 8); | 72 | slsmg_write_nstring(" ", 8); |
73 | |||
74 | /* The scroll bar isn't being used */ | ||
75 | if (!self->navkeypressed) | ||
76 | width += 1; | ||
77 | |||
54 | if (!*ol->line) | 78 | if (!*ol->line) |
55 | slsmg_write_nstring(" ", width - 18); | 79 | slsmg_write_nstring(" ", width - 18); |
56 | else | 80 | else |
@@ -58,6 +82,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
58 | 82 | ||
59 | if (!current_entry) | 83 | if (!current_entry) |
60 | ui_browser__set_color(self, HE_COLORSET_CODE); | 84 | ui_browser__set_color(self, HE_COLORSET_CODE); |
85 | else | ||
86 | ab->selection = ol; | ||
61 | } | 87 | } |
62 | 88 | ||
63 | static double objdump_line__calc_percent(struct objdump_line *self, | 89 | static double objdump_line__calc_percent(struct objdump_line *self, |
@@ -141,7 +167,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 167 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
142 | int evidx) | 168 | int evidx) |
143 | { | 169 | { |
144 | struct symbol *sym = browser->b.priv; | 170 | struct map_symbol *ms = browser->b.priv; |
171 | struct symbol *sym = ms->sym; | ||
145 | struct annotation *notes = symbol__annotation(sym); | 172 | struct annotation *notes = symbol__annotation(sym); |
146 | struct objdump_line *pos; | 173 | struct objdump_line *pos; |
147 | 174 | ||
@@ -163,25 +190,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
163 | browser->curr_hot = rb_last(&browser->entries); | 190 | browser->curr_hot = rb_last(&browser->entries); |
164 | } | 191 | } |
165 | 192 | ||
193 | static bool annotate_browser__toggle_source(struct annotate_browser *browser) | ||
194 | { | ||
195 | struct objdump_line *ol; | ||
196 | struct objdump_line_rb_node *olrb; | ||
197 | off_t offset = browser->b.index - browser->b.top_idx; | ||
198 | |||
199 | browser->b.seek(&browser->b, offset, SEEK_CUR); | ||
200 | ol = list_entry(browser->b.top, struct objdump_line, node); | ||
201 | olrb = objdump_line__rb(ol); | ||
202 | |||
203 | if (browser->hide_src_code) { | ||
204 | if (olrb->idx_asm < offset) | ||
205 | offset = olrb->idx; | ||
206 | |||
207 | browser->b.nr_entries = browser->nr_entries; | ||
208 | browser->hide_src_code = false; | ||
209 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
210 | browser->b.top_idx = olrb->idx - offset; | ||
211 | browser->b.index = olrb->idx; | ||
212 | } else { | ||
213 | if (olrb->idx_asm < 0) { | ||
214 | ui_helpline__puts("Only available for assembly lines."); | ||
215 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
216 | return false; | ||
217 | } | ||
218 | |||
219 | if (olrb->idx_asm < offset) | ||
220 | offset = olrb->idx_asm; | ||
221 | |||
222 | browser->b.nr_entries = browser->nr_asm_entries; | ||
223 | browser->hide_src_code = true; | ||
224 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
225 | browser->b.top_idx = olrb->idx_asm - offset; | ||
226 | browser->b.index = olrb->idx_asm; | ||
227 | } | ||
228 | |||
229 | return true; | ||
230 | } | ||
231 | |||
166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | 232 | static int annotate_browser__run(struct annotate_browser *self, int evidx, |
167 | int refresh) | 233 | int nr_events, void(*timer)(void *arg), |
234 | void *arg, int delay_secs) | ||
168 | { | 235 | { |
169 | struct rb_node *nd = NULL; | 236 | struct rb_node *nd = NULL; |
170 | struct symbol *sym = self->b.priv; | 237 | struct map_symbol *ms = self->b.priv; |
171 | /* | 238 | struct symbol *sym = ms->sym; |
172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | 239 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " |
173 | * examining the exit key for this function. | 240 | "H: Hottest, -> Line action, S -> Toggle source " |
174 | */ | 241 | "code view"; |
175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | ||
176 | NEWT_KEY_RIGHT, 0 }; | ||
177 | int key; | 242 | int key; |
178 | 243 | ||
179 | if (ui_browser__show(&self->b, sym->name, | 244 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
180 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
181 | "cycle hottest lines, H: Hottest") < 0) | ||
182 | return -1; | 245 | return -1; |
183 | 246 | ||
184 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
185 | annotate_browser__calc_percent(self, evidx); | 247 | annotate_browser__calc_percent(self, evidx); |
186 | 248 | ||
187 | if (self->curr_hot) | 249 | if (self->curr_hot) |
@@ -189,13 +251,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
189 | 251 | ||
190 | nd = self->curr_hot; | 252 | nd = self->curr_hot; |
191 | 253 | ||
192 | if (refresh != 0) | ||
193 | newtFormSetTimer(self->b.form, refresh); | ||
194 | |||
195 | while (1) { | 254 | while (1) { |
196 | key = ui_browser__run(&self->b); | 255 | key = ui_browser__run(&self->b, delay_secs); |
197 | 256 | ||
198 | if (refresh != 0) { | 257 | if (delay_secs != 0) { |
199 | annotate_browser__calc_percent(self, evidx); | 258 | annotate_browser__calc_percent(self, evidx); |
200 | /* | 259 | /* |
201 | * Current line focus got out of the list of most active | 260 | * Current line focus got out of the list of most active |
@@ -207,15 +266,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
207 | } | 266 | } |
208 | 267 | ||
209 | switch (key) { | 268 | switch (key) { |
210 | case -1: | 269 | case K_TIMER: |
211 | /* | 270 | if (timer != NULL) |
212 | * FIXME we need to check if it was | 271 | timer(arg); |
213 | * es.reason == NEWT_EXIT_TIMER | 272 | |
214 | */ | 273 | if (delay_secs != 0) |
215 | if (refresh != 0) | ||
216 | symbol__annotate_decay_histogram(sym, evidx); | 274 | symbol__annotate_decay_histogram(sym, evidx); |
217 | continue; | 275 | continue; |
218 | case NEWT_KEY_TAB: | 276 | case K_TAB: |
219 | if (nd != NULL) { | 277 | if (nd != NULL) { |
220 | nd = rb_prev(nd); | 278 | nd = rb_prev(nd); |
221 | if (nd == NULL) | 279 | if (nd == NULL) |
@@ -223,7 +281,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
223 | } else | 281 | } else |
224 | nd = self->curr_hot; | 282 | nd = self->curr_hot; |
225 | break; | 283 | break; |
226 | case NEWT_KEY_UNTAB: | 284 | case K_UNTAB: |
227 | if (nd != NULL) | 285 | if (nd != NULL) |
228 | nd = rb_next(nd); | 286 | nd = rb_next(nd); |
229 | if (nd == NULL) | 287 | if (nd == NULL) |
@@ -234,8 +292,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
234 | case 'H': | 292 | case 'H': |
235 | nd = self->curr_hot; | 293 | nd = self->curr_hot; |
236 | break; | 294 | break; |
237 | default: | 295 | case 'S': |
296 | if (annotate_browser__toggle_source(self)) | ||
297 | ui_helpline__puts(help); | ||
298 | continue; | ||
299 | case K_ENTER: | ||
300 | case K_RIGHT: | ||
301 | if (self->selection == NULL) { | ||
302 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | ||
303 | continue; | ||
304 | } | ||
305 | |||
306 | if (self->selection->offset == -1) { | ||
307 | ui_helpline__puts("Actions are only available for assembly lines."); | ||
308 | continue; | ||
309 | } else { | ||
310 | char *s = strstr(self->selection->line, "callq "); | ||
311 | struct annotation *notes; | ||
312 | struct symbol *target; | ||
313 | u64 ip; | ||
314 | |||
315 | if (s == NULL) { | ||
316 | ui_helpline__puts("Actions are only available for the 'callq' instruction."); | ||
317 | continue; | ||
318 | } | ||
319 | |||
320 | s = strchr(s, ' '); | ||
321 | if (s++ == NULL) { | ||
322 | ui_helpline__puts("Invallid callq instruction."); | ||
323 | continue; | ||
324 | } | ||
325 | |||
326 | ip = strtoull(s, NULL, 16); | ||
327 | ip = ms->map->map_ip(ms->map, ip); | ||
328 | target = map__find_symbol(ms->map, ip, NULL); | ||
329 | if (target == NULL) { | ||
330 | ui_helpline__puts("The called function was not found."); | ||
331 | continue; | ||
332 | } | ||
333 | |||
334 | notes = symbol__annotation(target); | ||
335 | pthread_mutex_lock(¬es->lock); | ||
336 | |||
337 | if (notes->src == NULL && | ||
338 | symbol__alloc_hist(target, nr_events) < 0) { | ||
339 | pthread_mutex_unlock(¬es->lock); | ||
340 | ui__warning("Not enough memory for annotating '%s' symbol!\n", | ||
341 | target->name); | ||
342 | continue; | ||
343 | } | ||
344 | |||
345 | pthread_mutex_unlock(¬es->lock); | ||
346 | symbol__tui_annotate(target, ms->map, evidx, nr_events, | ||
347 | timer, arg, delay_secs); | ||
348 | } | ||
349 | continue; | ||
350 | case K_LEFT: | ||
351 | case K_ESC: | ||
352 | case 'q': | ||
353 | case CTRL('c'): | ||
238 | goto out; | 354 | goto out; |
355 | default: | ||
356 | continue; | ||
239 | } | 357 | } |
240 | 358 | ||
241 | if (nd != NULL) | 359 | if (nd != NULL) |
@@ -246,22 +364,31 @@ out: | |||
246 | return key; | 364 | return key; |
247 | } | 365 | } |
248 | 366 | ||
249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | 367 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
368 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
250 | { | 369 | { |
251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | 370 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, |
371 | timer, arg, delay_secs); | ||
252 | } | 372 | } |
253 | 373 | ||
254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 374 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
255 | int refresh) | 375 | int nr_events, void(*timer)(void *arg), void *arg, |
376 | int delay_secs) | ||
256 | { | 377 | { |
257 | struct objdump_line *pos, *n; | 378 | struct objdump_line *pos, *n; |
258 | struct annotation *notes; | 379 | struct annotation *notes; |
380 | struct map_symbol ms = { | ||
381 | .map = map, | ||
382 | .sym = sym, | ||
383 | }; | ||
259 | struct annotate_browser browser = { | 384 | struct annotate_browser browser = { |
260 | .b = { | 385 | .b = { |
261 | .refresh = ui_browser__list_head_refresh, | 386 | .refresh = ui_browser__list_head_refresh, |
262 | .seek = ui_browser__list_head_seek, | 387 | .seek = ui_browser__list_head_seek, |
263 | .write = annotate_browser__write, | 388 | .write = annotate_browser__write, |
264 | .priv = sym, | 389 | .filter = objdump_line__filter, |
390 | .priv = &ms, | ||
391 | .use_navkeypressed = true, | ||
265 | }, | 392 | }, |
266 | }; | 393 | }; |
267 | int ret; | 394 | int ret; |
@@ -288,12 +415,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
288 | if (browser.b.width < line_len) | 415 | if (browser.b.width < line_len) |
289 | browser.b.width = line_len; | 416 | browser.b.width = line_len; |
290 | rbpos = objdump_line__rb(pos); | 417 | rbpos = objdump_line__rb(pos); |
291 | rbpos->idx = browser.b.nr_entries++; | 418 | rbpos->idx = browser.nr_entries++; |
419 | if (pos->offset != -1) | ||
420 | rbpos->idx_asm = browser.nr_asm_entries++; | ||
421 | else | ||
422 | rbpos->idx_asm = -1; | ||
292 | } | 423 | } |
293 | 424 | ||
425 | browser.b.nr_entries = browser.nr_entries; | ||
294 | browser.b.entries = ¬es->src->source, | 426 | browser.b.entries = ¬es->src->source, |
295 | browser.b.width += 18; /* Percentage */ | 427 | browser.b.width += 18; /* Percentage */ |
296 | ret = annotate_browser__run(&browser, evidx, refresh); | 428 | ret = annotate_browser__run(&browser, evidx, nr_events, |
429 | timer, arg, delay_secs); | ||
297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 430 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
298 | list_del(&pos->node); | 431 | list_del(&pos->node); |
299 | objdump_line__free(pos); | 432 | objdump_line__free(pos); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622dfc..4663dcb2a19b 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -24,8 +24,12 @@ struct hist_browser { | |||
24 | struct hists *hists; | 24 | struct hists *hists; |
25 | struct hist_entry *he_selection; | 25 | struct hist_entry *he_selection; |
26 | struct map_symbol *selection; | 26 | struct map_symbol *selection; |
27 | bool has_symbols; | ||
27 | }; | 28 | }; |
28 | 29 | ||
30 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||
31 | const char *ev_name); | ||
32 | |||
29 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | 33 | static void hist_browser__refresh_dimensions(struct hist_browser *self) |
30 | { | 34 | { |
31 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | 35 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ |
@@ -290,28 +294,34 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | |||
290 | ui_browser__reset_index(&self->b); | 294 | ui_browser__reset_index(&self->b); |
291 | } | 295 | } |
292 | 296 | ||
293 | static int hist_browser__run(struct hist_browser *self, const char *title) | 297 | static int hist_browser__run(struct hist_browser *self, const char *ev_name, |
298 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
294 | { | 299 | { |
295 | int key; | 300 | int key; |
296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 301 | char title[160]; |
297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | ||
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
299 | 302 | ||
300 | self->b.entries = &self->hists->entries; | 303 | self->b.entries = &self->hists->entries; |
301 | self->b.nr_entries = self->hists->nr_entries; | 304 | self->b.nr_entries = self->hists->nr_entries; |
302 | 305 | ||
303 | hist_browser__refresh_dimensions(self); | 306 | hist_browser__refresh_dimensions(self); |
307 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
304 | 308 | ||
305 | if (ui_browser__show(&self->b, title, | 309 | if (ui_browser__show(&self->b, title, |
306 | "Press '?' for help on key bindings") < 0) | 310 | "Press '?' for help on key bindings") < 0) |
307 | return -1; | 311 | return -1; |
308 | 312 | ||
309 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
310 | |||
311 | while (1) { | 313 | while (1) { |
312 | key = ui_browser__run(&self->b); | 314 | key = ui_browser__run(&self->b, delay_secs); |
313 | 315 | ||
314 | switch (key) { | 316 | switch (key) { |
317 | case -1: | ||
318 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
319 | timer(arg); | ||
320 | ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); | ||
321 | hists__browser_title(self->hists, title, sizeof(title), | ||
322 | ev_name); | ||
323 | ui_browser__show_title(&self->b, title); | ||
324 | continue; | ||
315 | case 'D': { /* Debug */ | 325 | case 'D': { /* Debug */ |
316 | static int seq; | 326 | static int seq; |
317 | struct hist_entry *h = rb_entry(self->b.top, | 327 | struct hist_entry *h = rb_entry(self->b.top, |
@@ -334,7 +344,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
334 | /* Expand the whole world. */ | 344 | /* Expand the whole world. */ |
335 | hist_browser__set_folding(self, true); | 345 | hist_browser__set_folding(self, true); |
336 | break; | 346 | break; |
337 | case NEWT_KEY_ENTER: | 347 | case K_ENTER: |
338 | if (hist_browser__toggle_fold(self)) | 348 | if (hist_browser__toggle_fold(self)) |
339 | break; | 349 | break; |
340 | /* fall thru */ | 350 | /* fall thru */ |
@@ -532,7 +542,7 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
532 | char s[256]; | 542 | char s[256]; |
533 | double percent; | 543 | double percent; |
534 | int printed = 0; | 544 | int printed = 0; |
535 | int color, width = self->b.width; | 545 | int width = self->b.width - 6; /* The percentage */ |
536 | char folded_sign = ' '; | 546 | char folded_sign = ' '; |
537 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | 547 | bool current_entry = ui_browser__is_current_entry(&self->b, row); |
538 | off_t row_offset = entry->row_offset; | 548 | off_t row_offset = entry->row_offset; |
@@ -548,26 +558,35 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
548 | } | 558 | } |
549 | 559 | ||
550 | if (row_offset == 0) { | 560 | if (row_offset == 0) { |
551 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | 561 | hist_entry__snprintf(entry, s, sizeof(s), self->hists); |
552 | 0, false, self->hists->stats.total_period); | ||
553 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | 562 | percent = (entry->period * 100.0) / self->hists->stats.total_period; |
554 | 563 | ||
555 | color = HE_COLORSET_SELECTED; | 564 | ui_browser__set_percent_color(&self->b, percent, current_entry); |
556 | if (!current_entry) { | ||
557 | if (percent >= MIN_RED) | ||
558 | color = HE_COLORSET_TOP; | ||
559 | else if (percent >= MIN_GREEN) | ||
560 | color = HE_COLORSET_MEDIUM; | ||
561 | else | ||
562 | color = HE_COLORSET_NORMAL; | ||
563 | } | ||
564 | |||
565 | ui_browser__set_color(&self->b, color); | ||
566 | ui_browser__gotorc(&self->b, row, 0); | 565 | ui_browser__gotorc(&self->b, row, 0); |
567 | if (symbol_conf.use_callchain) { | 566 | if (symbol_conf.use_callchain) { |
568 | slsmg_printf("%c ", folded_sign); | 567 | slsmg_printf("%c ", folded_sign); |
569 | width -= 2; | 568 | width -= 2; |
570 | } | 569 | } |
570 | |||
571 | slsmg_printf(" %5.2f%%", percent); | ||
572 | |||
573 | /* The scroll bar isn't being used */ | ||
574 | if (!self->b.navkeypressed) | ||
575 | width += 1; | ||
576 | |||
577 | if (!current_entry || !self->b.navkeypressed) | ||
578 | ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); | ||
579 | |||
580 | if (symbol_conf.show_nr_samples) { | ||
581 | slsmg_printf(" %11u", entry->nr_events); | ||
582 | width -= 12; | ||
583 | } | ||
584 | |||
585 | if (symbol_conf.show_total_period) { | ||
586 | slsmg_printf(" %12" PRIu64, entry->period); | ||
587 | width -= 13; | ||
588 | } | ||
589 | |||
571 | slsmg_write_nstring(s, width); | 590 | slsmg_write_nstring(s, width); |
572 | ++row; | 591 | ++row; |
573 | ++printed; | 592 | ++printed; |
@@ -585,14 +604,23 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
585 | return printed; | 604 | return printed; |
586 | } | 605 | } |
587 | 606 | ||
607 | static void ui_browser__hists_init_top(struct ui_browser *browser) | ||
608 | { | ||
609 | if (browser->top == NULL) { | ||
610 | struct hist_browser *hb; | ||
611 | |||
612 | hb = container_of(browser, struct hist_browser, b); | ||
613 | browser->top = rb_first(&hb->hists->entries); | ||
614 | } | ||
615 | } | ||
616 | |||
588 | static unsigned int hist_browser__refresh(struct ui_browser *self) | 617 | static unsigned int hist_browser__refresh(struct ui_browser *self) |
589 | { | 618 | { |
590 | unsigned row = 0; | 619 | unsigned row = 0; |
591 | struct rb_node *nd; | 620 | struct rb_node *nd; |
592 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | 621 | struct hist_browser *hb = container_of(self, struct hist_browser, b); |
593 | 622 | ||
594 | if (self->top == NULL) | 623 | ui_browser__hists_init_top(self); |
595 | self->top = rb_first(&hb->hists->entries); | ||
596 | 624 | ||
597 | for (nd = self->top; nd; nd = rb_next(nd)) { | 625 | for (nd = self->top; nd; nd = rb_next(nd)) { |
598 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 626 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -644,6 +672,8 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
644 | if (self->nr_entries == 0) | 672 | if (self->nr_entries == 0) |
645 | return; | 673 | return; |
646 | 674 | ||
675 | ui_browser__hists_init_top(self); | ||
676 | |||
647 | switch (whence) { | 677 | switch (whence) { |
648 | case SEEK_SET: | 678 | case SEEK_SET: |
649 | nd = hists__filter_entries(rb_first(self->entries)); | 679 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -761,6 +791,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
761 | self->hists = hists; | 791 | self->hists = hists; |
762 | self->b.refresh = hist_browser__refresh; | 792 | self->b.refresh = hist_browser__refresh; |
763 | self->b.seek = ui_browser__hists_seek; | 793 | self->b.seek = ui_browser__hists_seek; |
794 | self->b.use_navkeypressed = true, | ||
795 | self->has_symbols = sort_sym.list.next != NULL; | ||
764 | } | 796 | } |
765 | 797 | ||
766 | return self; | 798 | return self; |
@@ -782,11 +814,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
782 | } | 814 | } |
783 | 815 | ||
784 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | 816 | static int hists__browser_title(struct hists *self, char *bf, size_t size, |
785 | const char *ev_name, const struct dso *dso, | 817 | const char *ev_name) |
786 | const struct thread *thread) | ||
787 | { | 818 | { |
788 | char unit; | 819 | char unit; |
789 | int printed; | 820 | int printed; |
821 | const struct dso *dso = self->dso_filter; | ||
822 | const struct thread *thread = self->thread_filter; | ||
790 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 823 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
791 | 824 | ||
792 | nr_events = convert_unit(nr_events, &unit); | 825 | nr_events = convert_unit(nr_events, &unit); |
@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
803 | return printed; | 836 | return printed; |
804 | } | 837 | } |
805 | 838 | ||
806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, | 839 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
807 | const char *helpline, const char *ev_name, | 840 | const char *helpline, const char *ev_name, |
808 | bool left_exits) | 841 | bool left_exits, |
842 | void(*timer)(void *arg), void *arg, | ||
843 | int delay_secs) | ||
809 | { | 844 | { |
810 | struct hists *self = &evsel->hists; | 845 | struct hists *self = &evsel->hists; |
811 | struct hist_browser *browser = hist_browser__new(self); | 846 | struct hist_browser *browser = hist_browser__new(self); |
812 | struct pstack *fstack; | 847 | struct pstack *fstack; |
813 | const struct thread *thread_filter = NULL; | ||
814 | const struct dso *dso_filter = NULL; | ||
815 | char msg[160]; | ||
816 | int key = -1; | 848 | int key = -1; |
817 | 849 | ||
818 | if (browser == NULL) | 850 | if (browser == NULL) |
@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
824 | 856 | ||
825 | ui_helpline__push(helpline); | 857 | ui_helpline__push(helpline); |
826 | 858 | ||
827 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
828 | dso_filter, thread_filter); | ||
829 | while (1) { | 859 | while (1) { |
830 | const struct thread *thread = NULL; | 860 | const struct thread *thread = NULL; |
831 | const struct dso *dso = NULL; | 861 | const struct dso *dso = NULL; |
@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 864 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
835 | browse_map = -2; | 865 | browse_map = -2; |
836 | 866 | ||
837 | key = hist_browser__run(browser, msg); | 867 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
838 | 868 | ||
839 | if (browser->he_selection != NULL) { | 869 | if (browser->he_selection != NULL) { |
840 | thread = hist_browser__selected_thread(browser); | 870 | thread = hist_browser__selected_thread(browser); |
@@ -842,14 +872,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
842 | } | 872 | } |
843 | 873 | ||
844 | switch (key) { | 874 | switch (key) { |
845 | case NEWT_KEY_TAB: | 875 | case K_TAB: |
846 | case NEWT_KEY_UNTAB: | 876 | case K_UNTAB: |
877 | if (nr_events == 1) | ||
878 | continue; | ||
847 | /* | 879 | /* |
848 | * Exit the browser, let hists__browser_tree | 880 | * Exit the browser, let hists__browser_tree |
849 | * go to the next or previous | 881 | * go to the next or previous |
850 | */ | 882 | */ |
851 | goto out_free_stack; | 883 | goto out_free_stack; |
852 | case 'a': | 884 | case 'a': |
885 | if (!browser->has_symbols) { | ||
886 | ui__warning( | ||
887 | "Annotation is only available for symbolic views, " | ||
888 | "include \"sym\" in --sort to use it."); | ||
889 | continue; | ||
890 | } | ||
891 | |||
853 | if (browser->selection == NULL || | 892 | if (browser->selection == NULL || |
854 | browser->selection->sym == NULL || | 893 | browser->selection->sym == NULL || |
855 | browser->selection->map->dso->annotate_warned) | 894 | browser->selection->map->dso->annotate_warned) |
@@ -859,25 +898,29 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
859 | goto zoom_dso; | 898 | goto zoom_dso; |
860 | case 't': | 899 | case 't': |
861 | goto zoom_thread; | 900 | goto zoom_thread; |
862 | case NEWT_KEY_F1: | 901 | case K_F1: |
863 | case 'h': | 902 | case 'h': |
864 | case '?': | 903 | case '?': |
865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | 904 | ui__help_window("h/?/F1 Show this window\n" |
866 | "<- Zoom out\n" | 905 | "UP/DOWN/PGUP\n" |
867 | "a Annotate current symbol\n" | 906 | "PGDN/SPACE Navigate\n" |
868 | "h/?/F1 Show this window\n" | 907 | "q/ESC/CTRL+C Exit browser\n\n" |
869 | "C Collapse all callchains\n" | 908 | "For multiple event sessions:\n\n" |
870 | "E Expand all callchains\n" | 909 | "TAB/UNTAB Switch events\n\n" |
871 | "d Zoom into current DSO\n" | 910 | "For symbolic views (--sort has sym):\n\n" |
872 | "t Zoom into current Thread\n" | 911 | "-> Zoom into DSO/Threads & Annotate current symbol\n" |
873 | "TAB/UNTAB Switch events\n" | 912 | "<- Zoom out\n" |
874 | "q/CTRL+C Exit browser"); | 913 | "a Annotate current symbol\n" |
914 | "C Collapse all callchains\n" | ||
915 | "E Expand all callchains\n" | ||
916 | "d Zoom into current DSO\n" | ||
917 | "t Zoom into current Thread\n"); | ||
875 | continue; | 918 | continue; |
876 | case NEWT_KEY_ENTER: | 919 | case K_ENTER: |
877 | case NEWT_KEY_RIGHT: | 920 | case K_RIGHT: |
878 | /* menu */ | 921 | /* menu */ |
879 | break; | 922 | break; |
880 | case NEWT_KEY_LEFT: { | 923 | case K_LEFT: { |
881 | const void *top; | 924 | const void *top; |
882 | 925 | ||
883 | if (pstack__empty(fstack)) { | 926 | if (pstack__empty(fstack)) { |
@@ -889,21 +932,27 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
889 | continue; | 932 | continue; |
890 | } | 933 | } |
891 | top = pstack__pop(fstack); | 934 | top = pstack__pop(fstack); |
892 | if (top == &dso_filter) | 935 | if (top == &browser->hists->dso_filter) |
893 | goto zoom_out_dso; | 936 | goto zoom_out_dso; |
894 | if (top == &thread_filter) | 937 | if (top == &browser->hists->thread_filter) |
895 | goto zoom_out_thread; | 938 | goto zoom_out_thread; |
896 | continue; | 939 | continue; |
897 | } | 940 | } |
898 | case NEWT_KEY_ESCAPE: | 941 | case K_ESC: |
899 | if (!left_exits && | 942 | if (!left_exits && |
900 | !ui__dialog_yesno("Do you really want to exit?")) | 943 | !ui__dialog_yesno("Do you really want to exit?")) |
901 | continue; | 944 | continue; |
902 | /* Fall thru */ | 945 | /* Fall thru */ |
903 | default: | 946 | case 'q': |
947 | case CTRL('c'): | ||
904 | goto out_free_stack; | 948 | goto out_free_stack; |
949 | default: | ||
950 | continue; | ||
905 | } | 951 | } |
906 | 952 | ||
953 | if (!browser->has_symbols) | ||
954 | goto add_exit_option; | ||
955 | |||
907 | if (browser->selection != NULL && | 956 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | 957 | browser->selection->sym != NULL && |
909 | !browser->selection->map->dso->annotate_warned && | 958 | !browser->selection->map->dso->annotate_warned && |
@@ -913,14 +962,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
913 | 962 | ||
914 | if (thread != NULL && | 963 | if (thread != NULL && |
915 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 964 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
916 | (thread_filter ? "out of" : "into"), | 965 | (browser->hists->thread_filter ? "out of" : "into"), |
917 | (thread->comm_set ? thread->comm : ""), | 966 | (thread->comm_set ? thread->comm : ""), |
918 | thread->pid) > 0) | 967 | thread->pid) > 0) |
919 | zoom_thread = nr_options++; | 968 | zoom_thread = nr_options++; |
920 | 969 | ||
921 | if (dso != NULL && | 970 | if (dso != NULL && |
922 | asprintf(&options[nr_options], "Zoom %s %s DSO", | 971 | asprintf(&options[nr_options], "Zoom %s %s DSO", |
923 | (dso_filter ? "out of" : "into"), | 972 | (browser->hists->dso_filter ? "out of" : "into"), |
924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 973 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
925 | zoom_dso = nr_options++; | 974 | zoom_dso = nr_options++; |
926 | 975 | ||
@@ -928,7 +977,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
928 | browser->selection->map != NULL && | 977 | browser->selection->map != NULL && |
929 | asprintf(&options[nr_options], "Browse map details") > 0) | 978 | asprintf(&options[nr_options], "Browse map details") > 0) |
930 | browse_map = nr_options++; | 979 | browse_map = nr_options++; |
931 | 980 | add_exit_option: | |
932 | options[nr_options++] = (char *)"Exit"; | 981 | options[nr_options++] = (char *)"Exit"; |
933 | 982 | ||
934 | choice = ui__popup_menu(nr_options, options); | 983 | choice = ui__popup_menu(nr_options, options); |
@@ -948,46 +997,52 @@ do_annotate: | |||
948 | he = hist_browser__selected_entry(browser); | 997 | he = hist_browser__selected_entry(browser); |
949 | if (he == NULL) | 998 | if (he == NULL) |
950 | continue; | 999 | continue; |
951 | 1000 | /* | |
952 | hist_entry__tui_annotate(he, evsel->idx); | 1001 | * Don't let this be freed, say, by hists__decay_entry. |
1002 | */ | ||
1003 | he->used = true; | ||
1004 | hist_entry__tui_annotate(he, evsel->idx, nr_events, | ||
1005 | timer, arg, delay_secs); | ||
1006 | he->used = false; | ||
1007 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
953 | } else if (choice == browse_map) | 1008 | } else if (choice == browse_map) |
954 | map__browse(browser->selection->map); | 1009 | map__browse(browser->selection->map); |
955 | else if (choice == zoom_dso) { | 1010 | else if (choice == zoom_dso) { |
956 | zoom_dso: | 1011 | zoom_dso: |
957 | if (dso_filter) { | 1012 | if (browser->hists->dso_filter) { |
958 | pstack__remove(fstack, &dso_filter); | 1013 | pstack__remove(fstack, &browser->hists->dso_filter); |
959 | zoom_out_dso: | 1014 | zoom_out_dso: |
960 | ui_helpline__pop(); | 1015 | ui_helpline__pop(); |
961 | dso_filter = NULL; | 1016 | browser->hists->dso_filter = NULL; |
1017 | sort_dso.elide = false; | ||
962 | } else { | 1018 | } else { |
963 | if (dso == NULL) | 1019 | if (dso == NULL) |
964 | continue; | 1020 | continue; |
965 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | 1021 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", |
966 | dso->kernel ? "the Kernel" : dso->short_name); | 1022 | dso->kernel ? "the Kernel" : dso->short_name); |
967 | dso_filter = dso; | 1023 | browser->hists->dso_filter = dso; |
968 | pstack__push(fstack, &dso_filter); | 1024 | sort_dso.elide = true; |
1025 | pstack__push(fstack, &browser->hists->dso_filter); | ||
969 | } | 1026 | } |
970 | hists__filter_by_dso(self, dso_filter); | 1027 | hists__filter_by_dso(self); |
971 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
972 | dso_filter, thread_filter); | ||
973 | hist_browser__reset(browser); | 1028 | hist_browser__reset(browser); |
974 | } else if (choice == zoom_thread) { | 1029 | } else if (choice == zoom_thread) { |
975 | zoom_thread: | 1030 | zoom_thread: |
976 | if (thread_filter) { | 1031 | if (browser->hists->thread_filter) { |
977 | pstack__remove(fstack, &thread_filter); | 1032 | pstack__remove(fstack, &browser->hists->thread_filter); |
978 | zoom_out_thread: | 1033 | zoom_out_thread: |
979 | ui_helpline__pop(); | 1034 | ui_helpline__pop(); |
980 | thread_filter = NULL; | 1035 | browser->hists->thread_filter = NULL; |
1036 | sort_thread.elide = false; | ||
981 | } else { | 1037 | } else { |
982 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1038 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
983 | thread->comm_set ? thread->comm : "", | 1039 | thread->comm_set ? thread->comm : "", |
984 | thread->pid); | 1040 | thread->pid); |
985 | thread_filter = thread; | 1041 | browser->hists->thread_filter = thread; |
986 | pstack__push(fstack, &thread_filter); | 1042 | sort_thread.elide = true; |
1043 | pstack__push(fstack, &browser->hists->thread_filter); | ||
987 | } | 1044 | } |
988 | hists__filter_by_thread(self, thread_filter); | 1045 | hists__filter_by_thread(self); |
989 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
990 | dso_filter, thread_filter); | ||
991 | hist_browser__reset(browser); | 1046 | hist_browser__reset(browser); |
992 | } | 1047 | } |
993 | } | 1048 | } |
@@ -1026,9 +1081,10 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1026 | menu->selection = evsel; | 1081 | menu->selection = evsel; |
1027 | } | 1082 | } |
1028 | 1083 | ||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | 1084 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1085 | int nr_events, const char *help, | ||
1086 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
1030 | { | 1087 | { |
1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
1032 | struct perf_evlist *evlist = menu->b.priv; | 1088 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | 1089 | struct perf_evsel *pos; |
1034 | const char *ev_name, *title = "Available samples"; | 1090 | const char *ev_name, *title = "Available samples"; |
@@ -1038,50 +1094,65 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | |||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | 1094 | "ESC: exit, ENTER|->: Browse histograms") < 0) |
1039 | return -1; | 1095 | return -1; |
1040 | 1096 | ||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
1042 | |||
1043 | while (1) { | 1097 | while (1) { |
1044 | key = ui_browser__run(&menu->b); | 1098 | key = ui_browser__run(&menu->b, delay_secs); |
1045 | 1099 | ||
1046 | switch (key) { | 1100 | switch (key) { |
1047 | case NEWT_KEY_RIGHT: | 1101 | case K_TIMER: |
1048 | case NEWT_KEY_ENTER: | 1102 | timer(arg); |
1103 | continue; | ||
1104 | case K_RIGHT: | ||
1105 | case K_ENTER: | ||
1049 | if (!menu->selection) | 1106 | if (!menu->selection) |
1050 | continue; | 1107 | continue; |
1051 | pos = menu->selection; | 1108 | pos = menu->selection; |
1052 | browse_hists: | 1109 | browse_hists: |
1110 | perf_evlist__set_selected(evlist, pos); | ||
1111 | /* | ||
1112 | * Give the calling tool a chance to populate the non | ||
1113 | * default evsel resorted hists tree. | ||
1114 | */ | ||
1115 | if (timer) | ||
1116 | timer(arg); | ||
1053 | ev_name = event_name(pos); | 1117 | ev_name = event_name(pos); |
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | 1118 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1119 | ev_name, true, timer, | ||
1120 | arg, delay_secs); | ||
1055 | ui_browser__show_title(&menu->b, title); | 1121 | ui_browser__show_title(&menu->b, title); |
1056 | break; | 1122 | switch (key) { |
1057 | case NEWT_KEY_LEFT: | 1123 | case K_TAB: |
1124 | if (pos->node.next == &evlist->entries) | ||
1125 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1126 | else | ||
1127 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1128 | goto browse_hists; | ||
1129 | case K_UNTAB: | ||
1130 | if (pos->node.prev == &evlist->entries) | ||
1131 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1132 | else | ||
1133 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1134 | goto browse_hists; | ||
1135 | case K_ESC: | ||
1136 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
1137 | continue; | ||
1138 | /* Fall thru */ | ||
1139 | case 'q': | ||
1140 | case CTRL('c'): | ||
1141 | goto out; | ||
1142 | default: | ||
1143 | continue; | ||
1144 | } | ||
1145 | case K_LEFT: | ||
1058 | continue; | 1146 | continue; |
1059 | case NEWT_KEY_ESCAPE: | 1147 | case K_ESC: |
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | 1148 | if (!ui__dialog_yesno("Do you really want to exit?")) |
1061 | continue; | 1149 | continue; |
1062 | /* Fall thru */ | 1150 | /* Fall thru */ |
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
1067 | switch (key) { | ||
1068 | case NEWT_KEY_TAB: | ||
1069 | if (pos->node.next == &evlist->entries) | ||
1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1071 | else | ||
1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1073 | goto browse_hists; | ||
1074 | case NEWT_KEY_UNTAB: | ||
1075 | if (pos->node.prev == &evlist->entries) | ||
1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1077 | else | ||
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1079 | goto browse_hists; | ||
1080 | case 'q': | 1151 | case 'q': |
1081 | case CTRL('c'): | 1152 | case CTRL('c'): |
1082 | goto out; | 1153 | goto out; |
1083 | default: | 1154 | default: |
1084 | break; | 1155 | continue; |
1085 | } | 1156 | } |
1086 | } | 1157 | } |
1087 | 1158 | ||
@@ -1091,7 +1162,9 @@ out: | |||
1091 | } | 1162 | } |
1092 | 1163 | ||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1164 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1094 | const char *help) | 1165 | const char *help, |
1166 | void(*timer)(void *arg), void *arg, | ||
1167 | int delay_secs) | ||
1095 | { | 1168 | { |
1096 | struct perf_evsel *pos; | 1169 | struct perf_evsel *pos; |
1097 | struct perf_evsel_menu menu = { | 1170 | struct perf_evsel_menu menu = { |
@@ -1121,18 +1194,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1121 | pos->name = strdup(ev_name); | 1194 | pos->name = strdup(ev_name); |
1122 | } | 1195 | } |
1123 | 1196 | ||
1124 | return perf_evsel_menu__run(&menu, help); | 1197 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, |
1198 | arg, delay_secs); | ||
1125 | } | 1199 | } |
1126 | 1200 | ||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | 1201 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1202 | void(*timer)(void *arg), void *arg, | ||
1203 | int delay_secs) | ||
1128 | { | 1204 | { |
1129 | 1205 | ||
1130 | if (evlist->nr_entries == 1) { | 1206 | if (evlist->nr_entries == 1) { |
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1207 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1132 | struct perf_evsel, node); | 1208 | struct perf_evsel, node); |
1133 | const char *ev_name = event_name(first); | 1209 | const char *ev_name = event_name(first); |
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | 1210 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
1211 | ev_name, false, timer, arg, | ||
1212 | delay_secs); | ||
1135 | } | 1213 | } |
1136 | 1214 | ||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | 1215 | return __perf_evlist__tui_browse_hists(evlist, help, |
1216 | timer, arg, delay_secs); | ||
1138 | } | 1217 | } |
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 8462bffe20bc..6905bcc8be2d 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <newt.h> | ||
3 | #include <inttypes.h> | 4 | #include <inttypes.h> |
4 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
5 | #include <ctype.h> | 6 | #include <ctype.h> |
@@ -108,11 +109,8 @@ static int map_browser__run(struct map_browser *self) | |||
108 | verbose ? "" : "restart with -v to use") < 0) | 109 | verbose ? "" : "restart with -v to use") < 0) |
109 | return -1; | 110 | return -1; |
110 | 111 | ||
111 | if (verbose) | ||
112 | ui_browser__add_exit_key(&self->b, '/'); | ||
113 | |||
114 | while (1) { | 112 | while (1) { |
115 | key = ui_browser__run(&self->b); | 113 | key = ui_browser__run(&self->b, 0); |
116 | 114 | ||
117 | if (verbose && key == '/') | 115 | if (verbose && key == '/') |
118 | map_browser__search(self); | 116 | map_browser__search(self); |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c deleted file mode 100644 index 88403cf8396a..000000000000 --- a/tools/perf/util/ui/browsers/top.c +++ /dev/null | |||
@@ -1,212 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../../evlist.h" | ||
15 | #include "../../hist.h" | ||
16 | #include "../../sort.h" | ||
17 | #include "../../symbol.h" | ||
18 | #include "../../top.h" | ||
19 | |||
20 | struct perf_top_browser { | ||
21 | struct ui_browser b; | ||
22 | struct rb_root root; | ||
23 | struct sym_entry *selection; | ||
24 | float sum_ksamples; | ||
25 | int dso_width; | ||
26 | int dso_short_width; | ||
27 | int sym_width; | ||
28 | }; | ||
29 | |||
30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
31 | { | ||
32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
36 | struct perf_top *top = browser->priv; | ||
37 | int width = browser->width; | ||
38 | double pcnt; | ||
39 | |||
40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
41 | top_browser->sum_ksamples)); | ||
42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
43 | |||
44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
45 | slsmg_printf("%20.2f ", syme->weight); | ||
46 | width -= 24; | ||
47 | } else { | ||
48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
49 | width -= 23; | ||
50 | } | ||
51 | |||
52 | slsmg_printf("%4.1f%%", pcnt); | ||
53 | width -= 7; | ||
54 | |||
55 | if (verbose) { | ||
56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
57 | width -= 17; | ||
58 | } | ||
59 | |||
60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
61 | symbol->name); | ||
62 | width -= top_browser->sym_width; | ||
63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
64 | syme->map->dso->long_name : | ||
65 | syme->map->dso->short_name, width); | ||
66 | |||
67 | if (current_entry) | ||
68 | top_browser->selection = syme; | ||
69 | } | ||
70 | |||
71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
72 | { | ||
73 | struct perf_top *top = browser->b.priv; | ||
74 | u64 top_idx = browser->b.top_idx; | ||
75 | |||
76 | browser->root = RB_ROOT; | ||
77 | browser->b.top = NULL; | ||
78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
79 | /* | ||
80 | * No active symbols | ||
81 | */ | ||
82 | if (top->rb_entries == 0) | ||
83 | return; | ||
84 | |||
85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
86 | &browser->dso_short_width, | ||
87 | &browser->sym_width); | ||
88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
89 | browser->dso_width = browser->dso_short_width; | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
97 | * possible new top of the screen. | ||
98 | */ | ||
99 | browser->b.nr_entries = top->rb_entries; | ||
100 | |||
101 | if (top_idx >= browser->b.nr_entries) { | ||
102 | if (browser->b.height >= browser->b.nr_entries) | ||
103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
104 | else | ||
105 | top_idx = 0; | ||
106 | } | ||
107 | |||
108 | if (browser->b.index >= top_idx + browser->b.height) | ||
109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
110 | |||
111 | if (browser->b.index >= browser->b.nr_entries) | ||
112 | browser->b.index = browser->b.nr_entries - 1; | ||
113 | |||
114 | browser->b.top_idx = top_idx; | ||
115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
116 | } | ||
117 | |||
118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
119 | { | ||
120 | struct sym_entry *syme = browser->selection; | ||
121 | struct symbol *sym = sym_entry__symbol(syme); | ||
122 | struct annotation *notes = symbol__annotation(sym); | ||
123 | struct perf_top *top = browser->b.priv; | ||
124 | |||
125 | if (notes->src != NULL) | ||
126 | goto do_annotation; | ||
127 | |||
128 | pthread_mutex_lock(¬es->lock); | ||
129 | |||
130 | top->sym_filter_entry = NULL; | ||
131 | |||
132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
134 | sym->name); | ||
135 | pthread_mutex_unlock(¬es->lock); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | top->sym_filter_entry = syme; | ||
140 | |||
141 | pthread_mutex_unlock(¬es->lock); | ||
142 | do_annotation: | ||
143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
144 | } | ||
145 | |||
146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
147 | { | ||
148 | int key; | ||
149 | char title[160]; | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | int delay_msecs = top->delay_secs * 1000; | ||
152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
153 | |||
154 | perf_top_browser__update_rb_tree(browser); | ||
155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
156 | perf_top__reset_sample_counters(top); | ||
157 | |||
158 | if (ui_browser__show(&browser->b, title, | ||
159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
160 | return -1; | ||
161 | |||
162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
164 | |||
165 | while (1) { | ||
166 | key = ui_browser__run(&browser->b); | ||
167 | |||
168 | switch (key) { | ||
169 | case -1: | ||
170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
171 | perf_top_browser__update_rb_tree(browser); | ||
172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
173 | perf_top__reset_sample_counters(top); | ||
174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
175 | SLsmg_gotorc(0, 0); | ||
176 | slsmg_write_nstring(title, browser->b.width); | ||
177 | break; | ||
178 | case 'a': | ||
179 | case NEWT_KEY_RIGHT: | ||
180 | case NEWT_KEY_ENTER: | ||
181 | if (browser->selection) | ||
182 | perf_top_browser__annotate(browser); | ||
183 | break; | ||
184 | case NEWT_KEY_LEFT: | ||
185 | continue; | ||
186 | case NEWT_KEY_ESCAPE: | ||
187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
188 | continue; | ||
189 | /* Fall thru */ | ||
190 | default: | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | ui_browser__hide(&browser->b); | ||
196 | return key; | ||
197 | } | ||
198 | |||
199 | int perf_top__tui_browser(struct perf_top *top) | ||
200 | { | ||
201 | struct perf_top_browser browser = { | ||
202 | .b = { | ||
203 | .entries = &browser.root, | ||
204 | .refresh = ui_browser__rb_tree_refresh, | ||
205 | .seek = ui_browser__rb_tree_seek, | ||
206 | .write = perf_top_browser__write, | ||
207 | .priv = top, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | return perf_top_browser__run(&browser); | ||
212 | } | ||
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index ab6028d0c401..fdcbc0270acd 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _PERF_UI_HELPLINE_H_ | 1 | #ifndef _PERF_UI_HELPLINE_H_ |
2 | #define _PERF_UI_HELPLINE_H_ 1 | 2 | #define _PERF_UI_HELPLINE_H_ 1 |
3 | 3 | ||
4 | #include <stdio.h> | ||
5 | #include <stdarg.h> | ||
6 | |||
4 | void ui_helpline__init(void); | 7 | void ui_helpline__init(void); |
5 | void ui_helpline__pop(void); | 8 | void ui_helpline__pop(void); |
6 | void ui_helpline__push(const char *msg); | 9 | void ui_helpline__push(const char *msg); |
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h new file mode 100644 index 000000000000..3458b1985761 --- /dev/null +++ b/tools/perf/util/ui/keysyms.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef _PERF_KEYSYMS_H_ | ||
2 | #define _PERF_KEYSYMS_H_ 1 | ||
3 | |||
4 | #include "libslang.h" | ||
5 | |||
6 | #define K_DOWN SL_KEY_DOWN | ||
7 | #define K_END SL_KEY_END | ||
8 | #define K_ENTER '\r' | ||
9 | #define K_ESC 033 | ||
10 | #define K_F1 SL_KEY_F(1) | ||
11 | #define K_HOME SL_KEY_HOME | ||
12 | #define K_LEFT SL_KEY_LEFT | ||
13 | #define K_PGDN SL_KEY_NPAGE | ||
14 | #define K_PGUP SL_KEY_PPAGE | ||
15 | #define K_RIGHT SL_KEY_RIGHT | ||
16 | #define K_TAB '\t' | ||
17 | #define K_UNTAB SL_KEY_UNTAB | ||
18 | #define K_UP SL_KEY_UP | ||
19 | |||
20 | /* Not really keys */ | ||
21 | #define K_TIMER -1 | ||
22 | #define K_ERROR -2 | ||
23 | #define K_RESIZE -3 | ||
24 | |||
25 | #endif /* _PERF_KEYSYMS_H_ */ | ||
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 2b63e1c9b181..4d54b6450f5b 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
@@ -24,4 +24,6 @@ | |||
24 | #define sltt_set_color SLtt_set_color | 24 | #define sltt_set_color SLtt_set_color |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | #define SL_KEY_UNTAB 0x1000 | ||
28 | |||
27 | #endif /* _PERF_UI_SLANG_H_ */ | 29 | #endif /* _PERF_UI_SLANG_H_ */ |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index ee46d671db59..1e6ba06980c4 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "browser.h" | 7 | #include "browser.h" |
8 | #include "helpline.h" | 8 | #include "helpline.h" |
9 | #include "ui.h" | 9 | #include "ui.h" |
10 | #include "libslang.h" | ||
10 | 11 | ||
11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | 12 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; |
12 | 13 | ||
@@ -17,6 +18,33 @@ static void newt_suspend(void *d __used) | |||
17 | newtResume(); | 18 | newtResume(); |
18 | } | 19 | } |
19 | 20 | ||
21 | static int ui__init(void) | ||
22 | { | ||
23 | int err = SLkp_init(); | ||
24 | |||
25 | if (err < 0) | ||
26 | goto out; | ||
27 | |||
28 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); | ||
29 | out: | ||
30 | return err; | ||
31 | } | ||
32 | |||
33 | static void ui__exit(void) | ||
34 | { | ||
35 | SLtt_set_cursor_visibility(1); | ||
36 | SLsmg_refresh(); | ||
37 | SLsmg_reset_smg(); | ||
38 | SLang_reset_tty(); | ||
39 | } | ||
40 | |||
41 | static void ui__signal(int sig) | ||
42 | { | ||
43 | ui__exit(); | ||
44 | psignal(sig, "perf"); | ||
45 | exit(0); | ||
46 | } | ||
47 | |||
20 | void setup_browser(bool fallback_to_pager) | 48 | void setup_browser(bool fallback_to_pager) |
21 | { | 49 | { |
22 | if (!isatty(1) || !use_browser || dump_trace) { | 50 | if (!isatty(1) || !use_browser || dump_trace) { |
@@ -28,10 +56,16 @@ void setup_browser(bool fallback_to_pager) | |||
28 | 56 | ||
29 | use_browser = 1; | 57 | use_browser = 1; |
30 | newtInit(); | 58 | newtInit(); |
31 | newtCls(); | 59 | ui__init(); |
32 | newtSetSuspendCallback(newt_suspend, NULL); | 60 | newtSetSuspendCallback(newt_suspend, NULL); |
33 | ui_helpline__init(); | 61 | ui_helpline__init(); |
34 | ui_browser__init(); | 62 | ui_browser__init(); |
63 | |||
64 | signal(SIGSEGV, ui__signal); | ||
65 | signal(SIGFPE, ui__signal); | ||
66 | signal(SIGINT, ui__signal); | ||
67 | signal(SIGQUIT, ui__signal); | ||
68 | signal(SIGTERM, ui__signal); | ||
35 | } | 69 | } |
36 | 70 | ||
37 | void exit_browser(bool wait_for_ok) | 71 | void exit_browser(bool wait_for_ok) |
@@ -41,6 +75,6 @@ void exit_browser(bool wait_for_ok) | |||
41 | char title[] = "Fatal Error", ok[] = "Ok"; | 75 | char title[] = "Fatal Error", ok[] = "Ok"; |
42 | newtWinMessage(title, ok, ui_helpline__last_msg); | 76 | newtWinMessage(title, ok, ui_helpline__last_msg); |
43 | } | 77 | } |
44 | newtFinished(); | 78 | ui__exit(); |
45 | } | 79 | } |
46 | } | 80 | } |