diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-10 13:33:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-10 13:33:58 -0400 |
commit | c48ce9f190266b763e809dd79fcf4152f558793c (patch) | |
tree | 60490f3c5ae8e236dc6ea336ca9c695527c835f5 | |
parent | 84ed2da02f4cda6759880c87a213ee80c91ca3bd (diff) | |
parent | c68306ce20ad03ce655a367fc33ad06e12bb87a6 (diff) |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf tooling updates from Thomas Gleixner:
- handle uretprobe placement proper on little endian PPC64
- fix buffer handling in libtraceevent
- add a missing pointer derefence in perf probe
- fix the build of host tools in cross builds
- fix Intel PT timestamp handling
- synchronize memcpy, cpufeatures and bpf headers with the kernel headers
- support for vendor supplied JSON files describing PMU events
- a new set of tool tips
- initial work for clang/llvm support
- address some style issues found by cppcheck
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (35 commits)
tools build: Add feature detection for g++
tools build: Support compiling C++ source file
perf top/report: Add tips about a list option
perf report/top: Add a tip about system-wide collection from all CPUs
perf report/top: Add a tip about source line numbers with overhead
tools: Synchronize tools/include/uapi/linux/bpf.h
tools: Synchronize tools/arch/x86/include/asm/cpufeatures.h
perf bench mem: Sync memcpy assembly sources with the kernel
perf jevents: Fix Intel JSON fixed counter conversions
tools lib traceevent: Fix kbuffer_read_at_offset()
perf intel-pt: Fix MTC timestamp calculation for large MTC periods
perf intel-pt: Fix estimated timestamps for cycle-accurate mode
perf uretprobe ppc64le: Fix probe location
perf pmu-events: Add Skylake frontend MSR support
perf pmu-events: Fix fixed counters on Intel
perf tools: Make alias matching case-insensitive
perf tools: Allow period= in perf stat CPU event descriptions.
perf tools: Add README for info on parsing JSON/map files
perf list jevents: Add support for event list topics
perf list: Support long jevents descriptions
...
42 files changed, 2011 insertions, 75 deletions
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 92a8308b96f6..1188bc849ee3 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h | |||
@@ -106,7 +106,6 @@ | |||
106 | #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ | 106 | #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ |
107 | #define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ | 107 | #define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ |
108 | #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ | 108 | #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ |
109 | #define X86_FEATURE_MCE_RECOVERY ( 3*32+31) /* cpu has recoverable machine checks */ | ||
110 | 109 | ||
111 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ | 110 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ |
112 | #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ | 111 | #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ |
diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S index 2ec0b0abbfaa..49e6ebac7e73 100644 --- a/tools/arch/x86/lib/memcpy_64.S +++ b/tools/arch/x86/lib/memcpy_64.S | |||
@@ -181,11 +181,11 @@ ENDPROC(memcpy_orig) | |||
181 | 181 | ||
182 | #ifndef CONFIG_UML | 182 | #ifndef CONFIG_UML |
183 | /* | 183 | /* |
184 | * memcpy_mcsafe - memory copy with machine check exception handling | 184 | * memcpy_mcsafe_unrolled - memory copy with machine check exception handling |
185 | * Note that we only catch machine checks when reading the source addresses. | 185 | * Note that we only catch machine checks when reading the source addresses. |
186 | * Writes to target are posted and don't generate machine checks. | 186 | * Writes to target are posted and don't generate machine checks. |
187 | */ | 187 | */ |
188 | ENTRY(memcpy_mcsafe) | 188 | ENTRY(memcpy_mcsafe_unrolled) |
189 | cmpl $8, %edx | 189 | cmpl $8, %edx |
190 | /* Less than 8 bytes? Go to byte copy loop */ | 190 | /* Less than 8 bytes? Go to byte copy loop */ |
191 | jb .L_no_whole_words | 191 | jb .L_no_whole_words |
@@ -273,7 +273,7 @@ ENTRY(memcpy_mcsafe) | |||
273 | .L_done_memcpy_trap: | 273 | .L_done_memcpy_trap: |
274 | xorq %rax, %rax | 274 | xorq %rax, %rax |
275 | ret | 275 | ret |
276 | ENDPROC(memcpy_mcsafe) | 276 | ENDPROC(memcpy_mcsafe_unrolled) |
277 | 277 | ||
278 | .section .fixup, "ax" | 278 | .section .fixup, "ax" |
279 | /* Return -EFAULT for any failure */ | 279 | /* Return -EFAULT for any failure */ |
diff --git a/tools/build/Build b/tools/build/Build index 63a6c34c0c88..76d1a4960973 100644 --- a/tools/build/Build +++ b/tools/build/Build | |||
@@ -1 +1,3 @@ | |||
1 | hostprogs := fixdep | ||
2 | |||
1 | fixdep-y := fixdep.o | 3 | fixdep-y := fixdep.o |
diff --git a/tools/build/Build.include b/tools/build/Build.include index 4d000bc959b4..1dcb95e76f70 100644 --- a/tools/build/Build.include +++ b/tools/build/Build.include | |||
@@ -90,3 +90,9 @@ if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ | |||
90 | # - per object C flags | 90 | # - per object C flags |
91 | # - BUILD_STR macro to allow '-D"$(variable)"' constructs | 91 | # - BUILD_STR macro to allow '-D"$(variable)"' constructs |
92 | c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) | 92 | c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) |
93 | cxx_flags = -Wp,-MD,$(depfile),-MT,$@ $(CXXFLAGS) -D"BUILD_STR(s)=\#s" $(CXXFLAGS_$(basetarget).o) $(CXXFLAGS_$(obj)) | ||
94 | |||
95 | ### | ||
96 | ## HOSTCC C flags | ||
97 | |||
98 | host_c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CHOSTFLAGS) -D"BUILD_STR(s)=\#s" $(CHOSTFLAGS_$(basetarget).o) $(CHOSTFLAGS_$(obj)) | ||
diff --git a/tools/build/Makefile b/tools/build/Makefile index 0d5a0e3a8fa9..8332959fbca4 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile | |||
@@ -14,6 +14,12 @@ endef | |||
14 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) | 14 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) |
15 | $(call allow-override,LD,$(CROSS_COMPILE)ld) | 15 | $(call allow-override,LD,$(CROSS_COMPILE)ld) |
16 | 16 | ||
17 | HOSTCC ?= gcc | ||
18 | HOSTLD ?= ld | ||
19 | HOSTAR ?= ar | ||
20 | |||
21 | export HOSTCC HOSTLD HOSTAR | ||
22 | |||
17 | ifeq ($(V),1) | 23 | ifeq ($(V),1) |
18 | Q = | 24 | Q = |
19 | else | 25 | else |
@@ -36,7 +42,7 @@ $(OUTPUT)fixdep-in.o: FORCE | |||
36 | $(Q)$(MAKE) $(build)=fixdep | 42 | $(Q)$(MAKE) $(build)=fixdep |
37 | 43 | ||
38 | $(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o | 44 | $(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o |
39 | $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $< | 45 | $(QUIET_LINK)$(HOSTCC) $(LDFLAGS) -o $@ $< |
40 | 46 | ||
41 | FORCE: | 47 | FORCE: |
42 | 48 | ||
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index 27f3583193e6..99c0ccd2f176 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build | |||
@@ -58,6 +58,12 @@ quiet_cmd_mkdir = MKDIR $(dir $@) | |||
58 | quiet_cmd_cc_o_c = CC $@ | 58 | quiet_cmd_cc_o_c = CC $@ |
59 | cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< | 59 | cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< |
60 | 60 | ||
61 | quiet_cmd_host_cc_o_c = HOSTCC $@ | ||
62 | cmd_host_cc_o_c = $(HOSTCC) $(host_c_flags) -c -o $@ $< | ||
63 | |||
64 | quiet_cmd_cxx_o_c = CXX $@ | ||
65 | cmd_cxx_o_c = $(CXX) $(cxx_flags) -c -o $@ $< | ||
66 | |||
61 | quiet_cmd_cpp_i_c = CPP $@ | 67 | quiet_cmd_cpp_i_c = CPP $@ |
62 | cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< | 68 | cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< |
63 | 69 | ||
@@ -70,16 +76,28 @@ quiet_cmd_gen = GEN $@ | |||
70 | # If there's nothing to link, create empty $@ object. | 76 | # If there's nothing to link, create empty $@ object. |
71 | quiet_cmd_ld_multi = LD $@ | 77 | quiet_cmd_ld_multi = LD $@ |
72 | cmd_ld_multi = $(if $(strip $(obj-y)),\ | 78 | cmd_ld_multi = $(if $(strip $(obj-y)),\ |
73 | $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) | 79 | $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) |
80 | |||
81 | quiet_cmd_host_ld_multi = HOSTLD $@ | ||
82 | cmd_host_ld_multi = $(if $(strip $(obj-y)),\ | ||
83 | $(HOSTLD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(HOSTAR) rcs $@) | ||
84 | |||
85 | ifneq ($(filter $(obj),$(hostprogs)),) | ||
86 | host = host_ | ||
87 | endif | ||
74 | 88 | ||
75 | # Build rules | 89 | # Build rules |
76 | $(OUTPUT)%.o: %.c FORCE | 90 | $(OUTPUT)%.o: %.c FORCE |
77 | $(call rule_mkdir) | 91 | $(call rule_mkdir) |
78 | $(call if_changed_dep,cc_o_c) | 92 | $(call if_changed_dep,$(host)cc_o_c) |
93 | |||
94 | $(OUTPUT)%.o: %.cpp FORCE | ||
95 | $(call rule_mkdir) | ||
96 | $(call if_changed_dep,cxx_o_c) | ||
79 | 97 | ||
80 | $(OUTPUT)%.o: %.S FORCE | 98 | $(OUTPUT)%.o: %.S FORCE |
81 | $(call rule_mkdir) | 99 | $(call rule_mkdir) |
82 | $(call if_changed_dep,cc_o_c) | 100 | $(call if_changed_dep,$(host)cc_o_c) |
83 | 101 | ||
84 | $(OUTPUT)%.i: %.c FORCE | 102 | $(OUTPUT)%.i: %.c FORCE |
85 | $(call rule_mkdir) | 103 | $(call rule_mkdir) |
@@ -119,7 +137,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ; | |||
119 | 137 | ||
120 | $(in-target): $(obj-y) FORCE | 138 | $(in-target): $(obj-y) FORCE |
121 | $(call rule_mkdir) | 139 | $(call rule_mkdir) |
122 | $(call if_changed,ld_multi) | 140 | $(call if_changed,$(host)ld_multi) |
123 | 141 | ||
124 | __build: $(in-target) | 142 | __build: $(in-target) |
125 | @: | 143 | @: |
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index a120c6b755a9..ae52e029dd22 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature | |||
@@ -7,7 +7,7 @@ endif | |||
7 | 7 | ||
8 | feature_check = $(eval $(feature_check_code)) | 8 | feature_check = $(eval $(feature_check_code)) |
9 | define feature_check_code | 9 | define feature_check_code |
10 | feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) | 10 | feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) |
11 | endef | 11 | endef |
12 | 12 | ||
13 | feature_set = $(eval $(feature_set_code)) | 13 | feature_set = $(eval $(feature_set_code)) |
diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index be630bed66d2..ad22e4e7bc59 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include | |||
@@ -1,10 +1,6 @@ | |||
1 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj | 1 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj |
2 | 2 | ||
3 | ifdef CROSS_COMPILE | ||
4 | fixdep: | ||
5 | else | ||
6 | fixdep: | 3 | fixdep: |
7 | $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep | 4 | $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep |
8 | endif | ||
9 | 5 | ||
10 | .PHONY: fixdep | 6 | .PHONY: fixdep |
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index a0b29a311816..ac9c477a2a48 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile | |||
@@ -46,11 +46,13 @@ FILES= \ | |||
46 | test-lzma.bin \ | 46 | test-lzma.bin \ |
47 | test-bpf.bin \ | 47 | test-bpf.bin \ |
48 | test-get_cpuid.bin \ | 48 | test-get_cpuid.bin \ |
49 | test-sdt.bin | 49 | test-sdt.bin \ |
50 | test-cxx.bin | ||
50 | 51 | ||
51 | FILES := $(addprefix $(OUTPUT),$(FILES)) | 52 | FILES := $(addprefix $(OUTPUT),$(FILES)) |
52 | 53 | ||
53 | CC := $(CROSS_COMPILE)gcc -MD | 54 | CC := $(CROSS_COMPILE)gcc -MD |
55 | CXX := $(CROSS_COMPILE)g++ -MD | ||
54 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 56 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
55 | 57 | ||
56 | all: $(FILES) | 58 | all: $(FILES) |
@@ -58,6 +60,9 @@ all: $(FILES) | |||
58 | __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) | 60 | __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) |
59 | BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 | 61 | BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 |
60 | 62 | ||
63 | __BUILDXX = $(CXX) $(CXXFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS) | ||
64 | BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1 | ||
65 | |||
61 | ############################### | 66 | ############################### |
62 | 67 | ||
63 | $(OUTPUT)test-all.bin: | 68 | $(OUTPUT)test-all.bin: |
@@ -217,6 +222,9 @@ $(OUTPUT)test-bpf.bin: | |||
217 | $(OUTPUT)test-sdt.bin: | 222 | $(OUTPUT)test-sdt.bin: |
218 | $(BUILD) | 223 | $(BUILD) |
219 | 224 | ||
225 | $(OUTPUT)test-cxx.bin: | ||
226 | $(BUILDXX) -std=gnu++11 | ||
227 | |||
220 | -include $(OUTPUT)*.d | 228 | -include $(OUTPUT)*.d |
221 | 229 | ||
222 | ############################### | 230 | ############################### |
diff --git a/tools/build/feature/test-cxx.cpp b/tools/build/feature/test-cxx.cpp new file mode 100644 index 000000000000..b1dee9a31d6c --- /dev/null +++ b/tools/build/feature/test-cxx.cpp | |||
@@ -0,0 +1,15 @@ | |||
1 | #include <iostream> | ||
2 | #include <memory> | ||
3 | |||
4 | static void print_str(std::string s) | ||
5 | { | ||
6 | std::cout << s << std::endl; | ||
7 | } | ||
8 | |||
9 | int main() | ||
10 | { | ||
11 | std::string s("Hello World!"); | ||
12 | print_str(std::move(s)); | ||
13 | std::cout << "|" << s << "|" << std::endl; | ||
14 | return 0; | ||
15 | } | ||
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index da218fec6056..9e5fc168c8a3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
@@ -339,7 +339,7 @@ enum bpf_func_id { | |||
339 | BPF_FUNC_skb_change_type, | 339 | BPF_FUNC_skb_change_type, |
340 | 340 | ||
341 | /** | 341 | /** |
342 | * bpf_skb_in_cgroup(skb, map, index) - Check cgroup2 membership of skb | 342 | * bpf_skb_under_cgroup(skb, map, index) - Check cgroup2 membership of skb |
343 | * @skb: pointer to skb | 343 | * @skb: pointer to skb |
344 | * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type | 344 | * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type |
345 | * @index: index of the cgroup in the bpf_map | 345 | * @index: index of the cgroup in the bpf_map |
@@ -348,7 +348,7 @@ enum bpf_func_id { | |||
348 | * == 1 skb succeeded the cgroup2 descendant test | 348 | * == 1 skb succeeded the cgroup2 descendant test |
349 | * < 0 error | 349 | * < 0 error |
350 | */ | 350 | */ |
351 | BPF_FUNC_skb_in_cgroup, | 351 | BPF_FUNC_skb_under_cgroup, |
352 | 352 | ||
353 | /** | 353 | /** |
354 | * bpf_get_hash_recalc(skb) | 354 | * bpf_get_hash_recalc(skb) |
diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c index d50f3b58606b..6518bea926d6 100644 --- a/tools/lib/subcmd/pager.c +++ b/tools/lib/subcmd/pager.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <signal.h> | 5 | #include <signal.h> |
6 | #include <sys/ioctl.h> | ||
6 | #include "pager.h" | 7 | #include "pager.h" |
7 | #include "run-command.h" | 8 | #include "run-command.h" |
8 | #include "sigchain.h" | 9 | #include "sigchain.h" |
@@ -14,6 +15,7 @@ | |||
14 | */ | 15 | */ |
15 | 16 | ||
16 | static int spawned_pager; | 17 | static int spawned_pager; |
18 | static int pager_columns; | ||
17 | 19 | ||
18 | void pager_init(const char *pager_env) | 20 | void pager_init(const char *pager_env) |
19 | { | 21 | { |
@@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo) | |||
58 | void setup_pager(void) | 60 | void setup_pager(void) |
59 | { | 61 | { |
60 | const char *pager = getenv(subcmd_config.pager_env); | 62 | const char *pager = getenv(subcmd_config.pager_env); |
63 | struct winsize sz; | ||
61 | 64 | ||
62 | if (!isatty(1)) | 65 | if (!isatty(1)) |
63 | return; | 66 | return; |
67 | if (ioctl(1, TIOCGWINSZ, &sz) == 0) | ||
68 | pager_columns = sz.ws_col; | ||
64 | if (!pager) | 69 | if (!pager) |
65 | pager = getenv("PAGER"); | 70 | pager = getenv("PAGER"); |
66 | if (!(pager || access("/usr/bin/pager", X_OK))) | 71 | if (!(pager || access("/usr/bin/pager", X_OK))) |
@@ -98,3 +103,14 @@ int pager_in_use(void) | |||
98 | { | 103 | { |
99 | return spawned_pager; | 104 | return spawned_pager; |
100 | } | 105 | } |
106 | |||
107 | int pager_get_columns(void) | ||
108 | { | ||
109 | char *s; | ||
110 | |||
111 | s = getenv("COLUMNS"); | ||
112 | if (s) | ||
113 | return atoi(s); | ||
114 | |||
115 | return (pager_columns ? pager_columns : 80) - 2; | ||
116 | } | ||
diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h index 8b83714ecf73..623f5542d05d 100644 --- a/tools/lib/subcmd/pager.h +++ b/tools/lib/subcmd/pager.h | |||
@@ -5,5 +5,6 @@ extern void pager_init(const char *pager_env); | |||
5 | 5 | ||
6 | extern void setup_pager(void); | 6 | extern void setup_pager(void); |
7 | extern int pager_in_use(void); | 7 | extern int pager_in_use(void); |
8 | extern int pager_get_columns(void); | ||
8 | 9 | ||
9 | #endif /* __SUBCMD_PAGER_H */ | 10 | #endif /* __SUBCMD_PAGER_H */ |
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c index 3bcada3ae05a..65984f1c2974 100644 --- a/tools/lib/traceevent/kbuffer-parse.c +++ b/tools/lib/traceevent/kbuffer-parse.c | |||
@@ -622,6 +622,7 @@ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, | |||
622 | 622 | ||
623 | /* Reset the buffer */ | 623 | /* Reset the buffer */ |
624 | kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); | 624 | kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); |
625 | data = kbuffer_read_event(kbuf, ts); | ||
625 | 626 | ||
626 | while (kbuf->curr < offset) { | 627 | while (kbuf->curr < offset) { |
627 | data = kbuffer_next_event(kbuf, ts); | 628 | data = kbuffer_next_event(kbuf, ts); |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index a126e97a8114..41857cce5e86 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -8,13 +8,23 @@ perf-list - List all symbolic event types | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf list' [hw|sw|cache|tracepoint|pmu|event_glob] | 11 | 'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|event_glob] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command displays the symbolic event types which can be selected in the | 15 | This command displays the symbolic event types which can be selected in the |
16 | various perf commands with the -e option. | 16 | various perf commands with the -e option. |
17 | 17 | ||
18 | OPTIONS | ||
19 | ------- | ||
20 | --no-desc:: | ||
21 | Don't print descriptions. | ||
22 | |||
23 | -v:: | ||
24 | --long-desc:: | ||
25 | Print longer event descriptions. | ||
26 | |||
27 | |||
18 | [[EVENT_MODIFIERS]] | 28 | [[EVENT_MODIFIERS]] |
19 | EVENT MODIFIERS | 29 | EVENT MODIFIERS |
20 | --------------- | 30 | --------------- |
diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt index 5950b5a24efd..8a6479c0eac9 100644 --- a/tools/perf/Documentation/tips.txt +++ b/tools/perf/Documentation/tips.txt | |||
@@ -28,3 +28,7 @@ To change sampling frequency to 100 Hz: perf record -F 100 | |||
28 | See assembly instructions with percentage: perf annotate <symbol> | 28 | See assembly instructions with percentage: perf annotate <symbol> |
29 | If you prefer Intel style assembly, try: perf annotate -M intel | 29 | If you prefer Intel style assembly, try: perf annotate -M intel |
30 | For hierarchical output, try: perf report --hierarchy | 30 | For hierarchical output, try: perf report --hierarchy |
31 | Order by the overhead of source file name and line number: perf report -s srcline | ||
32 | System-wide collection from all CPUs: perf record -a | ||
33 | Show current config key-value pairs: perf config --list | ||
34 | Show user configuration overrides: perf config --user --list | ||
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index d710db16b963..982d6439bb07 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -144,6 +144,10 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld) | |||
144 | 144 | ||
145 | LD += $(EXTRA_LDFLAGS) | 145 | LD += $(EXTRA_LDFLAGS) |
146 | 146 | ||
147 | HOSTCC ?= gcc | ||
148 | HOSTLD ?= ld | ||
149 | HOSTAR ?= ar | ||
150 | |||
147 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config | 151 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config |
148 | 152 | ||
149 | RM = rm -f | 153 | RM = rm -f |
@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf | |||
345 | PERF_IN := $(OUTPUT)perf-in.o | 349 | PERF_IN := $(OUTPUT)perf-in.o |
346 | 350 | ||
347 | export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK | 351 | export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK |
352 | export HOSTCC HOSTLD HOSTAR | ||
348 | include $(srctree)/tools/build/Makefile.include | 353 | include $(srctree)/tools/build/Makefile.include |
349 | 354 | ||
355 | JEVENTS := $(OUTPUT)pmu-events/jevents | ||
356 | JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o | ||
357 | |||
358 | PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o | ||
359 | |||
360 | export JEVENTS | ||
361 | |||
362 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj | ||
363 | |||
350 | $(PERF_IN): prepare FORCE | 364 | $(PERF_IN): prepare FORCE |
351 | @(test -f ../../include/uapi/linux/perf_event.h && ( \ | 365 | @(test -f ../../include/uapi/linux/perf_event.h && ( \ |
352 | (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ | 366 | (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ |
@@ -443,9 +457,18 @@ $(PERF_IN): prepare FORCE | |||
443 | || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true | 457 | || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true |
444 | $(Q)$(MAKE) $(build)=perf | 458 | $(Q)$(MAKE) $(build)=perf |
445 | 459 | ||
446 | $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) | 460 | $(JEVENTS_IN): FORCE |
461 | $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents | ||
462 | |||
463 | $(JEVENTS): $(JEVENTS_IN) | ||
464 | $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@ | ||
465 | |||
466 | $(PMU_EVENTS_IN): $(JEVENTS) FORCE | ||
467 | $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events | ||
468 | |||
469 | $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) | ||
447 | $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ | 470 | $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ |
448 | $(PERF_IN) $(LIBS) -o $@ | 471 | $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@ |
449 | 472 | ||
450 | $(GTK_IN): fixdep FORCE | 473 | $(GTK_IN): fixdep FORCE |
451 | $(Q)$(MAKE) $(build)=gtk | 474 | $(Q)$(MAKE) $(build)=gtk |
@@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \ | |||
474 | ifneq ($(OUTPUT),) | 497 | ifneq ($(OUTPUT),) |
475 | %.o: $(OUTPUT)%.o | 498 | %.o: $(OUTPUT)%.o |
476 | @echo " # Redirected target $@ => $(OUTPUT)$@" | 499 | @echo " # Redirected target $@ => $(OUTPUT)$@" |
500 | pmu-events/%.o: $(OUTPUT)pmu-events/%.o | ||
501 | @echo " # Redirected target $@ => $(OUTPUT)$@" | ||
477 | util/%.o: $(OUTPUT)util/%.o | 502 | util/%.o: $(OUTPUT)util/%.o |
478 | @echo " # Redirected target $@ => $(OUTPUT)$@" | 503 | @echo " # Redirected target $@ => $(OUTPUT)$@" |
479 | bench/%.o: $(OUTPUT)bench/%.o | 504 | bench/%.o: $(OUTPUT)bench/%.o |
@@ -729,10 +754,11 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea | |||
729 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) | 754 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) |
730 | $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete | 755 | $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete |
731 | $(Q)$(RM) $(OUTPUT).config-detected | 756 | $(Q)$(RM) $(OUTPUT).config-detected |
732 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 | 757 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents |
733 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ | 758 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ |
734 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ | 759 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ |
735 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c | 760 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ |
761 | $(OUTPUT)pmu-events/pmu-events.c | ||
736 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 762 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
737 | $(python-clean) | 763 | $(python-clean) |
738 | 764 | ||
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index f8ccee132867..9aaa6f5a9347 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz) | |||
32 | } | 32 | } |
33 | return -1; | 33 | return -1; |
34 | } | 34 | } |
35 | |||
36 | char * | ||
37 | get_cpuid_str(void) | ||
38 | { | ||
39 | char *bufp; | ||
40 | |||
41 | if (asprintf(&bufp, "%.8lx", mfspr(SPRN_PVR)) < 0) | ||
42 | bufp = NULL; | ||
43 | |||
44 | return bufp; | ||
45 | } | ||
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index ed9d5d15d5b6..1030a6e504bb 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c | |||
@@ -82,7 +82,8 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev, | |||
82 | * | 82 | * |
83 | * In addition, we shouldn't specify an offset for kretprobes. | 83 | * In addition, we shouldn't specify an offset for kretprobes. |
84 | */ | 84 | */ |
85 | if (pev->point.offset || pev->point.retprobe || !map || !sym) | 85 | if (pev->point.offset || (!pev->uprobes && pev->point.retprobe) || |
86 | !map || !sym) | ||
86 | return; | 87 | return; |
87 | 88 | ||
88 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); | 89 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); |
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index 146d12a1cec0..a74a48db26f5 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -19,8 +19,8 @@ cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, | |||
19 | : "a" (op)); | 19 | : "a" (op)); |
20 | } | 20 | } |
21 | 21 | ||
22 | int | 22 | static int |
23 | get_cpuid(char *buffer, size_t sz) | 23 | __get_cpuid(char *buffer, size_t sz, const char *fmt) |
24 | { | 24 | { |
25 | unsigned int a, b, c, d, lvl; | 25 | unsigned int a, b, c, d, lvl; |
26 | int family = -1, model = -1, step = -1; | 26 | int family = -1, model = -1, step = -1; |
@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz) | |||
48 | if (family >= 0x6) | 48 | if (family >= 0x6) |
49 | model += ((a >> 16) & 0xf) << 4; | 49 | model += ((a >> 16) & 0xf) << 4; |
50 | } | 50 | } |
51 | nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); | 51 | nb = scnprintf(buffer, sz, fmt, vendor, family, model, step); |
52 | 52 | ||
53 | /* look for end marker to ensure the entire data fit */ | 53 | /* look for end marker to ensure the entire data fit */ |
54 | if (strchr(buffer, '$')) { | 54 | if (strchr(buffer, '$')) { |
@@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz) | |||
57 | } | 57 | } |
58 | return -1; | 58 | return -1; |
59 | } | 59 | } |
60 | |||
61 | int | ||
62 | get_cpuid(char *buffer, size_t sz) | ||
63 | { | ||
64 | return __get_cpuid(buffer, sz, "%s,%u,%u,%u$"); | ||
65 | } | ||
66 | |||
67 | char * | ||
68 | get_cpuid_str(void) | ||
69 | { | ||
70 | char *buf = malloc(128); | ||
71 | |||
72 | if (__get_cpuid(buf, 128, "%s-%u-%X$") < 0) { | ||
73 | free(buf); | ||
74 | return NULL; | ||
75 | } | ||
76 | return buf; | ||
77 | } | ||
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 88ee419e5189..ba9322ff858b 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -16,16 +16,23 @@ | |||
16 | #include "util/pmu.h" | 16 | #include "util/pmu.h" |
17 | #include <subcmd/parse-options.h> | 17 | #include <subcmd/parse-options.h> |
18 | 18 | ||
19 | static bool desc_flag = true; | ||
20 | |||
19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | 21 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) |
20 | { | 22 | { |
21 | int i; | 23 | int i; |
22 | bool raw_dump = false; | 24 | bool raw_dump = false; |
25 | bool long_desc_flag = false; | ||
23 | struct option list_options[] = { | 26 | struct option list_options[] = { |
24 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), | 27 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), |
28 | OPT_BOOLEAN('d', "desc", &desc_flag, | ||
29 | "Print extra event descriptions. --no-desc to not print."), | ||
30 | OPT_BOOLEAN('v', "long-desc", &long_desc_flag, | ||
31 | "Print longer event descriptions."), | ||
25 | OPT_END() | 32 | OPT_END() |
26 | }; | 33 | }; |
27 | const char * const list_usage[] = { | 34 | const char * const list_usage[] = { |
28 | "perf list [hw|sw|cache|tracepoint|pmu|sdt|event_glob]", | 35 | "perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|event_glob]", |
29 | NULL | 36 | NULL |
30 | }; | 37 | }; |
31 | 38 | ||
@@ -40,7 +47,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
40 | printf("\nList of pre-defined events (to be used in -e):\n\n"); | 47 | printf("\nList of pre-defined events (to be used in -e):\n\n"); |
41 | 48 | ||
42 | if (argc == 0) { | 49 | if (argc == 0) { |
43 | print_events(NULL, raw_dump); | 50 | print_events(NULL, raw_dump, !desc_flag, long_desc_flag); |
44 | return 0; | 51 | return 0; |
45 | } | 52 | } |
46 | 53 | ||
@@ -61,14 +68,16 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
61 | strcmp(argv[i], "hwcache") == 0) | 68 | strcmp(argv[i], "hwcache") == 0) |
62 | print_hwcache_events(NULL, raw_dump); | 69 | print_hwcache_events(NULL, raw_dump); |
63 | else if (strcmp(argv[i], "pmu") == 0) | 70 | else if (strcmp(argv[i], "pmu") == 0) |
64 | print_pmu_events(NULL, raw_dump); | 71 | print_pmu_events(NULL, raw_dump, !desc_flag, |
72 | long_desc_flag); | ||
65 | else if (strcmp(argv[i], "sdt") == 0) | 73 | else if (strcmp(argv[i], "sdt") == 0) |
66 | print_sdt_events(NULL, NULL, raw_dump); | 74 | print_sdt_events(NULL, NULL, raw_dump); |
67 | else if ((sep = strchr(argv[i], ':')) != NULL) { | 75 | else if ((sep = strchr(argv[i], ':')) != NULL) { |
68 | int sep_idx; | 76 | int sep_idx; |
69 | 77 | ||
70 | if (sep == NULL) { | 78 | if (sep == NULL) { |
71 | print_events(argv[i], raw_dump); | 79 | print_events(argv[i], raw_dump, !desc_flag, |
80 | long_desc_flag); | ||
72 | continue; | 81 | continue; |
73 | } | 82 | } |
74 | sep_idx = sep - argv[i]; | 83 | sep_idx = sep - argv[i]; |
@@ -90,7 +99,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
90 | print_symbol_events(s, PERF_TYPE_SOFTWARE, | 99 | print_symbol_events(s, PERF_TYPE_SOFTWARE, |
91 | event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump); | 100 | event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump); |
92 | print_hwcache_events(s, raw_dump); | 101 | print_hwcache_events(s, raw_dump); |
93 | print_pmu_events(s, raw_dump); | 102 | print_pmu_events(s, raw_dump, !desc_flag, |
103 | long_desc_flag); | ||
94 | print_tracepoint_events(NULL, s, raw_dump); | 104 | print_tracepoint_events(NULL, s, raw_dump); |
95 | print_sdt_events(NULL, s, raw_dump); | 105 | print_sdt_events(NULL, s, raw_dump); |
96 | free(s); | 106 | free(s); |
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build new file mode 100644 index 000000000000..9213a1273697 --- /dev/null +++ b/tools/perf/pmu-events/Build | |||
@@ -0,0 +1,13 @@ | |||
1 | hostprogs := jevents | ||
2 | |||
3 | jevents-y += json.o jsmn.o jevents.o | ||
4 | pmu-events-y += pmu-events.o | ||
5 | JDIR = pmu-events/arch/$(ARCH) | ||
6 | JSON = $(shell [ -d $(JDIR) ] && \ | ||
7 | find $(JDIR) -name '*.json' -o -name 'mapfile.csv') | ||
8 | # | ||
9 | # Locate/process JSON files in pmu-events/arch/ | ||
10 | # directory and create tables in pmu-events.c. | ||
11 | # | ||
12 | $(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS) | ||
13 | $(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V) | ||
diff --git a/tools/perf/pmu-events/README b/tools/perf/pmu-events/README new file mode 100644 index 000000000000..1408ade0d773 --- /dev/null +++ b/tools/perf/pmu-events/README | |||
@@ -0,0 +1,147 @@ | |||
1 | |||
2 | The contents of this directory allow users to specify PMU events in their | ||
3 | CPUs by their symbolic names rather than raw event codes (see example below). | ||
4 | |||
5 | The main program in this directory, is the 'jevents', which is built and | ||
6 | executed _BEFORE_ the perf binary itself is built. | ||
7 | |||
8 | The 'jevents' program tries to locate and process JSON files in the directory | ||
9 | tree tools/perf/pmu-events/arch/foo. | ||
10 | |||
11 | - Regular files with '.json' extension in the name are assumed to be | ||
12 | JSON files, each of which describes a set of PMU events. | ||
13 | |||
14 | - Regular files with basename starting with 'mapfile.csv' are assumed | ||
15 | to be a CSV file that maps a specific CPU to its set of PMU events. | ||
16 | (see below for mapfile format) | ||
17 | |||
18 | - Directories are traversed, but all other files are ignored. | ||
19 | |||
20 | The PMU events supported by a CPU model are expected to grouped into topics | ||
21 | such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic | ||
22 | should be placed in a separate JSON file - where the file name identifies | ||
23 | the topic. Eg: "Floating-point.json". | ||
24 | |||
25 | All the topic JSON files for a CPU model/family should be in a separate | ||
26 | sub directory. Thus for the Silvermont X86 CPU: | ||
27 | |||
28 | $ ls tools/perf/pmu-events/arch/x86/Silvermont_core | ||
29 | Cache.json Memory.json Virtual-Memory.json | ||
30 | Frontend.json Pipeline.json | ||
31 | |||
32 | Using the JSON files and the mapfile, 'jevents' generates the C source file, | ||
33 | 'pmu-events.c', which encodes the two sets of tables: | ||
34 | |||
35 | - Set of 'PMU events tables' for all known CPUs in the architecture, | ||
36 | (one table like the following, per JSON file; table name 'pme_power8' | ||
37 | is derived from JSON file name, 'power8.json'). | ||
38 | |||
39 | struct pmu_event pme_power8[] = { | ||
40 | |||
41 | ... | ||
42 | |||
43 | { | ||
44 | .name = "pm_1plus_ppc_cmpl", | ||
45 | .event = "event=0x100f2", | ||
46 | .desc = "1 or more ppc insts finished,", | ||
47 | }, | ||
48 | |||
49 | ... | ||
50 | } | ||
51 | |||
52 | - A 'mapping table' that maps each CPU of the architecture, to its | ||
53 | 'PMU events table' | ||
54 | |||
55 | struct pmu_events_map pmu_events_map[] = { | ||
56 | { | ||
57 | .cpuid = "004b0000", | ||
58 | .version = "1", | ||
59 | .type = "core", | ||
60 | .table = pme_power8 | ||
61 | }, | ||
62 | ... | ||
63 | |||
64 | }; | ||
65 | |||
66 | After the 'pmu-events.c' is generated, it is compiled and the resulting | ||
67 | 'pmu-events.o' is added to 'libperf.a' which is then used to build perf. | ||
68 | |||
69 | NOTES: | ||
70 | 1. Several CPUs can support same set of events and hence use a common | ||
71 | JSON file. Hence several entries in the pmu_events_map[] could map | ||
72 | to a single 'PMU events table'. | ||
73 | |||
74 | 2. The 'pmu-events.h' has an extern declaration for the mapping table | ||
75 | and the generated 'pmu-events.c' defines this table. | ||
76 | |||
77 | 3. _All_ known CPU tables for architecture are included in the perf | ||
78 | binary. | ||
79 | |||
80 | At run time, perf determines the actual CPU it is running on, finds the | ||
81 | matching events table and builds aliases for those events. This allows | ||
82 | users to specify events by their name: | ||
83 | |||
84 | $ perf stat -e pm_1plus_ppc_cmpl sleep 1 | ||
85 | |||
86 | where 'pm_1plus_ppc_cmpl' is a Power8 PMU event. | ||
87 | |||
88 | In case of errors when processing files in the tools/perf/pmu-events/arch | ||
89 | directory, 'jevents' tries to create an empty mapping file to allow the perf | ||
90 | build to succeed even if the PMU event aliases cannot be used. | ||
91 | |||
92 | However some errors in processing may cause the perf build to fail. | ||
93 | |||
94 | Mapfile format | ||
95 | =============== | ||
96 | |||
97 | The mapfile enables multiple CPU models to share a single set of PMU events. | ||
98 | It is required even if such mapping is 1:1. | ||
99 | |||
100 | The mapfile.csv format is expected to be: | ||
101 | |||
102 | Header line | ||
103 | CPUID,Version,Dir/path/name,Type | ||
104 | |||
105 | where: | ||
106 | |||
107 | Comma: | ||
108 | is the required field delimiter (i.e other fields cannot | ||
109 | have commas within them). | ||
110 | |||
111 | Comments: | ||
112 | Lines in which the first character is either '\n' or '#' | ||
113 | are ignored. | ||
114 | |||
115 | Header line | ||
116 | The header line is the first line in the file, which is | ||
117 | always _IGNORED_. It can empty. | ||
118 | |||
119 | CPUID: | ||
120 | CPUID is an arch-specific char string, that can be used | ||
121 | to identify CPU (and associate it with a set of PMU events | ||
122 | it supports). Multiple CPUIDS can point to the same | ||
123 | File/path/name.json. | ||
124 | |||
125 | Example: | ||
126 | CPUID == 'GenuineIntel-6-2E' (on x86). | ||
127 | CPUID == '004b0100' (PVR value in Powerpc) | ||
128 | Version: | ||
129 | is the Version of the mapfile. | ||
130 | |||
131 | Dir/path/name: | ||
132 | is the pathname to the directory containing the CPU's JSON | ||
133 | files, relative to the directory containing the mapfile.csv | ||
134 | |||
135 | Type: | ||
136 | indicates whether the events or "core" or "uncore" events. | ||
137 | |||
138 | |||
139 | Eg: | ||
140 | |||
141 | $ grep Silvermont tools/perf/pmu-events/arch/x86/mapfile.csv | ||
142 | GenuineIntel-6-37,V13,Silvermont_core,core | ||
143 | GenuineIntel-6-4D,V13,Silvermont_core,core | ||
144 | GenuineIntel-6-4C,V13,Silvermont_core,core | ||
145 | |||
146 | i.e the three CPU models use the JSON files (i.e PMU events) listed | ||
147 | in the directory 'tools/perf/pmu-events/arch/x86/Silvermont_core'. | ||
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c new file mode 100644 index 000000000000..41611d7f9873 --- /dev/null +++ b/tools/perf/pmu-events/jevents.c | |||
@@ -0,0 +1,814 @@ | |||
1 | #define _XOPEN_SOURCE 500 /* needed for nftw() */ | ||
2 | #define _GNU_SOURCE /* needed for asprintf() */ | ||
3 | |||
4 | /* Parse event JSON files */ | ||
5 | |||
6 | /* | ||
7 | * Copyright (c) 2014, Intel Corporation | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions are met: | ||
12 | * | ||
13 | * 1. Redistributions of source code must retain the above copyright notice, | ||
14 | * this list of conditions and the following disclaimer. | ||
15 | * | ||
16 | * 2. Redistributions in binary form must reproduce the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer in the | ||
18 | * documentation and/or other materials provided with the distribution. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
25 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
31 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #include <stdio.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <errno.h> | ||
37 | #include <string.h> | ||
38 | #include <ctype.h> | ||
39 | #include <unistd.h> | ||
40 | #include <stdarg.h> | ||
41 | #include <libgen.h> | ||
42 | #include <dirent.h> | ||
43 | #include <sys/time.h> /* getrlimit */ | ||
44 | #include <sys/resource.h> /* getrlimit */ | ||
45 | #include <ftw.h> | ||
46 | #include <sys/stat.h> | ||
47 | #include "jsmn.h" | ||
48 | #include "json.h" | ||
49 | #include "jevents.h" | ||
50 | |||
51 | #ifndef __maybe_unused | ||
52 | #define __maybe_unused __attribute__((unused)) | ||
53 | #endif | ||
54 | |||
55 | int verbose; | ||
56 | char *prog; | ||
57 | |||
58 | int eprintf(int level, int var, const char *fmt, ...) | ||
59 | { | ||
60 | |||
61 | int ret; | ||
62 | va_list args; | ||
63 | |||
64 | if (var < level) | ||
65 | return 0; | ||
66 | |||
67 | va_start(args, fmt); | ||
68 | |||
69 | ret = vfprintf(stderr, fmt, args); | ||
70 | |||
71 | va_end(args); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | __attribute__((weak)) char *get_cpu_str(void) | ||
77 | { | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | static void addfield(char *map, char **dst, const char *sep, | ||
82 | const char *a, jsmntok_t *bt) | ||
83 | { | ||
84 | unsigned int len = strlen(a) + 1 + strlen(sep); | ||
85 | int olen = *dst ? strlen(*dst) : 0; | ||
86 | int blen = bt ? json_len(bt) : 0; | ||
87 | char *out; | ||
88 | |||
89 | out = realloc(*dst, len + olen + blen); | ||
90 | if (!out) { | ||
91 | /* Don't add field in this case */ | ||
92 | return; | ||
93 | } | ||
94 | *dst = out; | ||
95 | |||
96 | if (!olen) | ||
97 | *(*dst) = 0; | ||
98 | else | ||
99 | strcat(*dst, sep); | ||
100 | strcat(*dst, a); | ||
101 | if (bt) | ||
102 | strncat(*dst, map + bt->start, blen); | ||
103 | } | ||
104 | |||
105 | static void fixname(char *s) | ||
106 | { | ||
107 | for (; *s; s++) | ||
108 | *s = tolower(*s); | ||
109 | } | ||
110 | |||
111 | static void fixdesc(char *s) | ||
112 | { | ||
113 | char *e = s + strlen(s); | ||
114 | |||
115 | /* Remove trailing dots that look ugly in perf list */ | ||
116 | --e; | ||
117 | while (e >= s && isspace(*e)) | ||
118 | --e; | ||
119 | if (*e == '.') | ||
120 | *e = 0; | ||
121 | } | ||
122 | |||
123 | static struct msrmap { | ||
124 | const char *num; | ||
125 | const char *pname; | ||
126 | } msrmap[] = { | ||
127 | { "0x3F6", "ldlat=" }, | ||
128 | { "0x1A6", "offcore_rsp=" }, | ||
129 | { "0x1A7", "offcore_rsp=" }, | ||
130 | { "0x3F7", "frontend=" }, | ||
131 | { NULL, NULL } | ||
132 | }; | ||
133 | |||
134 | static struct field { | ||
135 | const char *field; | ||
136 | const char *kernel; | ||
137 | } fields[] = { | ||
138 | { "EventCode", "event=" }, | ||
139 | { "UMask", "umask=" }, | ||
140 | { "CounterMask", "cmask=" }, | ||
141 | { "Invert", "inv=" }, | ||
142 | { "AnyThread", "any=" }, | ||
143 | { "EdgeDetect", "edge=" }, | ||
144 | { "SampleAfterValue", "period=" }, | ||
145 | { NULL, NULL } | ||
146 | }; | ||
147 | |||
148 | static void cut_comma(char *map, jsmntok_t *newval) | ||
149 | { | ||
150 | int i; | ||
151 | |||
152 | /* Cut off everything after comma */ | ||
153 | for (i = newval->start; i < newval->end; i++) { | ||
154 | if (map[i] == ',') | ||
155 | newval->end = i; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | static int match_field(char *map, jsmntok_t *field, int nz, | ||
160 | char **event, jsmntok_t *val) | ||
161 | { | ||
162 | struct field *f; | ||
163 | jsmntok_t newval = *val; | ||
164 | |||
165 | for (f = fields; f->field; f++) | ||
166 | if (json_streq(map, field, f->field) && nz) { | ||
167 | cut_comma(map, &newval); | ||
168 | addfield(map, event, ",", f->kernel, &newval); | ||
169 | return 1; | ||
170 | } | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct msrmap *lookup_msr(char *map, jsmntok_t *val) | ||
175 | { | ||
176 | jsmntok_t newval = *val; | ||
177 | static bool warned; | ||
178 | int i; | ||
179 | |||
180 | cut_comma(map, &newval); | ||
181 | for (i = 0; msrmap[i].num; i++) | ||
182 | if (json_streq(map, &newval, msrmap[i].num)) | ||
183 | return &msrmap[i]; | ||
184 | if (!warned) { | ||
185 | warned = true; | ||
186 | pr_err("%s: Unknown MSR in event file %.*s\n", prog, | ||
187 | json_len(val), map + val->start); | ||
188 | } | ||
189 | return NULL; | ||
190 | } | ||
191 | |||
192 | #define EXPECT(e, t, m) do { if (!(e)) { \ | ||
193 | jsmntok_t *loc = (t); \ | ||
194 | if (!(t)->start && (t) > tokens) \ | ||
195 | loc = (t) - 1; \ | ||
196 | pr_err("%s:%d: " m ", got %s\n", fn, \ | ||
197 | json_line(map, loc), \ | ||
198 | json_name(t)); \ | ||
199 | goto out_free; \ | ||
200 | } } while (0) | ||
201 | |||
202 | #define TOPIC_DEPTH 256 | ||
203 | static char *topic_array[TOPIC_DEPTH]; | ||
204 | static int topic_level; | ||
205 | |||
206 | static char *get_topic(void) | ||
207 | { | ||
208 | char *tp_old, *tp = NULL; | ||
209 | int i; | ||
210 | |||
211 | for (i = 0; i < topic_level + 1; i++) { | ||
212 | int n; | ||
213 | |||
214 | tp_old = tp; | ||
215 | n = asprintf(&tp, "%s%s", tp ?: "", topic_array[i]); | ||
216 | if (n < 0) { | ||
217 | pr_info("%s: asprintf() error %s\n", prog); | ||
218 | return NULL; | ||
219 | } | ||
220 | free(tp_old); | ||
221 | } | ||
222 | |||
223 | for (i = 0; i < (int) strlen(tp); i++) { | ||
224 | char c = tp[i]; | ||
225 | |||
226 | if (c == '-') | ||
227 | tp[i] = ' '; | ||
228 | else if (c == '.') { | ||
229 | tp[i] = '\0'; | ||
230 | break; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | return tp; | ||
235 | } | ||
236 | |||
237 | static int add_topic(int level, char *bname) | ||
238 | { | ||
239 | char *topic; | ||
240 | |||
241 | level -= 2; | ||
242 | |||
243 | if (level >= TOPIC_DEPTH) | ||
244 | return -EINVAL; | ||
245 | |||
246 | topic = strdup(bname); | ||
247 | if (!topic) { | ||
248 | pr_info("%s: strdup() error %s for file %s\n", prog, | ||
249 | strerror(errno), bname); | ||
250 | return -ENOMEM; | ||
251 | } | ||
252 | |||
253 | free(topic_array[topic_level]); | ||
254 | topic_array[topic_level] = topic; | ||
255 | topic_level = level; | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | struct perf_entry_data { | ||
260 | FILE *outfp; | ||
261 | char *topic; | ||
262 | }; | ||
263 | |||
264 | static int close_table; | ||
265 | |||
266 | static void print_events_table_prefix(FILE *fp, const char *tblname) | ||
267 | { | ||
268 | fprintf(fp, "struct pmu_event %s[] = {\n", tblname); | ||
269 | close_table = 1; | ||
270 | } | ||
271 | |||
272 | static int print_events_table_entry(void *data, char *name, char *event, | ||
273 | char *desc, char *long_desc) | ||
274 | { | ||
275 | struct perf_entry_data *pd = data; | ||
276 | FILE *outfp = pd->outfp; | ||
277 | char *topic = pd->topic; | ||
278 | |||
279 | /* | ||
280 | * TODO: Remove formatting chars after debugging to reduce | ||
281 | * string lengths. | ||
282 | */ | ||
283 | fprintf(outfp, "{\n"); | ||
284 | |||
285 | fprintf(outfp, "\t.name = \"%s\",\n", name); | ||
286 | fprintf(outfp, "\t.event = \"%s\",\n", event); | ||
287 | fprintf(outfp, "\t.desc = \"%s\",\n", desc); | ||
288 | fprintf(outfp, "\t.topic = \"%s\",\n", topic); | ||
289 | if (long_desc && long_desc[0]) | ||
290 | fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc); | ||
291 | |||
292 | fprintf(outfp, "},\n"); | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static void print_events_table_suffix(FILE *outfp) | ||
298 | { | ||
299 | fprintf(outfp, "{\n"); | ||
300 | |||
301 | fprintf(outfp, "\t.name = 0,\n"); | ||
302 | fprintf(outfp, "\t.event = 0,\n"); | ||
303 | fprintf(outfp, "\t.desc = 0,\n"); | ||
304 | |||
305 | fprintf(outfp, "},\n"); | ||
306 | fprintf(outfp, "};\n"); | ||
307 | close_table = 0; | ||
308 | } | ||
309 | |||
310 | static struct fixed { | ||
311 | const char *name; | ||
312 | const char *event; | ||
313 | } fixed[] = { | ||
314 | { "inst_retired.any", "event=0xc0" }, | ||
315 | { "inst_retired.any_p", "event=0xc0" }, | ||
316 | { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" }, | ||
317 | { "cpu_clk_unhalted.thread", "event=0x3c" }, | ||
318 | { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" }, | ||
319 | { NULL, NULL}, | ||
320 | }; | ||
321 | |||
322 | /* | ||
323 | * Handle different fixed counter encodings between JSON and perf. | ||
324 | */ | ||
325 | static char *real_event(const char *name, char *event) | ||
326 | { | ||
327 | int i; | ||
328 | |||
329 | for (i = 0; fixed[i].name; i++) | ||
330 | if (!strcasecmp(name, fixed[i].name)) | ||
331 | return (char *)fixed[i].event; | ||
332 | return event; | ||
333 | } | ||
334 | |||
335 | /* Call func with each event in the json file */ | ||
336 | int json_events(const char *fn, | ||
337 | int (*func)(void *data, char *name, char *event, char *desc, | ||
338 | char *long_desc), | ||
339 | void *data) | ||
340 | { | ||
341 | int err = -EIO; | ||
342 | size_t size; | ||
343 | jsmntok_t *tokens, *tok; | ||
344 | int i, j, len; | ||
345 | char *map; | ||
346 | |||
347 | if (!fn) | ||
348 | return -ENOENT; | ||
349 | |||
350 | tokens = parse_json(fn, &map, &size, &len); | ||
351 | if (!tokens) | ||
352 | return -EIO; | ||
353 | EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array"); | ||
354 | tok = tokens + 1; | ||
355 | for (i = 0; i < tokens->size; i++) { | ||
356 | char *event = NULL, *desc = NULL, *name = NULL; | ||
357 | char *long_desc = NULL; | ||
358 | char *extra_desc = NULL; | ||
359 | struct msrmap *msr = NULL; | ||
360 | jsmntok_t *msrval = NULL; | ||
361 | jsmntok_t *precise = NULL; | ||
362 | jsmntok_t *obj = tok++; | ||
363 | |||
364 | EXPECT(obj->type == JSMN_OBJECT, obj, "expected object"); | ||
365 | for (j = 0; j < obj->size; j += 2) { | ||
366 | jsmntok_t *field, *val; | ||
367 | int nz; | ||
368 | |||
369 | field = tok + j; | ||
370 | EXPECT(field->type == JSMN_STRING, tok + j, | ||
371 | "Expected field name"); | ||
372 | val = tok + j + 1; | ||
373 | EXPECT(val->type == JSMN_STRING, tok + j + 1, | ||
374 | "Expected string value"); | ||
375 | |||
376 | nz = !json_streq(map, val, "0"); | ||
377 | if (match_field(map, field, nz, &event, val)) { | ||
378 | /* ok */ | ||
379 | } else if (json_streq(map, field, "EventName")) { | ||
380 | addfield(map, &name, "", "", val); | ||
381 | } else if (json_streq(map, field, "BriefDescription")) { | ||
382 | addfield(map, &desc, "", "", val); | ||
383 | fixdesc(desc); | ||
384 | } else if (json_streq(map, field, | ||
385 | "PublicDescription")) { | ||
386 | addfield(map, &long_desc, "", "", val); | ||
387 | fixdesc(long_desc); | ||
388 | } else if (json_streq(map, field, "PEBS") && nz) { | ||
389 | precise = val; | ||
390 | } else if (json_streq(map, field, "MSRIndex") && nz) { | ||
391 | msr = lookup_msr(map, val); | ||
392 | } else if (json_streq(map, field, "MSRValue")) { | ||
393 | msrval = val; | ||
394 | } else if (json_streq(map, field, "Errata") && | ||
395 | !json_streq(map, val, "null")) { | ||
396 | addfield(map, &extra_desc, ". ", | ||
397 | " Spec update: ", val); | ||
398 | } else if (json_streq(map, field, "Data_LA") && nz) { | ||
399 | addfield(map, &extra_desc, ". ", | ||
400 | " Supports address when precise", | ||
401 | NULL); | ||
402 | } | ||
403 | /* ignore unknown fields */ | ||
404 | } | ||
405 | if (precise && desc && !strstr(desc, "(Precise Event)")) { | ||
406 | if (json_streq(map, precise, "2")) | ||
407 | addfield(map, &extra_desc, " ", | ||
408 | "(Must be precise)", NULL); | ||
409 | else | ||
410 | addfield(map, &extra_desc, " ", | ||
411 | "(Precise event)", NULL); | ||
412 | } | ||
413 | if (desc && extra_desc) | ||
414 | addfield(map, &desc, " ", extra_desc, NULL); | ||
415 | if (long_desc && extra_desc) | ||
416 | addfield(map, &long_desc, " ", extra_desc, NULL); | ||
417 | if (msr != NULL) | ||
418 | addfield(map, &event, ",", msr->pname, msrval); | ||
419 | fixname(name); | ||
420 | |||
421 | err = func(data, name, real_event(name, event), desc, long_desc); | ||
422 | free(event); | ||
423 | free(desc); | ||
424 | free(name); | ||
425 | free(long_desc); | ||
426 | free(extra_desc); | ||
427 | if (err) | ||
428 | break; | ||
429 | tok += j; | ||
430 | } | ||
431 | EXPECT(tok - tokens == len, tok, "unexpected objects at end"); | ||
432 | err = 0; | ||
433 | out_free: | ||
434 | free_json(map, size, tokens); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | static char *file_name_to_table_name(char *fname) | ||
439 | { | ||
440 | unsigned int i; | ||
441 | int n; | ||
442 | int c; | ||
443 | char *tblname; | ||
444 | |||
445 | /* | ||
446 | * Ensure tablename starts with alphabetic character. | ||
447 | * Derive rest of table name from basename of the JSON file, | ||
448 | * replacing hyphens and stripping out .json suffix. | ||
449 | */ | ||
450 | n = asprintf(&tblname, "pme_%s", basename(fname)); | ||
451 | if (n < 0) { | ||
452 | pr_info("%s: asprintf() error %s for file %s\n", prog, | ||
453 | strerror(errno), fname); | ||
454 | return NULL; | ||
455 | } | ||
456 | |||
457 | for (i = 0; i < strlen(tblname); i++) { | ||
458 | c = tblname[i]; | ||
459 | |||
460 | if (c == '-') | ||
461 | tblname[i] = '_'; | ||
462 | else if (c == '.') { | ||
463 | tblname[i] = '\0'; | ||
464 | break; | ||
465 | } else if (!isalnum(c) && c != '_') { | ||
466 | pr_err("%s: Invalid character '%c' in file name %s\n", | ||
467 | prog, c, basename(fname)); | ||
468 | free(tblname); | ||
469 | tblname = NULL; | ||
470 | break; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return tblname; | ||
475 | } | ||
476 | |||
477 | static void print_mapping_table_prefix(FILE *outfp) | ||
478 | { | ||
479 | fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n"); | ||
480 | } | ||
481 | |||
482 | static void print_mapping_table_suffix(FILE *outfp) | ||
483 | { | ||
484 | /* | ||
485 | * Print the terminating, NULL entry. | ||
486 | */ | ||
487 | fprintf(outfp, "{\n"); | ||
488 | fprintf(outfp, "\t.cpuid = 0,\n"); | ||
489 | fprintf(outfp, "\t.version = 0,\n"); | ||
490 | fprintf(outfp, "\t.type = 0,\n"); | ||
491 | fprintf(outfp, "\t.table = 0,\n"); | ||
492 | fprintf(outfp, "},\n"); | ||
493 | |||
494 | /* and finally, the closing curly bracket for the struct */ | ||
495 | fprintf(outfp, "};\n"); | ||
496 | } | ||
497 | |||
498 | static int process_mapfile(FILE *outfp, char *fpath) | ||
499 | { | ||
500 | int n = 16384; | ||
501 | FILE *mapfp; | ||
502 | char *save = NULL; | ||
503 | char *line, *p; | ||
504 | int line_num; | ||
505 | char *tblname; | ||
506 | |||
507 | pr_info("%s: Processing mapfile %s\n", prog, fpath); | ||
508 | |||
509 | line = malloc(n); | ||
510 | if (!line) | ||
511 | return -1; | ||
512 | |||
513 | mapfp = fopen(fpath, "r"); | ||
514 | if (!mapfp) { | ||
515 | pr_info("%s: Error %s opening %s\n", prog, strerror(errno), | ||
516 | fpath); | ||
517 | return -1; | ||
518 | } | ||
519 | |||
520 | print_mapping_table_prefix(outfp); | ||
521 | |||
522 | /* Skip first line (header) */ | ||
523 | p = fgets(line, n, mapfp); | ||
524 | if (!p) | ||
525 | goto out; | ||
526 | |||
527 | line_num = 1; | ||
528 | while (1) { | ||
529 | char *cpuid, *version, *type, *fname; | ||
530 | |||
531 | line_num++; | ||
532 | p = fgets(line, n, mapfp); | ||
533 | if (!p) | ||
534 | break; | ||
535 | |||
536 | if (line[0] == '#' || line[0] == '\n') | ||
537 | continue; | ||
538 | |||
539 | if (line[strlen(line)-1] != '\n') { | ||
540 | /* TODO Deal with lines longer than 16K */ | ||
541 | pr_info("%s: Mapfile %s: line %d too long, aborting\n", | ||
542 | prog, fpath, line_num); | ||
543 | return -1; | ||
544 | } | ||
545 | line[strlen(line)-1] = '\0'; | ||
546 | |||
547 | cpuid = strtok_r(p, ",", &save); | ||
548 | version = strtok_r(NULL, ",", &save); | ||
549 | fname = strtok_r(NULL, ",", &save); | ||
550 | type = strtok_r(NULL, ",", &save); | ||
551 | |||
552 | tblname = file_name_to_table_name(fname); | ||
553 | fprintf(outfp, "{\n"); | ||
554 | fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid); | ||
555 | fprintf(outfp, "\t.version = \"%s\",\n", version); | ||
556 | fprintf(outfp, "\t.type = \"%s\",\n", type); | ||
557 | |||
558 | /* | ||
559 | * CHECK: We can't use the type (eg "core") field in the | ||
560 | * table name. For us to do that, we need to somehow tweak | ||
561 | * the other caller of file_name_to_table(), process_json() | ||
562 | * to determine the type. process_json() file has no way | ||
563 | * of knowing these are "core" events unless file name has | ||
564 | * core in it. If filename has core in it, we can safely | ||
565 | * ignore the type field here also. | ||
566 | */ | ||
567 | fprintf(outfp, "\t.table = %s\n", tblname); | ||
568 | fprintf(outfp, "},\n"); | ||
569 | } | ||
570 | |||
571 | out: | ||
572 | print_mapping_table_suffix(outfp); | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * If we fail to locate/process JSON and map files, create a NULL mapping | ||
578 | * table. This would at least allow perf to build even if we can't find/use | ||
579 | * the aliases. | ||
580 | */ | ||
581 | static void create_empty_mapping(const char *output_file) | ||
582 | { | ||
583 | FILE *outfp; | ||
584 | |||
585 | pr_info("%s: Creating empty pmu_events_map[] table\n", prog); | ||
586 | |||
587 | /* Truncate file to clear any partial writes to it */ | ||
588 | outfp = fopen(output_file, "w"); | ||
589 | if (!outfp) { | ||
590 | perror("fopen()"); | ||
591 | _Exit(1); | ||
592 | } | ||
593 | |||
594 | fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n"); | ||
595 | print_mapping_table_prefix(outfp); | ||
596 | print_mapping_table_suffix(outfp); | ||
597 | fclose(outfp); | ||
598 | } | ||
599 | |||
600 | static int get_maxfds(void) | ||
601 | { | ||
602 | struct rlimit rlim; | ||
603 | |||
604 | if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) | ||
605 | return min((int)rlim.rlim_max / 2, 512); | ||
606 | |||
607 | return 512; | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * nftw() doesn't let us pass an argument to the processing function, | ||
612 | * so use a global variables. | ||
613 | */ | ||
614 | static FILE *eventsfp; | ||
615 | static char *mapfile; | ||
616 | |||
617 | static int process_one_file(const char *fpath, const struct stat *sb, | ||
618 | int typeflag, struct FTW *ftwbuf) | ||
619 | { | ||
620 | char *tblname, *bname = (char *) fpath + ftwbuf->base; | ||
621 | int is_dir = typeflag == FTW_D; | ||
622 | int is_file = typeflag == FTW_F; | ||
623 | int level = ftwbuf->level; | ||
624 | int err = 0; | ||
625 | |||
626 | pr_debug("%s %d %7jd %-20s %s\n", | ||
627 | is_file ? "f" : is_dir ? "d" : "x", | ||
628 | level, sb->st_size, bname, fpath); | ||
629 | |||
630 | /* base dir */ | ||
631 | if (level == 0) | ||
632 | return 0; | ||
633 | |||
634 | /* model directory, reset topic */ | ||
635 | if (level == 1 && is_dir) { | ||
636 | if (close_table) | ||
637 | print_events_table_suffix(eventsfp); | ||
638 | |||
639 | /* | ||
640 | * Drop file name suffix. Replace hyphens with underscores. | ||
641 | * Fail if file name contains any alphanum characters besides | ||
642 | * underscores. | ||
643 | */ | ||
644 | tblname = file_name_to_table_name(bname); | ||
645 | if (!tblname) { | ||
646 | pr_info("%s: Error determining table name for %s\n", prog, | ||
647 | bname); | ||
648 | return -1; | ||
649 | } | ||
650 | |||
651 | print_events_table_prefix(eventsfp, tblname); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * Save the mapfile name for now. We will process mapfile | ||
657 | * after processing all JSON files (so we can write out the | ||
658 | * mapping table after all PMU events tables). | ||
659 | * | ||
660 | * TODO: Allow for multiple mapfiles? Punt for now. | ||
661 | */ | ||
662 | if (level == 1 && is_file) { | ||
663 | if (!strncmp(bname, "mapfile.csv", 11)) { | ||
664 | if (mapfile) { | ||
665 | pr_info("%s: Many mapfiles? Using %s, ignoring %s\n", | ||
666 | prog, mapfile, fpath); | ||
667 | } else { | ||
668 | mapfile = strdup(fpath); | ||
669 | } | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | pr_info("%s: Ignoring file %s\n", prog, fpath); | ||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * If the file name does not have a .json extension, | ||
679 | * ignore it. It could be a readme.txt for instance. | ||
680 | */ | ||
681 | if (is_file) { | ||
682 | char *suffix = bname + strlen(bname) - 5; | ||
683 | |||
684 | if (strncmp(suffix, ".json", 5)) { | ||
685 | pr_info("%s: Ignoring file without .json suffix %s\n", prog, | ||
686 | fpath); | ||
687 | return 0; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | if (level > 1 && add_topic(level, bname)) | ||
692 | return -ENOMEM; | ||
693 | |||
694 | /* | ||
695 | * Assume all other files are JSON files. | ||
696 | * | ||
697 | * If mapfile refers to 'power7_core.json', we create a table | ||
698 | * named 'power7_core'. Any inconsistencies between the mapfile | ||
699 | * and directory tree could result in build failure due to table | ||
700 | * names not being found. | ||
701 | * | ||
702 | * Atleast for now, be strict with processing JSON file names. | ||
703 | * i.e. if JSON file name cannot be mapped to C-style table name, | ||
704 | * fail. | ||
705 | */ | ||
706 | if (is_file) { | ||
707 | struct perf_entry_data data = { | ||
708 | .topic = get_topic(), | ||
709 | .outfp = eventsfp, | ||
710 | }; | ||
711 | |||
712 | err = json_events(fpath, print_events_table_entry, &data); | ||
713 | |||
714 | free(data.topic); | ||
715 | } | ||
716 | |||
717 | return err; | ||
718 | } | ||
719 | |||
720 | #ifndef PATH_MAX | ||
721 | #define PATH_MAX 4096 | ||
722 | #endif | ||
723 | |||
724 | /* | ||
725 | * Starting in directory 'start_dirname', find the "mapfile.csv" and | ||
726 | * the set of JSON files for the architecture 'arch'. | ||
727 | * | ||
728 | * From each JSON file, create a C-style "PMU events table" from the | ||
729 | * JSON file (see struct pmu_event). | ||
730 | * | ||
731 | * From the mapfile, create a mapping between the CPU revisions and | ||
732 | * PMU event tables (see struct pmu_events_map). | ||
733 | * | ||
734 | * Write out the PMU events tables and the mapping table to pmu-event.c. | ||
735 | * | ||
736 | * If unable to process the JSON or arch files, create an empty mapping | ||
737 | * table so we can continue to build/use perf even if we cannot use the | ||
738 | * PMU event aliases. | ||
739 | */ | ||
740 | int main(int argc, char *argv[]) | ||
741 | { | ||
742 | int rc; | ||
743 | int maxfds; | ||
744 | char ldirname[PATH_MAX]; | ||
745 | |||
746 | const char *arch; | ||
747 | const char *output_file; | ||
748 | const char *start_dirname; | ||
749 | |||
750 | prog = basename(argv[0]); | ||
751 | if (argc < 4) { | ||
752 | pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog); | ||
753 | return 1; | ||
754 | } | ||
755 | |||
756 | arch = argv[1]; | ||
757 | start_dirname = argv[2]; | ||
758 | output_file = argv[3]; | ||
759 | |||
760 | if (argc > 4) | ||
761 | verbose = atoi(argv[4]); | ||
762 | |||
763 | eventsfp = fopen(output_file, "w"); | ||
764 | if (!eventsfp) { | ||
765 | pr_err("%s Unable to create required file %s (%s)\n", | ||
766 | prog, output_file, strerror(errno)); | ||
767 | return 2; | ||
768 | } | ||
769 | |||
770 | /* Include pmu-events.h first */ | ||
771 | fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n"); | ||
772 | |||
773 | sprintf(ldirname, "%s/%s", start_dirname, arch); | ||
774 | |||
775 | /* | ||
776 | * The mapfile allows multiple CPUids to point to the same JSON file, | ||
777 | * so, not sure if there is a need for symlinks within the pmu-events | ||
778 | * directory. | ||
779 | * | ||
780 | * For now, treat symlinks of JSON files as regular files and create | ||
781 | * separate tables for each symlink (presumably, each symlink refers | ||
782 | * to specific version of the CPU). | ||
783 | */ | ||
784 | |||
785 | maxfds = get_maxfds(); | ||
786 | mapfile = NULL; | ||
787 | rc = nftw(ldirname, process_one_file, maxfds, 0); | ||
788 | if (rc && verbose) { | ||
789 | pr_info("%s: Error walking file tree %s\n", prog, ldirname); | ||
790 | goto empty_map; | ||
791 | } else if (rc) { | ||
792 | goto empty_map; | ||
793 | } | ||
794 | |||
795 | if (close_table) | ||
796 | print_events_table_suffix(eventsfp); | ||
797 | |||
798 | if (!mapfile) { | ||
799 | pr_info("%s: No CPU->JSON mapping?\n", prog); | ||
800 | goto empty_map; | ||
801 | } | ||
802 | |||
803 | if (process_mapfile(eventsfp, mapfile)) { | ||
804 | pr_info("%s: Error processing mapfile %s\n", prog, mapfile); | ||
805 | goto empty_map; | ||
806 | } | ||
807 | |||
808 | return 0; | ||
809 | |||
810 | empty_map: | ||
811 | fclose(eventsfp); | ||
812 | create_empty_mapping(output_file); | ||
813 | return 0; | ||
814 | } | ||
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h new file mode 100644 index 000000000000..b0eb2744b498 --- /dev/null +++ b/tools/perf/pmu-events/jevents.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef JEVENTS_H | ||
2 | #define JEVENTS_H 1 | ||
3 | |||
4 | int json_events(const char *fn, | ||
5 | int (*func)(void *data, char *name, char *event, char *desc, | ||
6 | char *long_desc), | ||
7 | void *data); | ||
8 | char *get_cpu_str(void); | ||
9 | |||
10 | #ifndef min | ||
11 | #define min(x, y) ({ \ | ||
12 | typeof(x) _min1 = (x); \ | ||
13 | typeof(y) _min2 = (y); \ | ||
14 | (void) (&_min1 == &_min2); \ | ||
15 | _min1 < _min2 ? _min1 : _min2; }) | ||
16 | #endif | ||
17 | |||
18 | #endif | ||
diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c new file mode 100644 index 000000000000..11d1fa18bfa5 --- /dev/null +++ b/tools/perf/pmu-events/jsmn.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Serge A. Zaitsev | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | * of this software and associated documentation files (the "Software"), to deal | ||
6 | * in the Software without restriction, including without limitation the rights | ||
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
8 | * copies of the Software, and to permit persons to whom the Software is | ||
9 | * furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
20 | * THE SOFTWARE. | ||
21 | * | ||
22 | * Slightly modified by AK to not assume 0 terminated input. | ||
23 | */ | ||
24 | |||
25 | #include <stdlib.h> | ||
26 | #include "jsmn.h" | ||
27 | |||
28 | /* | ||
29 | * Allocates a fresh unused token from the token pool. | ||
30 | */ | ||
31 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, | ||
32 | jsmntok_t *tokens, size_t num_tokens) | ||
33 | { | ||
34 | jsmntok_t *tok; | ||
35 | |||
36 | if ((unsigned)parser->toknext >= num_tokens) | ||
37 | return NULL; | ||
38 | tok = &tokens[parser->toknext++]; | ||
39 | tok->start = tok->end = -1; | ||
40 | tok->size = 0; | ||
41 | return tok; | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * Fills token type and boundaries. | ||
46 | */ | ||
47 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, | ||
48 | int start, int end) | ||
49 | { | ||
50 | token->type = type; | ||
51 | token->start = start; | ||
52 | token->end = end; | ||
53 | token->size = 0; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Fills next available token with JSON primitive. | ||
58 | */ | ||
59 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, | ||
60 | size_t len, | ||
61 | jsmntok_t *tokens, size_t num_tokens) | ||
62 | { | ||
63 | jsmntok_t *token; | ||
64 | int start; | ||
65 | |||
66 | start = parser->pos; | ||
67 | |||
68 | for (; parser->pos < len; parser->pos++) { | ||
69 | switch (js[parser->pos]) { | ||
70 | #ifndef JSMN_STRICT | ||
71 | /* | ||
72 | * In strict mode primitive must be followed by "," | ||
73 | * or "}" or "]" | ||
74 | */ | ||
75 | case ':': | ||
76 | #endif | ||
77 | case '\t': | ||
78 | case '\r': | ||
79 | case '\n': | ||
80 | case ' ': | ||
81 | case ',': | ||
82 | case ']': | ||
83 | case '}': | ||
84 | goto found; | ||
85 | default: | ||
86 | break; | ||
87 | } | ||
88 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { | ||
89 | parser->pos = start; | ||
90 | return JSMN_ERROR_INVAL; | ||
91 | } | ||
92 | } | ||
93 | #ifdef JSMN_STRICT | ||
94 | /* | ||
95 | * In strict mode primitive must be followed by a | ||
96 | * comma/object/array. | ||
97 | */ | ||
98 | parser->pos = start; | ||
99 | return JSMN_ERROR_PART; | ||
100 | #endif | ||
101 | |||
102 | found: | ||
103 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
104 | if (token == NULL) { | ||
105 | parser->pos = start; | ||
106 | return JSMN_ERROR_NOMEM; | ||
107 | } | ||
108 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | ||
109 | parser->pos--; /* parent sees closing brackets */ | ||
110 | return JSMN_SUCCESS; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Fills next token with JSON string. | ||
115 | */ | ||
116 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, | ||
117 | size_t len, | ||
118 | jsmntok_t *tokens, size_t num_tokens) | ||
119 | { | ||
120 | jsmntok_t *token; | ||
121 | int start = parser->pos; | ||
122 | |||
123 | /* Skip starting quote */ | ||
124 | parser->pos++; | ||
125 | |||
126 | for (; parser->pos < len; parser->pos++) { | ||
127 | char c = js[parser->pos]; | ||
128 | |||
129 | /* Quote: end of string */ | ||
130 | if (c == '\"') { | ||
131 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
132 | if (token == NULL) { | ||
133 | parser->pos = start; | ||
134 | return JSMN_ERROR_NOMEM; | ||
135 | } | ||
136 | jsmn_fill_token(token, JSMN_STRING, start+1, | ||
137 | parser->pos); | ||
138 | return JSMN_SUCCESS; | ||
139 | } | ||
140 | |||
141 | /* Backslash: Quoted symbol expected */ | ||
142 | if (c == '\\') { | ||
143 | parser->pos++; | ||
144 | switch (js[parser->pos]) { | ||
145 | /* Allowed escaped symbols */ | ||
146 | case '\"': | ||
147 | case '/': | ||
148 | case '\\': | ||
149 | case 'b': | ||
150 | case 'f': | ||
151 | case 'r': | ||
152 | case 'n': | ||
153 | case 't': | ||
154 | break; | ||
155 | /* Allows escaped symbol \uXXXX */ | ||
156 | case 'u': | ||
157 | /* TODO */ | ||
158 | break; | ||
159 | /* Unexpected symbol */ | ||
160 | default: | ||
161 | parser->pos = start; | ||
162 | return JSMN_ERROR_INVAL; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | parser->pos = start; | ||
167 | return JSMN_ERROR_PART; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Parse JSON string and fill tokens. | ||
172 | */ | ||
173 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, | ||
174 | jsmntok_t *tokens, unsigned int num_tokens) | ||
175 | { | ||
176 | jsmnerr_t r; | ||
177 | int i; | ||
178 | jsmntok_t *token; | ||
179 | |||
180 | for (; parser->pos < len; parser->pos++) { | ||
181 | char c; | ||
182 | jsmntype_t type; | ||
183 | |||
184 | c = js[parser->pos]; | ||
185 | switch (c) { | ||
186 | case '{': | ||
187 | case '[': | ||
188 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
189 | if (token == NULL) | ||
190 | return JSMN_ERROR_NOMEM; | ||
191 | if (parser->toksuper != -1) | ||
192 | tokens[parser->toksuper].size++; | ||
193 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | ||
194 | token->start = parser->pos; | ||
195 | parser->toksuper = parser->toknext - 1; | ||
196 | break; | ||
197 | case '}': | ||
198 | case ']': | ||
199 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | ||
200 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
201 | token = &tokens[i]; | ||
202 | if (token->start != -1 && token->end == -1) { | ||
203 | if (token->type != type) | ||
204 | return JSMN_ERROR_INVAL; | ||
205 | parser->toksuper = -1; | ||
206 | token->end = parser->pos + 1; | ||
207 | break; | ||
208 | } | ||
209 | } | ||
210 | /* Error if unmatched closing bracket */ | ||
211 | if (i == -1) | ||
212 | return JSMN_ERROR_INVAL; | ||
213 | for (; i >= 0; i--) { | ||
214 | token = &tokens[i]; | ||
215 | if (token->start != -1 && token->end == -1) { | ||
216 | parser->toksuper = i; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | break; | ||
221 | case '\"': | ||
222 | r = jsmn_parse_string(parser, js, len, tokens, | ||
223 | num_tokens); | ||
224 | if (r < 0) | ||
225 | return r; | ||
226 | if (parser->toksuper != -1) | ||
227 | tokens[parser->toksuper].size++; | ||
228 | break; | ||
229 | case '\t': | ||
230 | case '\r': | ||
231 | case '\n': | ||
232 | case ':': | ||
233 | case ',': | ||
234 | case ' ': | ||
235 | break; | ||
236 | #ifdef JSMN_STRICT | ||
237 | /* | ||
238 | * In strict mode primitives are: | ||
239 | * numbers and booleans. | ||
240 | */ | ||
241 | case '-': | ||
242 | case '0': | ||
243 | case '1': | ||
244 | case '2': | ||
245 | case '3': | ||
246 | case '4': | ||
247 | case '5': | ||
248 | case '6': | ||
249 | case '7': | ||
250 | case '8': | ||
251 | case '9': | ||
252 | case 't': | ||
253 | case 'f': | ||
254 | case 'n': | ||
255 | #else | ||
256 | /* | ||
257 | * In non-strict mode every unquoted value | ||
258 | * is a primitive. | ||
259 | */ | ||
260 | /*FALL THROUGH */ | ||
261 | default: | ||
262 | #endif | ||
263 | r = jsmn_parse_primitive(parser, js, len, tokens, | ||
264 | num_tokens); | ||
265 | if (r < 0) | ||
266 | return r; | ||
267 | if (parser->toksuper != -1) | ||
268 | tokens[parser->toksuper].size++; | ||
269 | break; | ||
270 | |||
271 | #ifdef JSMN_STRICT | ||
272 | /* Unexpected char in strict mode */ | ||
273 | default: | ||
274 | return JSMN_ERROR_INVAL; | ||
275 | #endif | ||
276 | } | ||
277 | } | ||
278 | |||
279 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
280 | /* Unmatched opened object or array */ | ||
281 | if (tokens[i].start != -1 && tokens[i].end == -1) | ||
282 | return JSMN_ERROR_PART; | ||
283 | } | ||
284 | |||
285 | return JSMN_SUCCESS; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Creates a new parser based over a given buffer with an array of tokens | ||
290 | * available. | ||
291 | */ | ||
292 | void jsmn_init(jsmn_parser *parser) | ||
293 | { | ||
294 | parser->pos = 0; | ||
295 | parser->toknext = 0; | ||
296 | parser->toksuper = -1; | ||
297 | } | ||
298 | |||
299 | const char *jsmn_strerror(jsmnerr_t err) | ||
300 | { | ||
301 | switch (err) { | ||
302 | case JSMN_ERROR_NOMEM: | ||
303 | return "No enough tokens"; | ||
304 | case JSMN_ERROR_INVAL: | ||
305 | return "Invalid character inside JSON string"; | ||
306 | case JSMN_ERROR_PART: | ||
307 | return "The string is not a full JSON packet, more bytes expected"; | ||
308 | case JSMN_SUCCESS: | ||
309 | return "Success"; | ||
310 | default: | ||
311 | return "Unknown json error"; | ||
312 | } | ||
313 | } | ||
diff --git a/tools/perf/pmu-events/jsmn.h b/tools/perf/pmu-events/jsmn.h new file mode 100644 index 000000000000..d666b10cf25b --- /dev/null +++ b/tools/perf/pmu-events/jsmn.h | |||
@@ -0,0 +1,67 @@ | |||
1 | #ifndef __JSMN_H_ | ||
2 | #define __JSMN_H_ | ||
3 | |||
4 | /* | ||
5 | * JSON type identifier. Basic types are: | ||
6 | * o Object | ||
7 | * o Array | ||
8 | * o String | ||
9 | * o Other primitive: number, boolean (true/false) or null | ||
10 | */ | ||
11 | typedef enum { | ||
12 | JSMN_PRIMITIVE = 0, | ||
13 | JSMN_OBJECT = 1, | ||
14 | JSMN_ARRAY = 2, | ||
15 | JSMN_STRING = 3 | ||
16 | } jsmntype_t; | ||
17 | |||
18 | typedef enum { | ||
19 | /* Not enough tokens were provided */ | ||
20 | JSMN_ERROR_NOMEM = -1, | ||
21 | /* Invalid character inside JSON string */ | ||
22 | JSMN_ERROR_INVAL = -2, | ||
23 | /* The string is not a full JSON packet, more bytes expected */ | ||
24 | JSMN_ERROR_PART = -3, | ||
25 | /* Everything was fine */ | ||
26 | JSMN_SUCCESS = 0 | ||
27 | } jsmnerr_t; | ||
28 | |||
29 | /* | ||
30 | * JSON token description. | ||
31 | * @param type type (object, array, string etc.) | ||
32 | * @param start start position in JSON data string | ||
33 | * @param end end position in JSON data string | ||
34 | */ | ||
35 | typedef struct { | ||
36 | jsmntype_t type; | ||
37 | int start; | ||
38 | int end; | ||
39 | int size; | ||
40 | } jsmntok_t; | ||
41 | |||
42 | /* | ||
43 | * JSON parser. Contains an array of token blocks available. Also stores | ||
44 | * the string being parsed now and current position in that string | ||
45 | */ | ||
46 | typedef struct { | ||
47 | unsigned int pos; /* offset in the JSON string */ | ||
48 | int toknext; /* next token to allocate */ | ||
49 | int toksuper; /* superior token node, e.g parent object or array */ | ||
50 | } jsmn_parser; | ||
51 | |||
52 | /* | ||
53 | * Create JSON parser over an array of tokens | ||
54 | */ | ||
55 | void jsmn_init(jsmn_parser *parser); | ||
56 | |||
57 | /* | ||
58 | * Run JSON parser. It parses a JSON data string into and array of tokens, | ||
59 | * each describing a single JSON object. | ||
60 | */ | ||
61 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, | ||
62 | size_t len, | ||
63 | jsmntok_t *tokens, unsigned int num_tokens); | ||
64 | |||
65 | const char *jsmn_strerror(jsmnerr_t err); | ||
66 | |||
67 | #endif /* __JSMN_H_ */ | ||
diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c new file mode 100644 index 000000000000..f67bbb0aa36e --- /dev/null +++ b/tools/perf/pmu-events/json.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* Parse JSON files using the JSMN parser. */ | ||
2 | |||
3 | /* | ||
4 | * Copyright (c) 2014, Intel Corporation | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions are met: | ||
9 | * | ||
10 | * 1. Redistributions of source code must retain the above copyright notice, | ||
11 | * this list of conditions and the following disclaimer. | ||
12 | * | ||
13 | * 2. Redistributions in binary form must reproduce the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer in the | ||
15 | * documentation and/or other materials provided with the distribution. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
21 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
22 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
24 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
26 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
28 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <sys/mman.h> | ||
34 | #include <sys/stat.h> | ||
35 | #include <fcntl.h> | ||
36 | #include <stdio.h> | ||
37 | #include <errno.h> | ||
38 | #include <unistd.h> | ||
39 | #include "jsmn.h" | ||
40 | #include "json.h" | ||
41 | #include <linux/kernel.h> | ||
42 | |||
43 | |||
44 | static char *mapfile(const char *fn, size_t *size) | ||
45 | { | ||
46 | unsigned ps = sysconf(_SC_PAGESIZE); | ||
47 | struct stat st; | ||
48 | char *map = NULL; | ||
49 | int err; | ||
50 | int fd = open(fn, O_RDONLY); | ||
51 | |||
52 | if (fd < 0 && verbose && fn) { | ||
53 | pr_err("Error opening events file '%s': %s\n", fn, | ||
54 | strerror(errno)); | ||
55 | } | ||
56 | |||
57 | if (fd < 0) | ||
58 | return NULL; | ||
59 | err = fstat(fd, &st); | ||
60 | if (err < 0) | ||
61 | goto out; | ||
62 | *size = st.st_size; | ||
63 | map = mmap(NULL, | ||
64 | (st.st_size + ps - 1) & ~(ps - 1), | ||
65 | PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
66 | if (map == MAP_FAILED) | ||
67 | map = NULL; | ||
68 | out: | ||
69 | close(fd); | ||
70 | return map; | ||
71 | } | ||
72 | |||
73 | static void unmapfile(char *map, size_t size) | ||
74 | { | ||
75 | unsigned ps = sysconf(_SC_PAGESIZE); | ||
76 | munmap(map, roundup(size, ps)); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Parse json file using jsmn. Return array of tokens, | ||
81 | * and mapped file. Caller needs to free array. | ||
82 | */ | ||
83 | jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len) | ||
84 | { | ||
85 | jsmn_parser parser; | ||
86 | jsmntok_t *tokens; | ||
87 | jsmnerr_t res; | ||
88 | unsigned sz; | ||
89 | |||
90 | *map = mapfile(fn, size); | ||
91 | if (!*map) | ||
92 | return NULL; | ||
93 | /* Heuristic */ | ||
94 | sz = *size * 16; | ||
95 | tokens = malloc(sz); | ||
96 | if (!tokens) | ||
97 | goto error; | ||
98 | jsmn_init(&parser); | ||
99 | res = jsmn_parse(&parser, *map, *size, tokens, | ||
100 | sz / sizeof(jsmntok_t)); | ||
101 | if (res != JSMN_SUCCESS) { | ||
102 | pr_err("%s: json error %s\n", fn, jsmn_strerror(res)); | ||
103 | goto error_free; | ||
104 | } | ||
105 | if (len) | ||
106 | *len = parser.toknext; | ||
107 | return tokens; | ||
108 | error_free: | ||
109 | free(tokens); | ||
110 | error: | ||
111 | unmapfile(*map, *size); | ||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | void free_json(char *map, size_t size, jsmntok_t *tokens) | ||
116 | { | ||
117 | free(tokens); | ||
118 | unmapfile(map, size); | ||
119 | } | ||
120 | |||
121 | static int countchar(char *map, char c, int end) | ||
122 | { | ||
123 | int i; | ||
124 | int count = 0; | ||
125 | for (i = 0; i < end; i++) | ||
126 | if (map[i] == c) | ||
127 | count++; | ||
128 | return count; | ||
129 | } | ||
130 | |||
131 | /* Return line number of a jsmn token */ | ||
132 | int json_line(char *map, jsmntok_t *t) | ||
133 | { | ||
134 | return countchar(map, '\n', t->start) + 1; | ||
135 | } | ||
136 | |||
137 | static const char * const jsmn_types[] = { | ||
138 | [JSMN_PRIMITIVE] = "primitive", | ||
139 | [JSMN_ARRAY] = "array", | ||
140 | [JSMN_OBJECT] = "object", | ||
141 | [JSMN_STRING] = "string" | ||
142 | }; | ||
143 | |||
144 | #define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?") | ||
145 | |||
146 | /* Return type name of a jsmn token */ | ||
147 | const char *json_name(jsmntok_t *t) | ||
148 | { | ||
149 | return LOOKUP(jsmn_types, t->type); | ||
150 | } | ||
151 | |||
152 | int json_len(jsmntok_t *t) | ||
153 | { | ||
154 | return t->end - t->start; | ||
155 | } | ||
156 | |||
157 | /* Is string t equal to s? */ | ||
158 | int json_streq(char *map, jsmntok_t *t, const char *s) | ||
159 | { | ||
160 | unsigned len = json_len(t); | ||
161 | return len == strlen(s) && !strncasecmp(map + t->start, s, len); | ||
162 | } | ||
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h new file mode 100644 index 000000000000..278ebd32cfb6 --- /dev/null +++ b/tools/perf/pmu-events/json.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef JSON_H | ||
2 | #define JSON_H 1 | ||
3 | |||
4 | #include "jsmn.h" | ||
5 | |||
6 | jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len); | ||
7 | void free_json(char *map, size_t size, jsmntok_t *tokens); | ||
8 | int json_line(char *map, jsmntok_t *t); | ||
9 | const char *json_name(jsmntok_t *t); | ||
10 | int json_streq(char *map, jsmntok_t *t, const char *s); | ||
11 | int json_len(jsmntok_t *t); | ||
12 | |||
13 | extern int verbose; | ||
14 | |||
15 | #include <stdbool.h> | ||
16 | |||
17 | extern int eprintf(int level, int var, const char *fmt, ...); | ||
18 | #define pr_fmt(fmt) fmt | ||
19 | |||
20 | #define pr_err(fmt, ...) \ | ||
21 | eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
22 | |||
23 | #define pr_info(fmt, ...) \ | ||
24 | eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
25 | |||
26 | #define pr_debug(fmt, ...) \ | ||
27 | eprintf(2, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
28 | |||
29 | #ifndef roundup | ||
30 | #define roundup(x, y) ( \ | ||
31 | { \ | ||
32 | const typeof(y) __y = y; \ | ||
33 | (((x) + (__y - 1)) / __y) * __y; \ | ||
34 | } \ | ||
35 | ) | ||
36 | #endif | ||
37 | |||
38 | #endif | ||
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h new file mode 100644 index 000000000000..2eaef595d8a0 --- /dev/null +++ b/tools/perf/pmu-events/pmu-events.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef PMU_EVENTS_H | ||
2 | #define PMU_EVENTS_H | ||
3 | |||
4 | /* | ||
5 | * Describe each PMU event. Each CPU has a table of PMU events. | ||
6 | */ | ||
7 | struct pmu_event { | ||
8 | const char *name; | ||
9 | const char *event; | ||
10 | const char *desc; | ||
11 | const char *topic; | ||
12 | const char *long_desc; | ||
13 | }; | ||
14 | |||
15 | /* | ||
16 | * | ||
17 | * Map a CPU to its table of PMU events. The CPU is identified by the | ||
18 | * cpuid field, which is an arch-specific identifier for the CPU. | ||
19 | * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile | ||
20 | * must match the get_cpustr() in tools/perf/arch/xxx/util/header.c) | ||
21 | * | ||
22 | * The cpuid can contain any character other than the comma. | ||
23 | */ | ||
24 | struct pmu_events_map { | ||
25 | const char *cpuid; | ||
26 | const char *version; | ||
27 | const char *type; /* core, uncore etc */ | ||
28 | struct pmu_event *table; | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * Global table mapping each known CPU for the architecture to its | ||
33 | * table of PMU events. | ||
34 | */ | ||
35 | extern struct pmu_events_map pmu_events_map[]; | ||
36 | |||
37 | #endif | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ea34c5a32c11..d92e02006fb8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -384,15 +384,14 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist) | |||
384 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, | 384 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
385 | struct perf_evsel *evsel, int cpu) | 385 | struct perf_evsel *evsel, int cpu) |
386 | { | 386 | { |
387 | int thread, err; | 387 | int thread; |
388 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | 388 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); |
389 | 389 | ||
390 | if (!evsel->fd) | 390 | if (!evsel->fd) |
391 | return -EINVAL; | 391 | return -EINVAL; |
392 | 392 | ||
393 | for (thread = 0; thread < nr_threads; thread++) { | 393 | for (thread = 0; thread < nr_threads; thread++) { |
394 | err = ioctl(FD(evsel, cpu, thread), | 394 | int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); |
395 | PERF_EVENT_IOC_ENABLE, 0); | ||
396 | if (err) | 395 | if (err) |
397 | return err; | 396 | return err; |
398 | } | 397 | } |
@@ -403,14 +402,14 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | |||
403 | struct perf_evsel *evsel, | 402 | struct perf_evsel *evsel, |
404 | int thread) | 403 | int thread) |
405 | { | 404 | { |
406 | int cpu, err; | 405 | int cpu; |
407 | int nr_cpus = cpu_map__nr(evlist->cpus); | 406 | int nr_cpus = cpu_map__nr(evlist->cpus); |
408 | 407 | ||
409 | if (!evsel->fd) | 408 | if (!evsel->fd) |
410 | return -EINVAL; | 409 | return -EINVAL; |
411 | 410 | ||
412 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 411 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
413 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | 412 | int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); |
414 | if (err) | 413 | if (err) |
415 | return err; | 414 | return err; |
416 | } | 415 | } |
@@ -1606,10 +1605,9 @@ void perf_evlist__close(struct perf_evlist *evlist) | |||
1606 | struct perf_evsel *evsel; | 1605 | struct perf_evsel *evsel; |
1607 | int ncpus = cpu_map__nr(evlist->cpus); | 1606 | int ncpus = cpu_map__nr(evlist->cpus); |
1608 | int nthreads = thread_map__nr(evlist->threads); | 1607 | int nthreads = thread_map__nr(evlist->threads); |
1609 | int n; | ||
1610 | 1608 | ||
1611 | evlist__for_each_entry_reverse(evlist, evsel) { | 1609 | evlist__for_each_entry_reverse(evlist, evsel) { |
1612 | n = evsel->cpus ? evsel->cpus->nr : ncpus; | 1610 | int n = evsel->cpus ? evsel->cpus->nr : ncpus; |
1613 | perf_evsel__close(evsel, n, nthreads); | 1611 | perf_evsel__close(evsel, n, nthreads); |
1614 | } | 1612 | } |
1615 | } | 1613 | } |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 380e84c3af3d..8bc271141d9d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -985,14 +985,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
985 | 985 | ||
986 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 986 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
987 | { | 987 | { |
988 | int cpu, thread; | ||
989 | |||
990 | if (evsel->system_wide) | 988 | if (evsel->system_wide) |
991 | nthreads = 1; | 989 | nthreads = 1; |
992 | 990 | ||
993 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 991 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
994 | 992 | ||
995 | if (evsel->fd) { | 993 | if (evsel->fd) { |
994 | int cpu, thread; | ||
996 | for (cpu = 0; cpu < ncpus; cpu++) { | 995 | for (cpu = 0; cpu < ncpus; cpu++) { |
997 | for (thread = 0; thread < nthreads; thread++) { | 996 | for (thread = 0; thread < nthreads; thread++) { |
998 | FD(evsel, cpu, thread) = -1; | 997 | FD(evsel, cpu, thread) = -1; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d306ca118449..d30109b421ee 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -151,4 +151,5 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); | |||
151 | */ | 151 | */ |
152 | int get_cpuid(char *buffer, size_t sz); | 152 | int get_cpuid(char *buffer, size_t sz); |
153 | 153 | ||
154 | char *get_cpuid_str(void); | ||
154 | #endif /* __PERF_HEADER_H */ | 155 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 7591a0c37473..16c06d3ae577 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
@@ -90,6 +90,7 @@ struct intel_pt_decoder { | |||
90 | bool pge; | 90 | bool pge; |
91 | bool have_tma; | 91 | bool have_tma; |
92 | bool have_cyc; | 92 | bool have_cyc; |
93 | bool fixup_last_mtc; | ||
93 | uint64_t pos; | 94 | uint64_t pos; |
94 | uint64_t last_ip; | 95 | uint64_t last_ip; |
95 | uint64_t ip; | 96 | uint64_t ip; |
@@ -586,10 +587,31 @@ struct intel_pt_calc_cyc_to_tsc_info { | |||
586 | uint64_t tsc_timestamp; | 587 | uint64_t tsc_timestamp; |
587 | uint64_t timestamp; | 588 | uint64_t timestamp; |
588 | bool have_tma; | 589 | bool have_tma; |
590 | bool fixup_last_mtc; | ||
589 | bool from_mtc; | 591 | bool from_mtc; |
590 | double cbr_cyc_to_tsc; | 592 | double cbr_cyc_to_tsc; |
591 | }; | 593 | }; |
592 | 594 | ||
595 | /* | ||
596 | * MTC provides a 8-bit slice of CTC but the TMA packet only provides the lower | ||
597 | * 16 bits of CTC. If mtc_shift > 8 then some of the MTC bits are not in the CTC | ||
598 | * provided by the TMA packet. Fix-up the last_mtc calculated from the TMA | ||
599 | * packet by copying the missing bits from the current MTC assuming the least | ||
600 | * difference between the two, and that the current MTC comes after last_mtc. | ||
601 | */ | ||
602 | static void intel_pt_fixup_last_mtc(uint32_t mtc, int mtc_shift, | ||
603 | uint32_t *last_mtc) | ||
604 | { | ||
605 | uint32_t first_missing_bit = 1U << (16 - mtc_shift); | ||
606 | uint32_t mask = ~(first_missing_bit - 1); | ||
607 | |||
608 | *last_mtc |= mtc & mask; | ||
609 | if (*last_mtc >= mtc) { | ||
610 | *last_mtc -= first_missing_bit; | ||
611 | *last_mtc &= 0xff; | ||
612 | } | ||
613 | } | ||
614 | |||
593 | static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | 615 | static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) |
594 | { | 616 | { |
595 | struct intel_pt_decoder *decoder = pkt_info->decoder; | 617 | struct intel_pt_decoder *decoder = pkt_info->decoder; |
@@ -619,6 +641,11 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | |||
619 | return 0; | 641 | return 0; |
620 | 642 | ||
621 | mtc = pkt_info->packet.payload; | 643 | mtc = pkt_info->packet.payload; |
644 | if (decoder->mtc_shift > 8 && data->fixup_last_mtc) { | ||
645 | data->fixup_last_mtc = false; | ||
646 | intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift, | ||
647 | &data->last_mtc); | ||
648 | } | ||
622 | if (mtc > data->last_mtc) | 649 | if (mtc > data->last_mtc) |
623 | mtc_delta = mtc - data->last_mtc; | 650 | mtc_delta = mtc - data->last_mtc; |
624 | else | 651 | else |
@@ -687,6 +714,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | |||
687 | 714 | ||
688 | data->ctc_delta = 0; | 715 | data->ctc_delta = 0; |
689 | data->have_tma = true; | 716 | data->have_tma = true; |
717 | data->fixup_last_mtc = true; | ||
690 | 718 | ||
691 | return 0; | 719 | return 0; |
692 | 720 | ||
@@ -753,6 +781,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder, | |||
753 | .tsc_timestamp = decoder->tsc_timestamp, | 781 | .tsc_timestamp = decoder->tsc_timestamp, |
754 | .timestamp = decoder->timestamp, | 782 | .timestamp = decoder->timestamp, |
755 | .have_tma = decoder->have_tma, | 783 | .have_tma = decoder->have_tma, |
784 | .fixup_last_mtc = decoder->fixup_last_mtc, | ||
756 | .from_mtc = from_mtc, | 785 | .from_mtc = from_mtc, |
757 | .cbr_cyc_to_tsc = 0, | 786 | .cbr_cyc_to_tsc = 0, |
758 | }; | 787 | }; |
@@ -1271,6 +1300,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder) | |||
1271 | } | 1300 | } |
1272 | decoder->ctc_delta = 0; | 1301 | decoder->ctc_delta = 0; |
1273 | decoder->have_tma = true; | 1302 | decoder->have_tma = true; |
1303 | decoder->fixup_last_mtc = true; | ||
1274 | intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n", | 1304 | intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n", |
1275 | decoder->ctc_timestamp, decoder->last_mtc, ctc_rem); | 1305 | decoder->ctc_timestamp, decoder->last_mtc, ctc_rem); |
1276 | } | 1306 | } |
@@ -1285,6 +1315,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder) | |||
1285 | 1315 | ||
1286 | mtc = decoder->packet.payload; | 1316 | mtc = decoder->packet.payload; |
1287 | 1317 | ||
1318 | if (decoder->mtc_shift > 8 && decoder->fixup_last_mtc) { | ||
1319 | decoder->fixup_last_mtc = false; | ||
1320 | intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift, | ||
1321 | &decoder->last_mtc); | ||
1322 | } | ||
1323 | |||
1288 | if (mtc > decoder->last_mtc) | 1324 | if (mtc > decoder->last_mtc) |
1289 | mtc_delta = mtc - decoder->last_mtc; | 1325 | mtc_delta = mtc - decoder->last_mtc; |
1290 | else | 1326 | else |
@@ -1353,6 +1389,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder) | |||
1353 | timestamp, decoder->timestamp); | 1389 | timestamp, decoder->timestamp); |
1354 | else | 1390 | else |
1355 | decoder->timestamp = timestamp; | 1391 | decoder->timestamp = timestamp; |
1392 | |||
1393 | decoder->timestamp_insn_cnt = 0; | ||
1356 | } | 1394 | } |
1357 | 1395 | ||
1358 | /* Walk PSB+ packets when already in sync. */ | 1396 | /* Walk PSB+ packets when already in sync. */ |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 18e4519abef2..df85b9efd80f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1745,9 +1745,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1745 | int max_stack) | 1745 | int max_stack) |
1746 | { | 1746 | { |
1747 | struct ip_callchain *chain = sample->callchain; | 1747 | struct ip_callchain *chain = sample->callchain; |
1748 | int chain_nr = min(max_stack, (int)chain->nr); | 1748 | int chain_nr = min(max_stack, (int)chain->nr), i; |
1749 | u8 cpumode = PERF_RECORD_MISC_USER; | 1749 | u8 cpumode = PERF_RECORD_MISC_USER; |
1750 | int i, j, err; | ||
1751 | u64 ip; | 1750 | u64 ip; |
1752 | 1751 | ||
1753 | for (i = 0; i < chain_nr; i++) { | 1752 | for (i = 0; i < chain_nr; i++) { |
@@ -1758,7 +1757,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1758 | /* LBR only affects the user callchain */ | 1757 | /* LBR only affects the user callchain */ |
1759 | if (i != chain_nr) { | 1758 | if (i != chain_nr) { |
1760 | struct branch_stack *lbr_stack = sample->branch_stack; | 1759 | struct branch_stack *lbr_stack = sample->branch_stack; |
1761 | int lbr_nr = lbr_stack->nr; | 1760 | int lbr_nr = lbr_stack->nr, j; |
1762 | /* | 1761 | /* |
1763 | * LBR callstack can only get user call chain. | 1762 | * LBR callstack can only get user call chain. |
1764 | * The mix_chain_nr is kernel call chain | 1763 | * The mix_chain_nr is kernel call chain |
@@ -1772,6 +1771,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1772 | int mix_chain_nr = i + 1 + lbr_nr + 1; | 1771 | int mix_chain_nr = i + 1 + lbr_nr + 1; |
1773 | 1772 | ||
1774 | for (j = 0; j < mix_chain_nr; j++) { | 1773 | for (j = 0; j < mix_chain_nr; j++) { |
1774 | int err; | ||
1775 | if (callchain_param.order == ORDER_CALLEE) { | 1775 | if (callchain_param.order == ORDER_CALLEE) { |
1776 | if (j < i + 1) | 1776 | if (j < i + 1) |
1777 | ip = chain->ips[j]; | 1777 | ip = chain->ips[j]; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 33546c3ac1fe..4e778eae1510 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -924,6 +924,7 @@ config_term_avail(int term_type, struct parse_events_error *err) | |||
924 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | 924 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: |
925 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | 925 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: |
926 | case PARSE_EVENTS__TERM_TYPE_NAME: | 926 | case PARSE_EVENTS__TERM_TYPE_NAME: |
927 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
927 | return true; | 928 | return true; |
928 | default: | 929 | default: |
929 | if (!err) | 930 | if (!err) |
@@ -1458,7 +1459,7 @@ comp_pmu(const void *p1, const void *p2) | |||
1458 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; | 1459 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; |
1459 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; | 1460 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; |
1460 | 1461 | ||
1461 | return strcmp(pmu1->symbol, pmu2->symbol); | 1462 | return strcasecmp(pmu1->symbol, pmu2->symbol); |
1462 | } | 1463 | } |
1463 | 1464 | ||
1464 | static void perf_pmu__parse_cleanup(void) | 1465 | static void perf_pmu__parse_cleanup(void) |
@@ -2263,7 +2264,8 @@ out_enomem: | |||
2263 | /* | 2264 | /* |
2264 | * Print the help text for the event symbols: | 2265 | * Print the help text for the event symbols: |
2265 | */ | 2266 | */ |
2266 | void print_events(const char *event_glob, bool name_only) | 2267 | void print_events(const char *event_glob, bool name_only, bool quiet_flag, |
2268 | bool long_desc) | ||
2267 | { | 2269 | { |
2268 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, | 2270 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, |
2269 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); | 2271 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); |
@@ -2273,7 +2275,7 @@ void print_events(const char *event_glob, bool name_only) | |||
2273 | 2275 | ||
2274 | print_hwcache_events(event_glob, name_only); | 2276 | print_hwcache_events(event_glob, name_only); |
2275 | 2277 | ||
2276 | print_pmu_events(event_glob, name_only); | 2278 | print_pmu_events(event_glob, name_only, quiet_flag, long_desc); |
2277 | 2279 | ||
2278 | if (event_glob != NULL) | 2280 | if (event_glob != NULL) |
2279 | return; | 2281 | return; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 8d09a976fca8..da246a3ddb69 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -172,7 +172,8 @@ void parse_events_update_lists(struct list_head *list_event, | |||
172 | void parse_events_evlist_error(struct parse_events_evlist *data, | 172 | void parse_events_evlist_error(struct parse_events_evlist *data, |
173 | int idx, const char *str); | 173 | int idx, const char *str); |
174 | 174 | ||
175 | void print_events(const char *event_glob, bool name_only); | 175 | void print_events(const char *event_glob, bool name_only, bool quiet, |
176 | bool long_desc); | ||
176 | 177 | ||
177 | struct event_symbol { | 178 | struct event_symbol { |
178 | const char *symbol; | 179 | const char *symbol; |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 2babcdf62839..b1474dcadfa2 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -12,6 +12,9 @@ | |||
12 | #include "pmu.h" | 12 | #include "pmu.h" |
13 | #include "parse-events.h" | 13 | #include "parse-events.h" |
14 | #include "cpumap.h" | 14 | #include "cpumap.h" |
15 | #include "header.h" | ||
16 | #include "pmu-events/pmu-events.h" | ||
17 | #include "cache.h" | ||
15 | 18 | ||
16 | struct perf_pmu_format { | 19 | struct perf_pmu_format { |
17 | char *name; | 20 | char *name; |
@@ -220,7 +223,8 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, | |||
220 | } | 223 | } |
221 | 224 | ||
222 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | 225 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, |
223 | char *desc __maybe_unused, char *val) | 226 | char *desc, char *val, char *long_desc, |
227 | char *topic) | ||
224 | { | 228 | { |
225 | struct perf_pmu_alias *alias; | 229 | struct perf_pmu_alias *alias; |
226 | int ret; | 230 | int ret; |
@@ -253,6 +257,11 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | |||
253 | perf_pmu__parse_snapshot(alias, dir, name); | 257 | perf_pmu__parse_snapshot(alias, dir, name); |
254 | } | 258 | } |
255 | 259 | ||
260 | alias->desc = desc ? strdup(desc) : NULL; | ||
261 | alias->long_desc = long_desc ? strdup(long_desc) : | ||
262 | desc ? strdup(desc) : NULL; | ||
263 | alias->topic = topic ? strdup(topic) : NULL; | ||
264 | |||
256 | list_add_tail(&alias->list, list); | 265 | list_add_tail(&alias->list, list); |
257 | 266 | ||
258 | return 0; | 267 | return 0; |
@@ -269,7 +278,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
269 | 278 | ||
270 | buf[ret] = 0; | 279 | buf[ret] = 0; |
271 | 280 | ||
272 | return __perf_pmu__new_alias(list, dir, name, NULL, buf); | 281 | return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL); |
273 | } | 282 | } |
274 | 283 | ||
275 | static inline bool pmu_alias_info_file(char *name) | 284 | static inline bool pmu_alias_info_file(char *name) |
@@ -473,6 +482,68 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
473 | return cpus; | 482 | return cpus; |
474 | } | 483 | } |
475 | 484 | ||
485 | /* | ||
486 | * Return the CPU id as a raw string. | ||
487 | * | ||
488 | * Each architecture should provide a more precise id string that | ||
489 | * can be use to match the architecture's "mapfile". | ||
490 | */ | ||
491 | char * __weak get_cpuid_str(void) | ||
492 | { | ||
493 | return NULL; | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * From the pmu_events_map, find the table of PMU events that corresponds | ||
498 | * to the current running CPU. Then, add all PMU events from that table | ||
499 | * as aliases. | ||
500 | */ | ||
501 | static void pmu_add_cpu_aliases(struct list_head *head) | ||
502 | { | ||
503 | int i; | ||
504 | struct pmu_events_map *map; | ||
505 | struct pmu_event *pe; | ||
506 | char *cpuid; | ||
507 | |||
508 | cpuid = getenv("PERF_CPUID"); | ||
509 | if (cpuid) | ||
510 | cpuid = strdup(cpuid); | ||
511 | if (!cpuid) | ||
512 | cpuid = get_cpuid_str(); | ||
513 | if (!cpuid) | ||
514 | return; | ||
515 | |||
516 | pr_debug("Using CPUID %s\n", cpuid); | ||
517 | |||
518 | i = 0; | ||
519 | while (1) { | ||
520 | map = &pmu_events_map[i++]; | ||
521 | if (!map->table) | ||
522 | goto out; | ||
523 | |||
524 | if (!strcmp(map->cpuid, cpuid)) | ||
525 | break; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * Found a matching PMU events table. Create aliases | ||
530 | */ | ||
531 | i = 0; | ||
532 | while (1) { | ||
533 | pe = &map->table[i++]; | ||
534 | if (!pe->name) | ||
535 | break; | ||
536 | |||
537 | /* need type casts to override 'const' */ | ||
538 | __perf_pmu__new_alias(head, NULL, (char *)pe->name, | ||
539 | (char *)pe->desc, (char *)pe->event, | ||
540 | (char *)pe->long_desc, (char *)pe->topic); | ||
541 | } | ||
542 | |||
543 | out: | ||
544 | free(cpuid); | ||
545 | } | ||
546 | |||
476 | struct perf_event_attr * __weak | 547 | struct perf_event_attr * __weak |
477 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | 548 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) |
478 | { | 549 | { |
@@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
497 | if (pmu_aliases(name, &aliases)) | 568 | if (pmu_aliases(name, &aliases)) |
498 | return NULL; | 569 | return NULL; |
499 | 570 | ||
571 | if (!strcmp(name, "cpu")) | ||
572 | pmu_add_cpu_aliases(&aliases); | ||
573 | |||
500 | if (pmu_type(name, &type)) | 574 | if (pmu_type(name, &type)) |
501 | return NULL; | 575 | return NULL; |
502 | 576 | ||
@@ -983,21 +1057,63 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, | |||
983 | return buf; | 1057 | return buf; |
984 | } | 1058 | } |
985 | 1059 | ||
986 | static int cmp_string(const void *a, const void *b) | 1060 | struct sevent { |
1061 | char *name; | ||
1062 | char *desc; | ||
1063 | char *topic; | ||
1064 | }; | ||
1065 | |||
1066 | static int cmp_sevent(const void *a, const void *b) | ||
987 | { | 1067 | { |
988 | const char * const *as = a; | 1068 | const struct sevent *as = a; |
989 | const char * const *bs = b; | 1069 | const struct sevent *bs = b; |
990 | return strcmp(*as, *bs); | 1070 | |
1071 | /* Put extra events last */ | ||
1072 | if (!!as->desc != !!bs->desc) | ||
1073 | return !!as->desc - !!bs->desc; | ||
1074 | if (as->topic && bs->topic) { | ||
1075 | int n = strcmp(as->topic, bs->topic); | ||
1076 | |||
1077 | if (n) | ||
1078 | return n; | ||
1079 | } | ||
1080 | return strcmp(as->name, bs->name); | ||
991 | } | 1081 | } |
992 | 1082 | ||
993 | void print_pmu_events(const char *event_glob, bool name_only) | 1083 | static void wordwrap(char *s, int start, int max, int corr) |
1084 | { | ||
1085 | int column = start; | ||
1086 | int n; | ||
1087 | |||
1088 | while (*s) { | ||
1089 | int wlen = strcspn(s, " \t"); | ||
1090 | |||
1091 | if (column + wlen >= max && column > start) { | ||
1092 | printf("\n%*s", start, ""); | ||
1093 | column = start + corr; | ||
1094 | } | ||
1095 | n = printf("%s%.*s", column > start ? " " : "", wlen, s); | ||
1096 | if (n <= 0) | ||
1097 | break; | ||
1098 | s += wlen; | ||
1099 | column += n; | ||
1100 | while (isspace(*s)) | ||
1101 | s++; | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, | ||
1106 | bool long_desc) | ||
994 | { | 1107 | { |
995 | struct perf_pmu *pmu; | 1108 | struct perf_pmu *pmu; |
996 | struct perf_pmu_alias *alias; | 1109 | struct perf_pmu_alias *alias; |
997 | char buf[1024]; | 1110 | char buf[1024]; |
998 | int printed = 0; | 1111 | int printed = 0; |
999 | int len, j; | 1112 | int len, j; |
1000 | char **aliases; | 1113 | struct sevent *aliases; |
1114 | int numdesc = 0; | ||
1115 | int columns = pager_get_columns(); | ||
1116 | char *topic = NULL; | ||
1001 | 1117 | ||
1002 | pmu = NULL; | 1118 | pmu = NULL; |
1003 | len = 0; | 1119 | len = 0; |
@@ -1007,14 +1123,15 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1007 | if (pmu->selectable) | 1123 | if (pmu->selectable) |
1008 | len++; | 1124 | len++; |
1009 | } | 1125 | } |
1010 | aliases = zalloc(sizeof(char *) * len); | 1126 | aliases = zalloc(sizeof(struct sevent) * len); |
1011 | if (!aliases) | 1127 | if (!aliases) |
1012 | goto out_enomem; | 1128 | goto out_enomem; |
1013 | pmu = NULL; | 1129 | pmu = NULL; |
1014 | j = 0; | 1130 | j = 0; |
1015 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | 1131 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
1016 | list_for_each_entry(alias, &pmu->aliases, list) { | 1132 | list_for_each_entry(alias, &pmu->aliases, list) { |
1017 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | 1133 | char *name = alias->desc ? alias->name : |
1134 | format_alias(buf, sizeof(buf), pmu, alias); | ||
1018 | bool is_cpu = !strcmp(pmu->name, "cpu"); | 1135 | bool is_cpu = !strcmp(pmu->name, "cpu"); |
1019 | 1136 | ||
1020 | if (event_glob != NULL && | 1137 | if (event_glob != NULL && |
@@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1023 | event_glob)))) | 1140 | event_glob)))) |
1024 | continue; | 1141 | continue; |
1025 | 1142 | ||
1026 | if (is_cpu && !name_only) | 1143 | if (is_cpu && !name_only && !alias->desc) |
1027 | name = format_alias_or(buf, sizeof(buf), pmu, alias); | 1144 | name = format_alias_or(buf, sizeof(buf), pmu, alias); |
1028 | 1145 | ||
1029 | aliases[j] = strdup(name); | 1146 | aliases[j].name = name; |
1030 | if (aliases[j] == NULL) | 1147 | if (is_cpu && !name_only && !alias->desc) |
1148 | aliases[j].name = format_alias_or(buf, | ||
1149 | sizeof(buf), | ||
1150 | pmu, alias); | ||
1151 | aliases[j].name = strdup(aliases[j].name); | ||
1152 | if (!aliases[j].name) | ||
1031 | goto out_enomem; | 1153 | goto out_enomem; |
1154 | |||
1155 | aliases[j].desc = long_desc ? alias->long_desc : | ||
1156 | alias->desc; | ||
1157 | aliases[j].topic = alias->topic; | ||
1032 | j++; | 1158 | j++; |
1033 | } | 1159 | } |
1034 | if (pmu->selectable && | 1160 | if (pmu->selectable && |
@@ -1036,25 +1162,39 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1036 | char *s; | 1162 | char *s; |
1037 | if (asprintf(&s, "%s//", pmu->name) < 0) | 1163 | if (asprintf(&s, "%s//", pmu->name) < 0) |
1038 | goto out_enomem; | 1164 | goto out_enomem; |
1039 | aliases[j] = s; | 1165 | aliases[j].name = s; |
1040 | j++; | 1166 | j++; |
1041 | } | 1167 | } |
1042 | } | 1168 | } |
1043 | len = j; | 1169 | len = j; |
1044 | qsort(aliases, len, sizeof(char *), cmp_string); | 1170 | qsort(aliases, len, sizeof(struct sevent), cmp_sevent); |
1045 | for (j = 0; j < len; j++) { | 1171 | for (j = 0; j < len; j++) { |
1046 | if (name_only) { | 1172 | if (name_only) { |
1047 | printf("%s ", aliases[j]); | 1173 | printf("%s ", aliases[j].name); |
1048 | continue; | 1174 | continue; |
1049 | } | 1175 | } |
1050 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | 1176 | if (aliases[j].desc && !quiet_flag) { |
1177 | if (numdesc++ == 0) | ||
1178 | printf("\n"); | ||
1179 | if (aliases[j].topic && (!topic || | ||
1180 | strcmp(topic, aliases[j].topic))) { | ||
1181 | printf("%s%s:\n", topic ? "\n" : "", | ||
1182 | aliases[j].topic); | ||
1183 | topic = aliases[j].topic; | ||
1184 | } | ||
1185 | printf(" %-50s\n", aliases[j].name); | ||
1186 | printf("%*s", 8, "["); | ||
1187 | wordwrap(aliases[j].desc, 8, columns, 0); | ||
1188 | printf("]\n"); | ||
1189 | } else | ||
1190 | printf(" %-50s [Kernel PMU event]\n", aliases[j].name); | ||
1051 | printed++; | 1191 | printed++; |
1052 | } | 1192 | } |
1053 | if (printed && pager_in_use()) | 1193 | if (printed && pager_in_use()) |
1054 | printf("\n"); | 1194 | printf("\n"); |
1055 | out_free: | 1195 | out_free: |
1056 | for (j = 0; j < len; j++) | 1196 | for (j = 0; j < len; j++) |
1057 | zfree(&aliases[j]); | 1197 | zfree(&aliases[j].name); |
1058 | zfree(&aliases); | 1198 | zfree(&aliases); |
1059 | return; | 1199 | return; |
1060 | 1200 | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 743422ad900b..25712034c815 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -40,6 +40,9 @@ struct perf_pmu_info { | |||
40 | 40 | ||
41 | struct perf_pmu_alias { | 41 | struct perf_pmu_alias { |
42 | char *name; | 42 | char *name; |
43 | char *desc; | ||
44 | char *long_desc; | ||
45 | char *topic; | ||
43 | struct list_head terms; /* HEAD struct parse_events_term -> list */ | 46 | struct list_head terms; /* HEAD struct parse_events_term -> list */ |
44 | struct list_head list; /* ELEM */ | 47 | struct list_head list; /* ELEM */ |
45 | char unit[UNIT_MAX_LEN+1]; | 48 | char unit[UNIT_MAX_LEN+1]; |
@@ -71,7 +74,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); | |||
71 | 74 | ||
72 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 75 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
73 | 76 | ||
74 | void print_pmu_events(const char *event_glob, bool name_only); | 77 | void print_pmu_events(const char *event_glob, bool name_only, bool quiet, |
78 | bool long_desc); | ||
75 | bool pmu_have_event(const char *pname, const char *name); | 79 | bool pmu_have_event(const char *pname, const char *name); |
76 | 80 | ||
77 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | 81 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index fcfbef07b92d..d281ae2b54e8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -213,7 +213,7 @@ static int convert_exec_to_group(const char *exec, char **result) | |||
213 | goto out; | 213 | goto out; |
214 | } | 214 | } |
215 | 215 | ||
216 | for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) { | 216 | for (ptr2 = ptr1; *ptr2 != '\0'; ptr2++) { |
217 | if (!isalnum(*ptr2) && *ptr2 != '_') { | 217 | if (!isalnum(*ptr2) && *ptr2 != '_') { |
218 | *ptr2 = '\0'; | 218 | *ptr2 = '\0'; |
219 | break; | 219 | break; |
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index b268a6648a5d..318424ea561d 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
@@ -66,9 +66,8 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) { | |||
66 | int strbuf_grow(struct strbuf *buf, size_t); | 66 | int strbuf_grow(struct strbuf *buf, size_t); |
67 | 67 | ||
68 | static inline int strbuf_setlen(struct strbuf *sb, size_t len) { | 68 | static inline int strbuf_setlen(struct strbuf *sb, size_t len) { |
69 | int ret; | ||
70 | if (!sb->alloc) { | 69 | if (!sb->alloc) { |
71 | ret = strbuf_grow(sb, 0); | 70 | int ret = strbuf_grow(sb, 0); |
72 | if (ret) | 71 | if (ret) |
73 | return ret; | 72 | return ret; |
74 | } | 73 | } |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 8b10a55410a2..f5af87f66663 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -14,13 +14,12 @@ | |||
14 | 14 | ||
15 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | 15 | int thread__init_map_groups(struct thread *thread, struct machine *machine) |
16 | { | 16 | { |
17 | struct thread *leader; | ||
18 | pid_t pid = thread->pid_; | 17 | pid_t pid = thread->pid_; |
19 | 18 | ||
20 | if (pid == thread->tid || pid == -1) { | 19 | if (pid == thread->tid || pid == -1) { |
21 | thread->mg = map_groups__new(machine); | 20 | thread->mg = map_groups__new(machine); |
22 | } else { | 21 | } else { |
23 | leader = __machine__findnew_thread(machine, pid, pid); | 22 | struct thread *leader = __machine__findnew_thread(machine, pid, pid); |
24 | if (leader) { | 23 | if (leader) { |
25 | thread->mg = map_groups__get(leader->mg); | 24 | thread->mg = map_groups__get(leader->mg); |
26 | thread__put(leader); | 25 | thread__put(leader); |
@@ -130,11 +129,10 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, | |||
130 | bool exec) | 129 | bool exec) |
131 | { | 130 | { |
132 | struct comm *new, *curr = thread__comm(thread); | 131 | struct comm *new, *curr = thread__comm(thread); |
133 | int err; | ||
134 | 132 | ||
135 | /* Override the default :tid entry */ | 133 | /* Override the default :tid entry */ |
136 | if (!thread->comm_set) { | 134 | if (!thread->comm_set) { |
137 | err = comm__override(curr, str, timestamp, exec); | 135 | int err = comm__override(curr, str, timestamp, exec); |
138 | if (err) | 136 | if (err) |
139 | return err; | 137 | return err; |
140 | } else { | 138 | } else { |
@@ -270,10 +268,9 @@ static int thread__clone_map_groups(struct thread *thread, | |||
270 | 268 | ||
271 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | 269 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
272 | { | 270 | { |
273 | int err; | ||
274 | |||
275 | if (parent->comm_set) { | 271 | if (parent->comm_set) { |
276 | const char *comm = thread__comm_str(parent); | 272 | const char *comm = thread__comm_str(parent); |
273 | int err; | ||
277 | if (!comm) | 274 | if (!comm) |
278 | return -ENOMEM; | 275 | return -ENOMEM; |
279 | err = thread__set_comm(thread, comm, timestamp); | 276 | err = thread__set_comm(thread, comm, timestamp); |