aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2016-10-04 04:04:47 -0400
committerIngo Molnar <mingo@kernel.org>2016-10-04 04:04:47 -0400
commit8657355f5b5f657407efc12a2223e8a3a6d658de (patch)
tree29608a0c914ce6f8fc67a90bb1df282eac58a20a /tools
parent597f03f9d133e9837d00965016170271d4f87dcf (diff)
parentb42c7369e3f451e22c2b0be5d193955498d37546 (diff)
Merge tag 'perf-core-for-mingo-20161003' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/core improvements and fixes: - Allow vendors to provide JSON files describing PMU events, that then get parsed to generate C tables that are linked against perf, allowing the use of the names in their documentations, such as: # perf list l1d List of pre-defined events (to be used in -e): Cache: l1d.replacement [L1D data line replacements] l1d_pend_miss.fb_full [Cycles a demand request was blocked due to Fill Buffers inavailability] l1d_pend_miss.pending [L1D miss oustandings duration in cycles] l1d_pend_miss.pending_cycles [Cycles with L1D load Misses outstanding] l1d_pend_miss.pending_cycles_any [Cycles with L1D load Misses outstanding from any thread on physical core] l2_trans.l1d_wb [L1D writebacks that access L2 cache] Pipeline: cycle_activity.cycles_l1d_miss [Cycles while L1 cache miss demand load is outstanding] cycle_activity.cycles_l1d_pending [Cycles while L1 cache miss demand load is outstanding] cycle_activity.stalls_l1d_miss [Execution stalls while L1 cache miss demand load is outstanding] cycle_activity.stalls_l1d_pending [Execution stalls while L1 cache miss demand load is outstanding] The above example was done on a Broadwell based ThinkPad t450s after downloading and installing such JSON files which will be added to the tools/perf/pmu-events/ directory in a subsequent patchkit. Now one can use those names with -e/--event in all 'perf tools'. (Andi Kleen, Sukadev Bhattiprolu) - Add a missing pointer dereference in 'perf probe' (Colin Ian King) - Add support for building host programs to be used in generating files to be used in the build process, such as fixdep and jevents, fixing the usage of these features in a cross compilation setup (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/build/Build2
-rw-r--r--tools/build/Build.include5
-rw-r--r--tools/build/Makefile8
-rw-r--r--tools/build/Makefile.build19
-rw-r--r--tools/build/Makefile.include4
-rw-r--r--tools/lib/subcmd/pager.c16
-rw-r--r--tools/lib/subcmd/pager.h1
-rw-r--r--tools/perf/Documentation/perf-list.txt12
-rw-r--r--tools/perf/Makefile.perf34
-rw-r--r--tools/perf/arch/powerpc/util/header.c11
-rw-r--r--tools/perf/arch/x86/util/header.c24
-rw-r--r--tools/perf/builtin-list.c20
-rw-r--r--tools/perf/pmu-events/Build13
-rw-r--r--tools/perf/pmu-events/README147
-rw-r--r--tools/perf/pmu-events/jevents.c812
-rw-r--r--tools/perf/pmu-events/jevents.h18
-rw-r--r--tools/perf/pmu-events/jsmn.c313
-rw-r--r--tools/perf/pmu-events/jsmn.h67
-rw-r--r--tools/perf/pmu-events/json.c162
-rw-r--r--tools/perf/pmu-events/json.h38
-rw-r--r--tools/perf/pmu-events/pmu-events.h37
-rw-r--r--tools/perf/util/evlist.c12
-rw-r--r--tools/perf/util/evsel.c3
-rw-r--r--tools/perf/util/header.h1
-rw-r--r--tools/perf/util/machine.c6
-rw-r--r--tools/perf/util/parse-events.c8
-rw-r--r--tools/perf/util/parse-events.h3
-rw-r--r--tools/perf/util/pmu.c176
-rw-r--r--tools/perf/util/pmu.h6
-rw-r--r--tools/perf/util/probe-event.c2
-rw-r--r--tools/perf/util/strbuf.h3
-rw-r--r--tools/perf/util/thread.c9
32 files changed, 1926 insertions, 66 deletions
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 @@
1hostprogs := fixdep
2
1fixdep-y := fixdep.o 3fixdep-y := fixdep.o
diff --git a/tools/build/Build.include b/tools/build/Build.include
index 4d000bc959b4..02489380d79b 100644
--- a/tools/build/Build.include
+++ b/tools/build/Build.include
@@ -90,3 +90,8 @@ 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
92c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) 92c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj))
93
94###
95## HOSTCC C flags
96
97host_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
17HOSTCC ?= gcc
18HOSTLD ?= ld
19HOSTAR ?= ar
20
21export HOSTCC HOSTLD HOSTAR
22
17ifeq ($(V),1) 23ifeq ($(V),1)
18 Q = 24 Q =
19else 25else
@@ -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
41FORCE: 47FORCE:
42 48
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build
index 27f3583193e6..190519a94ce5 100644
--- a/tools/build/Makefile.build
+++ b/tools/build/Makefile.build
@@ -58,6 +58,9 @@ quiet_cmd_mkdir = MKDIR $(dir $@)
58quiet_cmd_cc_o_c = CC $@ 58quiet_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
61quiet_cmd_host_cc_o_c = HOSTCC $@
62 cmd_host_cc_o_c = $(HOSTCC) $(host_c_flags) -c -o $@ $<
63
61quiet_cmd_cpp_i_c = CPP $@ 64quiet_cmd_cpp_i_c = CPP $@
62 cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< 65 cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $<
63 66
@@ -70,16 +73,24 @@ quiet_cmd_gen = GEN $@
70# If there's nothing to link, create empty $@ object. 73# If there's nothing to link, create empty $@ object.
71quiet_cmd_ld_multi = LD $@ 74quiet_cmd_ld_multi = LD $@
72 cmd_ld_multi = $(if $(strip $(obj-y)),\ 75 cmd_ld_multi = $(if $(strip $(obj-y)),\
73 $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) 76 $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@)
77
78quiet_cmd_host_ld_multi = HOSTLD $@
79 cmd_host_ld_multi = $(if $(strip $(obj-y)),\
80 $(HOSTLD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(HOSTAR) rcs $@)
81
82ifneq ($(filter $(obj),$(hostprogs)),)
83 host = host_
84endif
74 85
75# Build rules 86# Build rules
76$(OUTPUT)%.o: %.c FORCE 87$(OUTPUT)%.o: %.c FORCE
77 $(call rule_mkdir) 88 $(call rule_mkdir)
78 $(call if_changed_dep,cc_o_c) 89 $(call if_changed_dep,$(host)cc_o_c)
79 90
80$(OUTPUT)%.o: %.S FORCE 91$(OUTPUT)%.o: %.S FORCE
81 $(call rule_mkdir) 92 $(call rule_mkdir)
82 $(call if_changed_dep,cc_o_c) 93 $(call if_changed_dep,$(host)cc_o_c)
83 94
84$(OUTPUT)%.i: %.c FORCE 95$(OUTPUT)%.i: %.c FORCE
85 $(call rule_mkdir) 96 $(call rule_mkdir)
@@ -119,7 +130,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ;
119 130
120$(in-target): $(obj-y) FORCE 131$(in-target): $(obj-y) FORCE
121 $(call rule_mkdir) 132 $(call rule_mkdir)
122 $(call if_changed,ld_multi) 133 $(call if_changed,$(host)ld_multi)
123 134
124__build: $(in-target) 135__build: $(in-target)
125 @: 136 @:
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 @@
1build := -f $(srctree)/tools/build/Makefile.build dir=. obj 1build := -f $(srctree)/tools/build/Makefile.build dir=. obj
2 2
3ifdef CROSS_COMPILE
4fixdep:
5else
6fixdep: 3fixdep:
7 $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep 4 $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
8endif
9 5
10.PHONY: fixdep 6.PHONY: fixdep
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
16static int spawned_pager; 17static int spawned_pager;
18static int pager_columns;
17 19
18void pager_init(const char *pager_env) 20void pager_init(const char *pager_env)
19{ 21{
@@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo)
58void setup_pager(void) 60void 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
107int 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
6extern void setup_pager(void); 6extern void setup_pager(void);
7extern int pager_in_use(void); 7extern int pager_in_use(void);
8extern int pager_get_columns(void);
8 9
9#endif /* __SUBCMD_PAGER_H */ 10#endif /* __SUBCMD_PAGER_H */
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
8SYNOPSIS 8SYNOPSIS
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
13DESCRIPTION 13DESCRIPTION
14----------- 14-----------
15This command displays the symbolic event types which can be selected in the 15This command displays the symbolic event types which can be selected in the
16various perf commands with the -e option. 16various perf commands with the -e option.
17 17
18OPTIONS
19-------
20--no-desc::
21Don't print descriptions.
22
23-v::
24--long-desc::
25Print longer event descriptions.
26
27
18[[EVENT_MODIFIERS]] 28[[EVENT_MODIFIERS]]
19EVENT MODIFIERS 29EVENT MODIFIERS
20--------------- 30---------------
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
145LD += $(EXTRA_LDFLAGS) 145LD += $(EXTRA_LDFLAGS)
146 146
147HOSTCC ?= gcc
148HOSTLD ?= ld
149HOSTAR ?= ar
150
147PKG_CONFIG = $(CROSS_COMPILE)pkg-config 151PKG_CONFIG = $(CROSS_COMPILE)pkg-config
148 152
149RM = rm -f 153RM = rm -f
@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf
345PERF_IN := $(OUTPUT)perf-in.o 349PERF_IN := $(OUTPUT)perf-in.o
346 350
347export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK 351export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
352export HOSTCC HOSTLD HOSTAR
348include $(srctree)/tools/build/Makefile.include 353include $(srctree)/tools/build/Makefile.include
349 354
355JEVENTS := $(OUTPUT)pmu-events/jevents
356JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
357
358PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o
359
360export JEVENTS
361
362build := -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) \
474ifneq ($(OUTPUT),) 497ifneq ($(OUTPUT),)
475%.o: $(OUTPUT)%.o 498%.o: $(OUTPUT)%.o
476 @echo " # Redirected target $@ => $(OUTPUT)$@" 499 @echo " # Redirected target $@ => $(OUTPUT)$@"
500pmu-events/%.o: $(OUTPUT)pmu-events/%.o
501 @echo " # Redirected target $@ => $(OUTPUT)$@"
477util/%.o: $(OUTPUT)util/%.o 502util/%.o: $(OUTPUT)util/%.o
478 @echo " # Redirected target $@ => $(OUTPUT)$@" 503 @echo " # Redirected target $@ => $(OUTPUT)$@"
479bench/%.o: $(OUTPUT)bench/%.o 504bench/%.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
36char *
37get_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/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
22int 22static int
23get_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
61int
62get_cpuid(char *buffer, size_t sz)
63{
64 return __get_cpuid(buffer, sz, "%s,%u,%u,%u$");
65}
66
67char *
68get_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
19static bool desc_flag = true;
20
19int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) 21int 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 @@
1hostprogs := jevents
2
3jevents-y += json.o jsmn.o jevents.o
4pmu-events-y += pmu-events.o
5JDIR = pmu-events/arch/$(ARCH)
6JSON = $(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
2The contents of this directory allow users to specify PMU events in their
3CPUs by their symbolic names rather than raw event codes (see example below).
4
5The main program in this directory, is the 'jevents', which is built and
6executed _BEFORE_ the perf binary itself is built.
7
8The 'jevents' program tries to locate and process JSON files in the directory
9tree 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
20The PMU events supported by a CPU model are expected to grouped into topics
21such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic
22should be placed in a separate JSON file - where the file name identifies
23the topic. Eg: "Floating-point.json".
24
25All the topic JSON files for a CPU model/family should be in a separate
26sub 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
32Using 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
66After 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
69NOTES:
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
80At run time, perf determines the actual CPU it is running on, finds the
81matching events table and builds aliases for those events. This allows
82users to specify events by their name:
83
84 $ perf stat -e pm_1plus_ppc_cmpl sleep 1
85
86where 'pm_1plus_ppc_cmpl' is a Power8 PMU event.
87
88In case of errors when processing files in the tools/perf/pmu-events/arch
89directory, 'jevents' tries to create an empty mapping file to allow the perf
90build to succeed even if the PMU event aliases cannot be used.
91
92However some errors in processing may cause the perf build to fail.
93
94Mapfile format
95===============
96
97The mapfile enables multiple CPU models to share a single set of PMU events.
98It is required even if such mapping is 1:1.
99
100The mapfile.csv format is expected to be:
101
102 Header line
103 CPUID,Version,Dir/path/name,Type
104
105where:
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..79c2133bc534
--- /dev/null
+++ b/tools/perf/pmu-events/jevents.c
@@ -0,0 +1,812 @@
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
55int verbose;
56char *prog;
57
58int 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
81static 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
105static void fixname(char *s)
106{
107 for (; *s; s++)
108 *s = tolower(*s);
109}
110
111static 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
123static 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
134static 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
148static 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
159static 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
174static 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
203static char *topic_array[TOPIC_DEPTH];
204static int topic_level;
205
206static 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
237static 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
259struct perf_entry_data {
260 FILE *outfp;
261 char *topic;
262};
263
264static int close_table;
265
266static 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
272static 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
297static 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
310static struct fixed {
311 const char *name;
312 const char *event;
313} fixed[] = {
314 { "inst_retired.any", "event=0xc0" },
315 { "cpu_clk_unhalted.thread", "event=0x3c" },
316 { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" },
317 { NULL, NULL},
318};
319
320/*
321 * Handle different fixed counter encodings between JSON and perf.
322 */
323static char *real_event(const char *name, char *event)
324{
325 int i;
326
327 for (i = 0; fixed[i].name; i++)
328 if (!strcasecmp(name, fixed[i].name))
329 return (char *)fixed[i].event;
330 return event;
331}
332
333/* Call func with each event in the json file */
334int json_events(const char *fn,
335 int (*func)(void *data, char *name, char *event, char *desc,
336 char *long_desc),
337 void *data)
338{
339 int err = -EIO;
340 size_t size;
341 jsmntok_t *tokens, *tok;
342 int i, j, len;
343 char *map;
344
345 if (!fn)
346 return -ENOENT;
347
348 tokens = parse_json(fn, &map, &size, &len);
349 if (!tokens)
350 return -EIO;
351 EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
352 tok = tokens + 1;
353 for (i = 0; i < tokens->size; i++) {
354 char *event = NULL, *desc = NULL, *name = NULL;
355 char *long_desc = NULL;
356 char *extra_desc = NULL;
357 struct msrmap *msr = NULL;
358 jsmntok_t *msrval = NULL;
359 jsmntok_t *precise = NULL;
360 jsmntok_t *obj = tok++;
361
362 EXPECT(obj->type == JSMN_OBJECT, obj, "expected object");
363 for (j = 0; j < obj->size; j += 2) {
364 jsmntok_t *field, *val;
365 int nz;
366
367 field = tok + j;
368 EXPECT(field->type == JSMN_STRING, tok + j,
369 "Expected field name");
370 val = tok + j + 1;
371 EXPECT(val->type == JSMN_STRING, tok + j + 1,
372 "Expected string value");
373
374 nz = !json_streq(map, val, "0");
375 if (match_field(map, field, nz, &event, val)) {
376 /* ok */
377 } else if (json_streq(map, field, "EventName")) {
378 addfield(map, &name, "", "", val);
379 } else if (json_streq(map, field, "BriefDescription")) {
380 addfield(map, &desc, "", "", val);
381 fixdesc(desc);
382 } else if (json_streq(map, field,
383 "PublicDescription")) {
384 addfield(map, &long_desc, "", "", val);
385 fixdesc(long_desc);
386 } else if (json_streq(map, field, "PEBS") && nz) {
387 precise = val;
388 } else if (json_streq(map, field, "MSRIndex") && nz) {
389 msr = lookup_msr(map, val);
390 } else if (json_streq(map, field, "MSRValue")) {
391 msrval = val;
392 } else if (json_streq(map, field, "Errata") &&
393 !json_streq(map, val, "null")) {
394 addfield(map, &extra_desc, ". ",
395 " Spec update: ", val);
396 } else if (json_streq(map, field, "Data_LA") && nz) {
397 addfield(map, &extra_desc, ". ",
398 " Supports address when precise",
399 NULL);
400 }
401 /* ignore unknown fields */
402 }
403 if (precise && desc && !strstr(desc, "(Precise Event)")) {
404 if (json_streq(map, precise, "2"))
405 addfield(map, &extra_desc, " ",
406 "(Must be precise)", NULL);
407 else
408 addfield(map, &extra_desc, " ",
409 "(Precise event)", NULL);
410 }
411 if (desc && extra_desc)
412 addfield(map, &desc, " ", extra_desc, NULL);
413 if (long_desc && extra_desc)
414 addfield(map, &long_desc, " ", extra_desc, NULL);
415 if (msr != NULL)
416 addfield(map, &event, ",", msr->pname, msrval);
417 fixname(name);
418
419 err = func(data, name, real_event(name, event), desc, long_desc);
420 free(event);
421 free(desc);
422 free(name);
423 free(long_desc);
424 free(extra_desc);
425 if (err)
426 break;
427 tok += j;
428 }
429 EXPECT(tok - tokens == len, tok, "unexpected objects at end");
430 err = 0;
431out_free:
432 free_json(map, size, tokens);
433 return err;
434}
435
436static char *file_name_to_table_name(char *fname)
437{
438 unsigned int i;
439 int n;
440 int c;
441 char *tblname;
442
443 /*
444 * Ensure tablename starts with alphabetic character.
445 * Derive rest of table name from basename of the JSON file,
446 * replacing hyphens and stripping out .json suffix.
447 */
448 n = asprintf(&tblname, "pme_%s", basename(fname));
449 if (n < 0) {
450 pr_info("%s: asprintf() error %s for file %s\n", prog,
451 strerror(errno), fname);
452 return NULL;
453 }
454
455 for (i = 0; i < strlen(tblname); i++) {
456 c = tblname[i];
457
458 if (c == '-')
459 tblname[i] = '_';
460 else if (c == '.') {
461 tblname[i] = '\0';
462 break;
463 } else if (!isalnum(c) && c != '_') {
464 pr_err("%s: Invalid character '%c' in file name %s\n",
465 prog, c, basename(fname));
466 free(tblname);
467 tblname = NULL;
468 break;
469 }
470 }
471
472 return tblname;
473}
474
475static void print_mapping_table_prefix(FILE *outfp)
476{
477 fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n");
478}
479
480static void print_mapping_table_suffix(FILE *outfp)
481{
482 /*
483 * Print the terminating, NULL entry.
484 */
485 fprintf(outfp, "{\n");
486 fprintf(outfp, "\t.cpuid = 0,\n");
487 fprintf(outfp, "\t.version = 0,\n");
488 fprintf(outfp, "\t.type = 0,\n");
489 fprintf(outfp, "\t.table = 0,\n");
490 fprintf(outfp, "},\n");
491
492 /* and finally, the closing curly bracket for the struct */
493 fprintf(outfp, "};\n");
494}
495
496static int process_mapfile(FILE *outfp, char *fpath)
497{
498 int n = 16384;
499 FILE *mapfp;
500 char *save = NULL;
501 char *line, *p;
502 int line_num;
503 char *tblname;
504
505 pr_info("%s: Processing mapfile %s\n", prog, fpath);
506
507 line = malloc(n);
508 if (!line)
509 return -1;
510
511 mapfp = fopen(fpath, "r");
512 if (!mapfp) {
513 pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
514 fpath);
515 return -1;
516 }
517
518 print_mapping_table_prefix(outfp);
519
520 /* Skip first line (header) */
521 p = fgets(line, n, mapfp);
522 if (!p)
523 goto out;
524
525 line_num = 1;
526 while (1) {
527 char *cpuid, *version, *type, *fname;
528
529 line_num++;
530 p = fgets(line, n, mapfp);
531 if (!p)
532 break;
533
534 if (line[0] == '#' || line[0] == '\n')
535 continue;
536
537 if (line[strlen(line)-1] != '\n') {
538 /* TODO Deal with lines longer than 16K */
539 pr_info("%s: Mapfile %s: line %d too long, aborting\n",
540 prog, fpath, line_num);
541 return -1;
542 }
543 line[strlen(line)-1] = '\0';
544
545 cpuid = strtok_r(p, ",", &save);
546 version = strtok_r(NULL, ",", &save);
547 fname = strtok_r(NULL, ",", &save);
548 type = strtok_r(NULL, ",", &save);
549
550 tblname = file_name_to_table_name(fname);
551 fprintf(outfp, "{\n");
552 fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
553 fprintf(outfp, "\t.version = \"%s\",\n", version);
554 fprintf(outfp, "\t.type = \"%s\",\n", type);
555
556 /*
557 * CHECK: We can't use the type (eg "core") field in the
558 * table name. For us to do that, we need to somehow tweak
559 * the other caller of file_name_to_table(), process_json()
560 * to determine the type. process_json() file has no way
561 * of knowing these are "core" events unless file name has
562 * core in it. If filename has core in it, we can safely
563 * ignore the type field here also.
564 */
565 fprintf(outfp, "\t.table = %s\n", tblname);
566 fprintf(outfp, "},\n");
567 }
568
569out:
570 print_mapping_table_suffix(outfp);
571 return 0;
572}
573
574/*
575 * If we fail to locate/process JSON and map files, create a NULL mapping
576 * table. This would at least allow perf to build even if we can't find/use
577 * the aliases.
578 */
579static void create_empty_mapping(const char *output_file)
580{
581 FILE *outfp;
582
583 pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
584
585 /* Truncate file to clear any partial writes to it */
586 outfp = fopen(output_file, "w");
587 if (!outfp) {
588 perror("fopen()");
589 _Exit(1);
590 }
591
592 fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n");
593 print_mapping_table_prefix(outfp);
594 print_mapping_table_suffix(outfp);
595 fclose(outfp);
596}
597
598static int get_maxfds(void)
599{
600 struct rlimit rlim;
601
602 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
603 return min((int)rlim.rlim_max / 2, 512);
604
605 return 512;
606}
607
608/*
609 * nftw() doesn't let us pass an argument to the processing function,
610 * so use a global variables.
611 */
612static FILE *eventsfp;
613static char *mapfile;
614
615static int process_one_file(const char *fpath, const struct stat *sb,
616 int typeflag, struct FTW *ftwbuf)
617{
618 char *tblname, *bname = (char *) fpath + ftwbuf->base;
619 int is_dir = typeflag == FTW_D;
620 int is_file = typeflag == FTW_F;
621 int level = ftwbuf->level;
622 int err = 0;
623
624 pr_debug("%s %d %7jd %-20s %s\n",
625 is_file ? "f" : is_dir ? "d" : "x",
626 level, sb->st_size, bname, fpath);
627
628 /* base dir */
629 if (level == 0)
630 return 0;
631
632 /* model directory, reset topic */
633 if (level == 1 && is_dir) {
634 if (close_table)
635 print_events_table_suffix(eventsfp);
636
637 /*
638 * Drop file name suffix. Replace hyphens with underscores.
639 * Fail if file name contains any alphanum characters besides
640 * underscores.
641 */
642 tblname = file_name_to_table_name(bname);
643 if (!tblname) {
644 pr_info("%s: Error determining table name for %s\n", prog,
645 bname);
646 return -1;
647 }
648
649 print_events_table_prefix(eventsfp, tblname);
650 return 0;
651 }
652
653 /*
654 * Save the mapfile name for now. We will process mapfile
655 * after processing all JSON files (so we can write out the
656 * mapping table after all PMU events tables).
657 *
658 * TODO: Allow for multiple mapfiles? Punt for now.
659 */
660 if (level == 1 && is_file) {
661 if (!strncmp(bname, "mapfile.csv", 11)) {
662 if (mapfile) {
663 pr_info("%s: Many mapfiles? Using %s, ignoring %s\n",
664 prog, mapfile, fpath);
665 } else {
666 mapfile = strdup(fpath);
667 }
668 return 0;
669 }
670
671 pr_info("%s: Ignoring file %s\n", prog, fpath);
672 return 0;
673 }
674
675 /*
676 * If the file name does not have a .json extension,
677 * ignore it. It could be a readme.txt for instance.
678 */
679 if (is_file) {
680 char *suffix = bname + strlen(bname) - 5;
681
682 if (strncmp(suffix, ".json", 5)) {
683 pr_info("%s: Ignoring file without .json suffix %s\n", prog,
684 fpath);
685 return 0;
686 }
687 }
688
689 if (level > 1 && add_topic(level, bname))
690 return -ENOMEM;
691
692 /*
693 * Assume all other files are JSON files.
694 *
695 * If mapfile refers to 'power7_core.json', we create a table
696 * named 'power7_core'. Any inconsistencies between the mapfile
697 * and directory tree could result in build failure due to table
698 * names not being found.
699 *
700 * Atleast for now, be strict with processing JSON file names.
701 * i.e. if JSON file name cannot be mapped to C-style table name,
702 * fail.
703 */
704 if (is_file) {
705 struct perf_entry_data data = {
706 .topic = get_topic(),
707 .outfp = eventsfp,
708 };
709
710 err = json_events(fpath, print_events_table_entry, &data);
711
712 free(data.topic);
713 }
714
715 return err;
716}
717
718#ifndef PATH_MAX
719#define PATH_MAX 4096
720#endif
721
722/*
723 * Starting in directory 'start_dirname', find the "mapfile.csv" and
724 * the set of JSON files for the architecture 'arch'.
725 *
726 * From each JSON file, create a C-style "PMU events table" from the
727 * JSON file (see struct pmu_event).
728 *
729 * From the mapfile, create a mapping between the CPU revisions and
730 * PMU event tables (see struct pmu_events_map).
731 *
732 * Write out the PMU events tables and the mapping table to pmu-event.c.
733 *
734 * If unable to process the JSON or arch files, create an empty mapping
735 * table so we can continue to build/use perf even if we cannot use the
736 * PMU event aliases.
737 */
738int main(int argc, char *argv[])
739{
740 int rc;
741 int maxfds;
742 char ldirname[PATH_MAX];
743
744 const char *arch;
745 const char *output_file;
746 const char *start_dirname;
747
748 prog = basename(argv[0]);
749 if (argc < 4) {
750 pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
751 return 1;
752 }
753
754 arch = argv[1];
755 start_dirname = argv[2];
756 output_file = argv[3];
757
758 if (argc > 4)
759 verbose = atoi(argv[4]);
760
761 eventsfp = fopen(output_file, "w");
762 if (!eventsfp) {
763 pr_err("%s Unable to create required file %s (%s)\n",
764 prog, output_file, strerror(errno));
765 return 2;
766 }
767
768 /* Include pmu-events.h first */
769 fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n");
770
771 sprintf(ldirname, "%s/%s", start_dirname, arch);
772
773 /*
774 * The mapfile allows multiple CPUids to point to the same JSON file,
775 * so, not sure if there is a need for symlinks within the pmu-events
776 * directory.
777 *
778 * For now, treat symlinks of JSON files as regular files and create
779 * separate tables for each symlink (presumably, each symlink refers
780 * to specific version of the CPU).
781 */
782
783 maxfds = get_maxfds();
784 mapfile = NULL;
785 rc = nftw(ldirname, process_one_file, maxfds, 0);
786 if (rc && verbose) {
787 pr_info("%s: Error walking file tree %s\n", prog, ldirname);
788 goto empty_map;
789 } else if (rc) {
790 goto empty_map;
791 }
792
793 if (close_table)
794 print_events_table_suffix(eventsfp);
795
796 if (!mapfile) {
797 pr_info("%s: No CPU->JSON mapping?\n", prog);
798 goto empty_map;
799 }
800
801 if (process_mapfile(eventsfp, mapfile)) {
802 pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
803 goto empty_map;
804 }
805
806 return 0;
807
808empty_map:
809 fclose(eventsfp);
810 create_empty_mapping(output_file);
811 return 0;
812}
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
4int json_events(const char *fn,
5 int (*func)(void *data, char *name, char *event, char *desc,
6 char *long_desc),
7 void *data);
8char *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 */
31static 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 */
47static 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 */
59static 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
102found:
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 */
116static 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 */
173jsmnerr_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 */
292void jsmn_init(jsmn_parser *parser)
293{
294 parser->pos = 0;
295 parser->toknext = 0;
296 parser->toksuper = -1;
297}
298
299const 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 */
11typedef enum {
12 JSMN_PRIMITIVE = 0,
13 JSMN_OBJECT = 1,
14 JSMN_ARRAY = 2,
15 JSMN_STRING = 3
16} jsmntype_t;
17
18typedef 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 */
35typedef 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 */
46typedef 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 */
55void 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 */
61jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
62 size_t len,
63 jsmntok_t *tokens, unsigned int num_tokens);
64
65const 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
44static 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;
68out:
69 close(fd);
70 return map;
71}
72
73static 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 */
83jsmntok_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;
108error_free:
109 free(tokens);
110error:
111 unmapfile(*map, *size);
112 return NULL;
113}
114
115void free_json(char *map, size_t size, jsmntok_t *tokens)
116{
117 free(tokens);
118 unmapfile(map, size);
119}
120
121static 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 */
132int json_line(char *map, jsmntok_t *t)
133{
134 return countchar(map, '\n', t->start) + 1;
135}
136
137static 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 */
147const char *json_name(jsmntok_t *t)
148{
149 return LOOKUP(jsmn_types, t->type);
150}
151
152int json_len(jsmntok_t *t)
153{
154 return t->end - t->start;
155}
156
157/* Is string t equal to s? */
158int 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
6jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len);
7void free_json(char *map, size_t size, jsmntok_t *tokens);
8int json_line(char *map, jsmntok_t *t);
9const char *json_name(jsmntok_t *t);
10int json_streq(char *map, jsmntok_t *t, const char *s);
11int json_len(jsmntok_t *t);
12
13extern int verbose;
14
15#include <stdbool.h>
16
17extern 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 */
7struct 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 */
24struct 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 */
35extern 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)
384static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, 384static 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
986static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) 986static 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 */
152int get_cpuid(char *buffer, size_t sz); 152int get_cpuid(char *buffer, size_t sz);
153 153
154char *get_cpuid_str(void);
154#endif /* __PERF_HEADER_H */ 155#endif /* __PERF_HEADER_H */
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
1464static void perf_pmu__parse_cleanup(void) 1465static 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 */
2266void print_events(const char *event_glob, bool name_only) 2267void 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,
172void parse_events_evlist_error(struct parse_events_evlist *data, 172void parse_events_evlist_error(struct parse_events_evlist *data,
173 int idx, const char *str); 173 int idx, const char *str);
174 174
175void print_events(const char *event_glob, bool name_only); 175void print_events(const char *event_glob, bool name_only, bool quiet,
176 bool long_desc);
176 177
177struct event_symbol { 178struct 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
16struct perf_pmu_format { 19struct 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
222static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, 225static 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
275static inline bool pmu_alias_info_file(char *name) 284static 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 */
491char * __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 */
501static 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
543out:
544 free(cpuid);
545}
546
476struct perf_event_attr * __weak 547struct perf_event_attr * __weak
477perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) 548perf_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
986static int cmp_string(const void *a, const void *b) 1060struct sevent {
1061 char *name;
1062 char *desc;
1063 char *topic;
1064};
1065
1066static 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
993void print_pmu_events(const char *event_glob, bool name_only) 1083static 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
1105void 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");
1055out_free: 1195out_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
41struct perf_pmu_alias { 41struct 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
72struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); 75struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
73 76
74void print_pmu_events(const char *event_glob, bool name_only); 77void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
78 bool long_desc);
75bool pmu_have_event(const char *pname, const char *name); 79bool pmu_have_event(const char *pname, const char *name);
76 80
77int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, 81int 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) {
66int strbuf_grow(struct strbuf *buf, size_t); 66int strbuf_grow(struct strbuf *buf, size_t);
67 67
68static inline int strbuf_setlen(struct strbuf *sb, size_t len) { 68static 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
15int thread__init_map_groups(struct thread *thread, struct machine *machine) 15int 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
271int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) 269int 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);