Commit c48ce9f1 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 84ed2da0 c68306ce
...@@ -106,7 +106,6 @@ ...@@ -106,7 +106,6 @@
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */
#define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ #define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
#define X86_FEATURE_MCE_RECOVERY ( 3*32+31) /* cpu has recoverable machine checks */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */
......
...@@ -181,11 +181,11 @@ ENDPROC(memcpy_orig) ...@@ -181,11 +181,11 @@ ENDPROC(memcpy_orig)
#ifndef CONFIG_UML #ifndef CONFIG_UML
/* /*
* memcpy_mcsafe - memory copy with machine check exception handling * memcpy_mcsafe_unrolled - memory copy with machine check exception handling
* Note that we only catch machine checks when reading the source addresses. * Note that we only catch machine checks when reading the source addresses.
* Writes to target are posted and don't generate machine checks. * Writes to target are posted and don't generate machine checks.
*/ */
ENTRY(memcpy_mcsafe) ENTRY(memcpy_mcsafe_unrolled)
cmpl $8, %edx cmpl $8, %edx
/* Less than 8 bytes? Go to byte copy loop */ /* Less than 8 bytes? Go to byte copy loop */
jb .L_no_whole_words jb .L_no_whole_words
...@@ -273,7 +273,7 @@ ENTRY(memcpy_mcsafe) ...@@ -273,7 +273,7 @@ ENTRY(memcpy_mcsafe)
.L_done_memcpy_trap: .L_done_memcpy_trap:
xorq %rax, %rax xorq %rax, %rax
ret ret
ENDPROC(memcpy_mcsafe) ENDPROC(memcpy_mcsafe_unrolled)
.section .fixup, "ax" .section .fixup, "ax"
/* Return -EFAULT for any failure */ /* Return -EFAULT for any failure */
......
hostprogs := fixdep
fixdep-y := fixdep.o fixdep-y := fixdep.o
...@@ -90,3 +90,9 @@ if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ ...@@ -90,3 +90,9 @@ if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
# - per object C flags # - per object C flags
# - BUILD_STR macro to allow '-D"$(variable)"' constructs # - BUILD_STR macro to allow '-D"$(variable)"' constructs
c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj))
cxx_flags = -Wp,-MD,$(depfile),-MT,$@ $(CXXFLAGS) -D"BUILD_STR(s)=\#s" $(CXXFLAGS_$(basetarget).o) $(CXXFLAGS_$(obj))
###
## HOSTCC C flags
host_c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CHOSTFLAGS) -D"BUILD_STR(s)=\#s" $(CHOSTFLAGS_$(basetarget).o) $(CHOSTFLAGS_$(obj))
...@@ -14,6 +14,12 @@ endef ...@@ -14,6 +14,12 @@ endef
$(call allow-override,CC,$(CROSS_COMPILE)gcc) $(call allow-override,CC,$(CROSS_COMPILE)gcc)
$(call allow-override,LD,$(CROSS_COMPILE)ld) $(call allow-override,LD,$(CROSS_COMPILE)ld)
HOSTCC ?= gcc
HOSTLD ?= ld
HOSTAR ?= ar
export HOSTCC HOSTLD HOSTAR
ifeq ($(V),1) ifeq ($(V),1)
Q = Q =
else else
...@@ -36,7 +42,7 @@ $(OUTPUT)fixdep-in.o: FORCE ...@@ -36,7 +42,7 @@ $(OUTPUT)fixdep-in.o: FORCE
$(Q)$(MAKE) $(build)=fixdep $(Q)$(MAKE) $(build)=fixdep
$(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o $(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $< $(QUIET_LINK)$(HOSTCC) $(LDFLAGS) -o $@ $<
FORCE: FORCE:
......
...@@ -58,6 +58,12 @@ quiet_cmd_mkdir = MKDIR $(dir $@) ...@@ -58,6 +58,12 @@ quiet_cmd_mkdir = MKDIR $(dir $@)
quiet_cmd_cc_o_c = CC $@ quiet_cmd_cc_o_c = CC $@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
quiet_cmd_host_cc_o_c = HOSTCC $@
cmd_host_cc_o_c = $(HOSTCC) $(host_c_flags) -c -o $@ $<
quiet_cmd_cxx_o_c = CXX $@
cmd_cxx_o_c = $(CXX) $(cxx_flags) -c -o $@ $<
quiet_cmd_cpp_i_c = CPP $@ quiet_cmd_cpp_i_c = CPP $@
cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $<
...@@ -70,16 +76,28 @@ quiet_cmd_gen = GEN $@ ...@@ -70,16 +76,28 @@ quiet_cmd_gen = GEN $@
# If there's nothing to link, create empty $@ object. # If there's nothing to link, create empty $@ object.
quiet_cmd_ld_multi = LD $@ quiet_cmd_ld_multi = LD $@
cmd_ld_multi = $(if $(strip $(obj-y)),\ cmd_ld_multi = $(if $(strip $(obj-y)),\
$(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@)
quiet_cmd_host_ld_multi = HOSTLD $@
cmd_host_ld_multi = $(if $(strip $(obj-y)),\
$(HOSTLD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(HOSTAR) rcs $@)
ifneq ($(filter $(obj),$(hostprogs)),)
host = host_
endif
# Build rules # Build rules
$(OUTPUT)%.o: %.c FORCE $(OUTPUT)%.o: %.c FORCE
$(call rule_mkdir) $(call rule_mkdir)
$(call if_changed_dep,cc_o_c) $(call if_changed_dep,$(host)cc_o_c)
$(OUTPUT)%.o: %.cpp FORCE
$(call rule_mkdir)
$(call if_changed_dep,cxx_o_c)
$(OUTPUT)%.o: %.S FORCE $(OUTPUT)%.o: %.S FORCE
$(call rule_mkdir) $(call rule_mkdir)
$(call if_changed_dep,cc_o_c) $(call if_changed_dep,$(host)cc_o_c)
$(OUTPUT)%.i: %.c FORCE $(OUTPUT)%.i: %.c FORCE
$(call rule_mkdir) $(call rule_mkdir)
...@@ -119,7 +137,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ; ...@@ -119,7 +137,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ;
$(in-target): $(obj-y) FORCE $(in-target): $(obj-y) FORCE
$(call rule_mkdir) $(call rule_mkdir)
$(call if_changed,ld_multi) $(call if_changed,$(host)ld_multi)
__build: $(in-target) __build: $(in-target)
@: @:
......
...@@ -7,7 +7,7 @@ endif ...@@ -7,7 +7,7 @@ endif
feature_check = $(eval $(feature_check_code)) feature_check = $(eval $(feature_check_code))
define feature_check_code define feature_check_code
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) 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)
endef endef
feature_set = $(eval $(feature_set_code)) feature_set = $(eval $(feature_set_code))
......
build := -f $(srctree)/tools/build/Makefile.build dir=. obj build := -f $(srctree)/tools/build/Makefile.build dir=. obj
ifdef CROSS_COMPILE
fixdep:
else
fixdep: fixdep:
$(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
endif
.PHONY: fixdep .PHONY: fixdep
...@@ -46,11 +46,13 @@ FILES= \ ...@@ -46,11 +46,13 @@ FILES= \
test-lzma.bin \ test-lzma.bin \
test-bpf.bin \ test-bpf.bin \
test-get_cpuid.bin \ test-get_cpuid.bin \
test-sdt.bin test-sdt.bin \
test-cxx.bin
FILES := $(addprefix $(OUTPUT),$(FILES)) FILES := $(addprefix $(OUTPUT),$(FILES))
CC := $(CROSS_COMPILE)gcc -MD CC := $(CROSS_COMPILE)gcc -MD
CXX := $(CROSS_COMPILE)g++ -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config PKG_CONFIG := $(CROSS_COMPILE)pkg-config
all: $(FILES) all: $(FILES)
...@@ -58,6 +60,9 @@ all: $(FILES) ...@@ -58,6 +60,9 @@ all: $(FILES)
__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS)
BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1
__BUILDXX = $(CXX) $(CXXFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS)
BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1
############################### ###############################
$(OUTPUT)test-all.bin: $(OUTPUT)test-all.bin:
...@@ -217,6 +222,9 @@ $(OUTPUT)test-bpf.bin: ...@@ -217,6 +222,9 @@ $(OUTPUT)test-bpf.bin:
$(OUTPUT)test-sdt.bin: $(OUTPUT)test-sdt.bin:
$(BUILD) $(BUILD)
$(OUTPUT)test-cxx.bin:
$(BUILDXX) -std=gnu++11
-include $(OUTPUT)*.d -include $(OUTPUT)*.d
############################### ###############################
......
#include <iostream>
#include <memory>
static void print_str(std::string s)
{
std::cout << s << std::endl;
}
int main()
{
std::string s("Hello World!");
print_str(std::move(s));
std::cout << "|" << s << "|" << std::endl;
return 0;
}
...@@ -339,7 +339,7 @@ enum bpf_func_id { ...@@ -339,7 +339,7 @@ enum bpf_func_id {
BPF_FUNC_skb_change_type, BPF_FUNC_skb_change_type,
/** /**
* bpf_skb_in_cgroup(skb, map, index) - Check cgroup2 membership of skb * bpf_skb_under_cgroup(skb, map, index) - Check cgroup2 membership of skb
* @skb: pointer to skb * @skb: pointer to skb
* @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type
* @index: index of the cgroup in the bpf_map * @index: index of the cgroup in the bpf_map
...@@ -348,7 +348,7 @@ enum bpf_func_id { ...@@ -348,7 +348,7 @@ enum bpf_func_id {
* == 1 skb succeeded the cgroup2 descendant test * == 1 skb succeeded the cgroup2 descendant test
* < 0 error * < 0 error
*/ */
BPF_FUNC_skb_in_cgroup, BPF_FUNC_skb_under_cgroup,
/** /**
* bpf_get_hash_recalc(skb) * bpf_get_hash_recalc(skb)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <sys/ioctl.h>
#include "pager.h" #include "pager.h"
#include "run-command.h" #include "run-command.h"
#include "sigchain.h" #include "sigchain.h"
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
*/ */
static int spawned_pager; static int spawned_pager;
static int pager_columns;
void pager_init(const char *pager_env) void pager_init(const char *pager_env)
{ {
...@@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo) ...@@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo)
void setup_pager(void) void setup_pager(void)
{ {
const char *pager = getenv(subcmd_config.pager_env); const char *pager = getenv(subcmd_config.pager_env);
struct winsize sz;
if (!isatty(1)) if (!isatty(1))
return; return;
if (ioctl(1, TIOCGWINSZ, &sz) == 0)
pager_columns = sz.ws_col;
if (!pager) if (!pager)
pager = getenv("PAGER"); pager = getenv("PAGER");
if (!(pager || access("/usr/bin/pager", X_OK))) if (!(pager || access("/usr/bin/pager", X_OK)))
...@@ -98,3 +103,14 @@ int pager_in_use(void) ...@@ -98,3 +103,14 @@ int pager_in_use(void)
{ {
return spawned_pager; return spawned_pager;
} }
int pager_get_columns(void)
{
char *s;
s = getenv("COLUMNS");
if (s)
return atoi(s);
return (pager_columns ? pager_columns : 80) - 2;
}
...@@ -5,5 +5,6 @@ extern void pager_init(const char *pager_env); ...@@ -5,5 +5,6 @@ extern void pager_init(const char *pager_env);
extern void setup_pager(void); extern void setup_pager(void);
extern int pager_in_use(void); extern int pager_in_use(void);
extern int pager_get_columns(void);
#endif /* __SUBCMD_PAGER_H */ #endif /* __SUBCMD_PAGER_H */
...@@ -622,6 +622,7 @@ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, ...@@ -622,6 +622,7 @@ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset,
/* Reset the buffer */ /* Reset the buffer */
kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); kbuffer_load_subbuffer(kbuf, kbuf->subbuffer);
data = kbuffer_read_event(kbuf, ts);
while (kbuf->curr < offset) { while (kbuf->curr < offset) {
data = kbuffer_next_event(kbuf, ts); data = kbuffer_next_event(kbuf, ts);
......
...@@ -8,13 +8,23 @@ perf-list - List all symbolic event types ...@@ -8,13 +8,23 @@ perf-list - List all symbolic event types
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'perf list' [hw|sw|cache|tracepoint|pmu|event_glob] 'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|event_glob]
DESCRIPTION DESCRIPTION
----------- -----------
This command displays the symbolic event types which can be selected in the This command displays the symbolic event types which can be selected in the
various perf commands with the -e option. various perf commands with the -e option.
OPTIONS
-------
--no-desc::
Don't print descriptions.
-v::
--long-desc::
Print longer event descriptions.
[[EVENT_MODIFIERS]] [[EVENT_MODIFIERS]]
EVENT MODIFIERS EVENT MODIFIERS
--------------- ---------------
......
...@@ -28,3 +28,7 @@ To change sampling frequency to 100 Hz: perf record -F 100 ...@@ -28,3 +28,7 @@ To change sampling frequency to 100 Hz: perf record -F 100
See assembly instructions with percentage: perf annotate <symbol> See assembly instructions with percentage: perf annotate <symbol>
If you prefer Intel style assembly, try: perf annotate -M intel If you prefer Intel style assembly, try: perf annotate -M intel
For hierarchical output, try: perf report --hierarchy For hierarchical output, try: perf report --hierarchy
Order by the overhead of source file name and line number: perf report -s srcline
System-wide collection from all CPUs: perf record -a
Show current config key-value pairs: perf config --list
Show user configuration overrides: perf config --user --list
...@@ -144,6 +144,10 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld) ...@@ -144,6 +144,10 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld)
LD += $(EXTRA_LDFLAGS) LD += $(EXTRA_LDFLAGS)
HOSTCC ?= gcc
HOSTLD ?= ld
HOSTAR ?= ar
PKG_CONFIG = $(CROSS_COMPILE)pkg-config PKG_CONFIG = $(CROSS_COMPILE)pkg-config
RM = rm -f RM = rm -f
...@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf ...@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf
PERF_IN := $(OUTPUT)perf-in.o PERF_IN := $(OUTPUT)perf-in.o
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
export HOSTCC HOSTLD HOSTAR
include $(srctree)/tools/build/Makefile.include include $(srctree)/tools/build/Makefile.include
JEVENTS := $(OUTPUT)pmu-events/jevents
JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o
export JEVENTS
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
$(PERF_IN): prepare FORCE $(PERF_IN): prepare FORCE
@(test -f ../../include/uapi/linux/perf_event.h && ( \ @(test -f ../../include/uapi/linux/perf_event.h && ( \
(diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ (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,9 +457,18 @@ $(PERF_IN): prepare FORCE
|| echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true
$(Q)$(MAKE) $(build)=perf $(Q)$(MAKE) $(build)=perf
$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) $(JEVENTS_IN): FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents
$(JEVENTS): $(JEVENTS_IN)
$(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@
$(PMU_EVENTS_IN): $(JEVENTS) FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events
$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
$(PERF_IN) $(LIBS) -o $@ $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@
$(GTK_IN): fixdep FORCE $(GTK_IN): fixdep FORCE
$(Q)$(MAKE) $(build)=gtk $(Q)$(MAKE) $(build)=gtk
...@@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \ ...@@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \
ifneq ($(OUTPUT),) ifneq ($(OUTPUT),)
%.o: $(OUTPUT)%.o %.o: $(OUTPUT)%.o
@echo " # Redirected target $@ => $(OUTPUT)$@" @echo " # Redirected target $@ => $(OUTPUT)$@"
pmu-events/%.o: $(OUTPUT)pmu-events/%.o
@echo " # Redirected target $@ => $(OUTPUT)$@"
util/%.o: $(OUTPUT)util/%.o util/%.o: $(OUTPUT)util/%.o
@echo " # Redirected target $@ => $(OUTPUT)$@" @echo " # Redirected target $@ => $(OUTPUT)$@"
bench/%.o: $(OUTPUT)bench/%.o bench/%.o: $(OUTPUT)bench/%.o
...@@ -729,10 +754,11 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea ...@@ -729,10 +754,11 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
$(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) $(OUTPUT).config-detected $(Q)$(RM) $(OUTPUT).config-detected
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents
$(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* \ $(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* \
$(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \
$(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
$(OUTPUT)pmu-events/pmu-events.c
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean) $(python-clean)
......
...@@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz) ...@@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz)
} }
return -1; return -1;
} }
char *
get_cpuid_str(void)
{
char *bufp;
if (asprintf(&bufp, "%.8lx", mfspr(SPRN_PVR)) < 0)
bufp = NULL;
return bufp;
}
...@@ -82,7 +82,8 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev, ...@@ -82,7 +82,8 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
* *
* In addition, we shouldn't specify an offset for kretprobes. * In addition, we shouldn't specify an offset for kretprobes.
*/ */
if (pev->point.offset || pev->point.retprobe || !map || !sym) if (pev->point.offset || (!pev->uprobes && pev->point.retprobe) ||
!map || !sym)
return; return;
lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
......
...@@ -19,8 +19,8 @@ cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, ...@@ -19,8 +19,8 @@ cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
: "a" (op)); : "a" (op));
} }
int static int
get_cpuid(char *buffer, size_t sz) __get_cpuid(char *buffer, size_t sz, const char *fmt)
{ {
unsigned int a, b, c, d, lvl; unsigned int a, b, c, d, lvl;
int family = -1, model = -1, step = -1; int family = -1, model = -1, step = -1;
...@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz) ...@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz)
if (family >= 0x6) if (family >= 0x6)
model += ((a >> 16) & 0xf) << 4; model += ((a >> 16) & 0xf) << 4;
} }
nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); nb = scnprintf(buffer, sz, fmt, vendor, family, model, step);
/* look for end marker to ensure the entire data fit */ /* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) { if (strchr(buffer, '$')) {
...@@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz) ...@@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz)
} }
return -1; return -1;
} }
int
get_cpuid(char *buffer, size_t sz)
{
return __get_cpuid(buffer, sz, "%s,%u,%u,%u$");
}
char *
get_cpuid_str(void)
{
char *buf = malloc(128);
if (__get_cpuid(buf, 128, "%s-%u-%X$") < 0) {
free(buf);
return NULL;
}
return buf;
}
...@@ -16,16 +16,23 @@ ...@@ -16,16 +16,23 @@
#include "util/pmu.h" #include "util/pmu.h"
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
static bool desc_flag = true;
int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
int i; int i;
bool raw_dump = false; bool raw_dump = false;
bool long_desc_flag = false;
struct option list_options[] = { struct option list_options[] = {
OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"),
OPT_BOOLEAN('d', "desc", &desc_flag,
"Print extra event descriptions. --no-desc to not print."),
OPT_BOOLEAN('v', "long-desc", &long_desc_flag,
"Print longer event descriptions."),
OPT_END() OPT_END()
}; };
const char * const list_usage[] = { const char * const list_usage[] = {
"perf list [hw|sw|cache|tracepoint|pmu|sdt|event_glob]", "perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|event_glob]",
NULL NULL
}; };
...@@ -40,7 +47,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -40,7 +47,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
printf("\nList of pre-defined events (to be used in -e):\n\n"); printf("\nList of pre-defined events (to be used in -e):\n\n");
if (argc == 0) { if (argc == 0) {
print_events(NULL, raw_dump); print_events(NULL, raw_dump, !desc_flag, long_desc_flag);
return 0; return 0;
} }
...@@ -61,14 +68,16 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -61,14 +68,16 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
strcmp(argv[i], "hwcache") == 0) strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, raw_dump); print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0) else if (strcmp(argv[i], "pmu") == 0)
print_pmu_events(NULL, raw_dump); print_pmu_events(NULL, raw_dump, !desc_flag,
long_desc_flag);
else if (strcmp(argv[i], "sdt") == 0) else if (strcmp(argv[i], "sdt") == 0)
print_sdt_events(NULL, NULL, raw_dump); print_sdt_events(NULL, NULL, raw_dump);
else if ((sep = strchr(argv[i], ':')) != NULL) { else if ((sep = strchr(argv[i], ':')) != NULL) {
int sep_idx; int sep_idx;
if (sep == NULL) { if (sep == NULL) {
print_events(argv[i], raw_dump); print_events(argv[i], raw_dump, !desc_flag,
long_desc_flag);
continue; continue;
} }
sep_idx = sep - argv[i]; sep_idx = sep - argv[i];
...@@ -90,7 +99,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -90,7 +99,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
print_symbol_events(s, PERF_TYPE_SOFTWARE, print_symbol_events(s, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump); event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
print_hwcache_events(s, raw_dump); print_hwcache_events(s, raw_dump);
print_pmu_events(s, raw_dump); print_pmu_events(s, raw_dump, !desc_flag,
long_desc_flag);
print_tracepoint_events(NULL, s, raw_dump); print_tracepoint_events(NULL, s, raw_dump);
print_sdt_events(NULL, s, raw_dump); print_sdt_events(NULL, s, raw_dump);
free(s); free(s);
......
hostprogs := jevents
jevents-y += json.o jsmn.o jevents.o
pmu-events-y += pmu-events.o
JDIR = pmu-events/arch/$(ARCH)
JSON = $(shell [ -d $(JDIR) ] && \
find $(JDIR) -name '*.json' -o -name 'mapfile.csv')
#
# Locate/process JSON files in pmu-events/arch/
# directory and create tables in pmu-events.c.
#
$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS)
$(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V)
The contents of this directory allow users to specify PMU events in their
CPUs by their symbolic names rather than raw event codes (see example below).
The main program in this directory, is the 'jevents', which is built and
executed _BEFORE_ the perf binary itself is built.
The 'jevents' program tries to locate and process JSON files in the directory
tree tools/perf/pmu-events/arch/foo.
- Regular files with '.json' extension in the name are assumed to be
JSON files, each of which describes a set of PMU events.
- Regular files with basename starting with 'mapfile.csv' are assumed
to be a CSV file that maps a specific CPU to its set of PMU events.
(see below for mapfile format)
- Directories are traversed, but all other files are ignored.
The PMU events supported by a CPU model are expected to grouped into topics
such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic
should be placed in a separate JSON file - where the file name identifies
the topic. Eg: "Floating-point.json".
All the topic JSON files for a CPU model/family should be in a separate
sub directory. Thus for the Silvermont X86 CPU:
$ ls tools/perf/pmu-events/arch/x86/Silvermont_core
Cache.json Memory.json Virtual-Memory.json
Frontend.json Pipeline.json
Using the JSON files and the mapfile, 'jevents' generates the C source file,
'pmu-events.c', which encodes the two sets of tables:
- Set of 'PMU events tables' for all known CPUs in the architecture,
(one table like the following, per JSON file; table name 'pme_power8'
is derived from JSON file name, 'power8.json').
struct pmu_event pme_power8[] = {
...
{
.name = "pm_1plus_ppc_cmpl",
.event = "event=0x100f2",
.desc = "1 or more ppc insts finished,",
},
...
}
- A 'mapping table' that maps each CPU of the architecture, to its
'PMU events table'
struct pmu_events_map pmu_events_map[] = {
{
.cpuid = "004b0000",
.version = "1",
.type = "core",
.table = pme_power8
},
...
};
After the 'pmu-events.c' is generated, it is compiled and the resulting
'pmu-events.o' is added to 'libperf.a' which is then used to build perf.
NOTES:
1. Several CPUs can support same set of events and hence use a common
JSON file. Hence several entries in the pmu_events_map[] could map
to a single 'PMU events table'.
2. The 'pmu-events.h' has an extern declaration for the mapping table
and the generated 'pmu-events.c' defines this table.
3. _All_ known CPU tables for architecture are included in the perf
binary.
At run time, perf determines the actual CPU it is running on, finds the
matching events table and builds aliases for those events. This allows
users to specify events by their name:
$ perf stat -e pm_1plus_ppc_cmpl sleep 1
where 'pm_1plus_ppc_cmpl' is a Power8 PMU event.
In case of errors when processing files in the tools/perf/pmu-events/arch
directory, 'jevents' tries to create an empty mapping file to allow the perf
build to succeed even if the PMU event aliases cannot be used.
However some errors in processing may cause the perf build to fail.
Mapfile format
===============
The mapfile enables multiple CPU models to share a single set of PMU events.
It is required even if such mapping is 1:1.
The mapfile.csv format is expected to be:
Header line
CPUID,Version,Dir/path/name,Type
where:
Comma:
is the required field delimiter (i.e other fields cannot
have commas within them).
Comments:
Lines in which the first character is either '\n' or '#'
are ignored.
Header line
The header line is the first line in the file, which is
always _IGNORED_. It can empty.
CPUID:
CPUID is an arch-specific char string, that can be used
to identify CPU (and associate it with a set of PMU events
it supports). Multiple CPUIDS can point to the same
File/path/name.json.
Example:
CPUID == 'GenuineIntel-6-2E' (on x86).
CPUID == '004b0100' (PVR value in Powerpc)
Version:
is the Version of the mapfile.
Dir/path/name:
is the pathname to the directory containing the CPU's JSON
files, relative to the directory containing the mapfile.csv
Type:
indicates whether the events or "core" or "uncore" events.
Eg:
$ grep Silvermont tools/perf/pmu-events/arch/x86/mapfile.csv
GenuineIntel-6-37,V13,Silvermont_core,core
GenuineIntel-6-4D,V13,Silvermont_core,core
GenuineIntel-6-4C,V13,Silvermont_core,core
i.e the three CPU models use the JSON files (i.e PMU events) listed
in the directory 'tools/perf/pmu-events/arch/x86/Silvermont_core'.
This diff is collapsed.
#ifndef JEVENTS_H
#define JEVENTS_H 1
int json_events(const char *fn,
int (*func)(void *data, char *name, char *event, char *desc,
char *long_desc),
void *data);
char *get_cpu_str(void);
#ifndef min
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
#endif
#endif
/*
* Copyright (c) 2010 Serge A. Zaitsev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Slightly modified by AK to not assume 0 terminated input.
*/
#include <stdlib.h>
#include "jsmn.h"
/*
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
jsmntok_t *tokens, size_t num_tokens)
{
jsmntok_t *tok;
if ((unsigned)parser->toknext >= num_tokens)
return NULL;
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
return tok;
}
/*
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
int start, int end)
{
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/*
* Fills next available token with JSON primitive.
*/
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
size_t len,
jsmntok_t *tokens, size_t num_tokens)
{
jsmntok_t *token;
int start;
start = parser->pos;
for (; parser->pos < len; parser->pos++) {
switch (js[parser->pos]) {
#ifndef JSMN_STRICT
/*
* In strict mode primitive must be followed by ","
* or "}" or "]"
*/
case ':':
#endif
case '\t':
case '\r':
case '\n':
case ' ':
case ',':
case ']':
case '}':
goto found;
default:
break;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/*
* In strict mode primitive must be followed by a
* comma/object/array.
*/
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
parser->pos--; /* parent sees closing brackets */
return JSMN_SUCCESS;
}
/*
* Fills next token with JSON string.
*/
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
size_t len,
jsmntok_t *tokens, size_t num_tokens)
{
jsmntok_t *token;
int start = parser->pos;
/* Skip starting quote */
parser->pos++;
for (; parser->pos < len; parser->pos++) {
char c = js[parser->pos];
/* Quote: end of string */
if (c == '\"') {
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start+1,
parser->pos);
return JSMN_SUCCESS;
}
/* Backslash: Quoted symbol expected */
if (c == '\\') {
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"':
case '/':
case '\\':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break;
/* Allows escaped symbol \uXXXX */
case 'u':
/* TODO */
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/*
* Parse JSON string and fill tokens.
*/
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens)
{
jsmnerr_t r;
int i;
jsmntok_t *token;
for (; parser->pos < len; parser->pos++) {
char c;
jsmntype_t type;
c = js[parser->pos];
switch (c) {
case '{':
case '[':
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL)
return JSMN_ERROR_NOMEM;
if (parser->toksuper != -1)
tokens[parser->toksuper].size++;
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
parser->toksuper = parser->toknext - 1;
break;
case '}':
case ']':
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type)
return JSMN_ERROR_INVAL;
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1)
return JSMN_ERROR_INVAL;
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens,
num_tokens);
if (r < 0)
return r;
if (parser->toksuper != -1)
tokens[parser->toksuper].size++;
break;
case '\t':
case '\r':
case '\n':
case ':':
case ',':
case ' ':
break;
#ifdef JSMN_STRICT
/*
* In strict mode primitives are:
* numbers and booleans.
*/
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 't':
case 'f':
case 'n':
#else
/*
* In non-strict mode every unquoted value
* is a primitive.
*/
/*FALL THROUGH */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens,
num_tokens);
if (r < 0)
return r;
if (parser->toksuper != -1)
tokens[parser->toksuper].size++;
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1)
return JSMN_ERROR_PART;
}
return JSMN_SUCCESS;
}
/*
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
void jsmn_init(jsmn_parser *parser)
{
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}
const char *jsmn_strerror(jsmnerr_t err)
{
switch (err) {
case JSMN_ERROR_NOMEM:
return "No enough tokens";
case JSMN_ERROR_INVAL:
return "Invalid character inside JSON string";
case JSMN_ERROR_PART:
return "The string is not a full JSON packet, more bytes expected";
case JSMN_SUCCESS:
return "Success";
default:
return "Unknown json error";
}
}
#ifndef __JSMN_H_
#define __JSMN_H_
/*
* JSON type identifier. Basic types are:
* o Object
* o Array
* o String
* o Other primitive: number, boolean (true/false) or null
*/
typedef enum {
JSMN_PRIMITIVE = 0,
JSMN_OBJECT = 1,
JSMN_ARRAY = 2,
JSMN_STRING = 3
} jsmntype_t;
typedef enum {
/* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3,
/* Everything was fine */
JSMN_SUCCESS = 0
} jsmnerr_t;
/*
* JSON token description.
* @param type type (object, array, string etc.)
* @param start start position in JSON data string
* @param end end position in JSON data string
*/
typedef struct {
jsmntype_t type;
int start;
int end;
int size;
} jsmntok_t;
/*
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string
*/
typedef struct {
unsigned int pos; /* offset in the JSON string */
int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;
/*
* Create JSON parser over an array of tokens
*/
void jsmn_init(jsmn_parser *parser);
/*
* Run JSON parser. It parses a JSON data string into and array of tokens,
* each describing a single JSON object.
*/
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,
size_t len,
jsmntok_t *tokens, unsigned int num_tokens);
const char *jsmn_strerror(jsmnerr_t err);
#endif /* __JSMN_H_ */
/* Parse JSON files using the JSMN parser. */
/*
* Copyright (c) 2014, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include "jsmn.h"
#include "json.h"
#include <linux/kernel.h>
static char *mapfile(const char *fn, size_t *size)
{
unsigned ps = sysconf(_SC_PAGESIZE);
struct stat st;
char *map = NULL;
int err;
int fd = open(fn, O_RDONLY);
if (fd < 0 && verbose && fn) {
pr_err("Error opening events file '%s': %s\n", fn,
strerror(errno));
}
if (fd < 0)
return NULL;
err = fstat(fd, &st);
if (err < 0)
goto out;
*size = st.st_size;
map = mmap(NULL,
(st.st_size + ps - 1) & ~(ps - 1),
PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED)
map = NULL;
out:
close(fd);
return map;
}
static void unmapfile(char *map, size_t size)
{
unsigned ps = sysconf(_SC_PAGESIZE);
munmap(map, roundup(size, ps));
}
/*
* Parse json file using jsmn. Return array of tokens,
* and mapped file. Caller needs to free array.
*/
jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len)
{
jsmn_parser parser;
jsmntok_t *tokens;
jsmnerr_t res;
unsigned sz;
*map = mapfile(fn, size);
if (!*map)
return NULL;
/* Heuristic */
sz = *size * 16;
tokens = malloc(sz);
if (!tokens)
goto error;
jsmn_init(&parser);
res = jsmn_parse(&parser, *map, *size, tokens,
sz / sizeof(jsmntok_t));
if (res != JSMN_SUCCESS) {
pr_err("%s: json error %s\n", fn, jsmn_strerror(res));
goto error_free;
}
if (len)
*len = parser.toknext;
return tokens;
error_free:
free(tokens);
error:
unmapfile(*map, *size);
return NULL;
}
void free_json(char *map, size_t size, jsmntok_t *tokens)
{
free(tokens);
unmapfile(map, size);
}
static int countchar(char *map, char c, int end)
{
int i;
int count = 0;
for (i = 0; i < end; i++)
if (map[i] == c)
count++;
return count;
}
/* Return line number of a jsmn token */
int json_line(char *map, jsmntok_t *t)
{
return countchar(map, '\n', t->start) + 1;
}
static const char * const jsmn_types[] = {
[JSMN_PRIMITIVE] = "primitive",
[JSMN_ARRAY] = "array",
[JSMN_OBJECT] = "object",
[JSMN_STRING] = "string"
};
#define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?")
/* Return type name of a jsmn token */
const char *json_name(jsmntok_t *t)
{
return LOOKUP(jsmn_types, t->type);
}
int json_len(jsmntok_t *t)
{
return t->end - t->start;
}
/* Is string t equal to s? */
int json_streq(char *map, jsmntok_t *t, const char *s)
{
unsigned len = json_len(t);
return len == strlen(s) && !strncasecmp(map + t->start, s, len);
}
#ifndef JSON_H
#define JSON_H 1
#include "jsmn.h"
jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len);
void free_json(char *map, size_t size, jsmntok_t *tokens);
int json_line(char *map, jsmntok_t *t);
const char *json_name(jsmntok_t *t);
int json_streq(char *map, jsmntok_t *t, const char *s);
int json_len(jsmntok_t *t);
extern int verbose;
#include <stdbool.h>
extern int eprintf(int level, int var, const char *fmt, ...);
#define pr_fmt(fmt) fmt
#define pr_err(fmt, ...) \
eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug(fmt, ...) \
eprintf(2, verbose, pr_fmt(fmt), ##__VA_ARGS__)
#ifndef roundup
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
#endif
#endif
#ifndef PMU_EVENTS_H
#define PMU_EVENTS_H
/*
* Describe each PMU event. Each CPU has a table of PMU events.
*/
struct pmu_event {
const char *name;
const char *event;
const char *desc;
const char *topic;
const char *long_desc;
};
/*
*
* Map a CPU to its table of PMU events. The CPU is identified by the
* cpuid field, which is an arch-specific identifier for the CPU.
* The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
* must match the get_cpustr() in tools/perf/arch/xxx/util/header.c)
*
* The cpuid can contain any character other than the comma.
*/
struct pmu_events_map {
const char *cpuid;
const char *version;
const char *type; /* core, uncore etc */
struct pmu_event *table;
};
/*
* Global table mapping each known CPU for the architecture to its
* table of PMU events.
*/
extern struct pmu_events_map pmu_events_map[];
#endif
...@@ -384,15 +384,14 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist) ...@@ -384,15 +384,14 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist)
static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
struct perf_evsel *evsel, int cpu) struct perf_evsel *evsel, int cpu)
{ {
int thread, err; int thread;
int nr_threads = perf_evlist__nr_threads(evlist, evsel); int nr_threads = perf_evlist__nr_threads(evlist, evsel);
if (!evsel->fd) if (!evsel->fd)
return -EINVAL; return -EINVAL;
for (thread = 0; thread < nr_threads; thread++) { for (thread = 0; thread < nr_threads; thread++) {
err = ioctl(FD(evsel, cpu, thread), int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0);
PERF_EVENT_IOC_ENABLE, 0);
if (err) if (err)
return err; return err;
} }
...@@ -403,14 +402,14 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, ...@@ -403,14 +402,14 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
struct perf_evsel *evsel, struct perf_evsel *evsel,
int thread) int thread)
{ {
int cpu, err; int cpu;
int nr_cpus = cpu_map__nr(evlist->cpus); int nr_cpus = cpu_map__nr(evlist->cpus);
if (!evsel->fd) if (!evsel->fd)
return -EINVAL; return -EINVAL;
for (cpu = 0; cpu < nr_cpus; cpu++) { for (cpu = 0; cpu < nr_cpus; cpu++) {
err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0);
if (err) if (err)
return err; return err;
} }
...@@ -1606,10 +1605,9 @@ void perf_evlist__close(struct perf_evlist *evlist) ...@@ -1606,10 +1605,9 @@ void perf_evlist__close(struct perf_evlist *evlist)
struct perf_evsel *evsel; struct perf_evsel *evsel;
int ncpus = cpu_map__nr(evlist->cpus); int ncpus = cpu_map__nr(evlist->cpus);
int nthreads = thread_map__nr(evlist->threads); int nthreads = thread_map__nr(evlist->threads);
int n;
evlist__for_each_entry_reverse(evlist, evsel) { evlist__for_each_entry_reverse(evlist, evsel) {
n = evsel->cpus ? evsel->cpus->nr : ncpus; int n = evsel->cpus ? evsel->cpus->nr : ncpus;
perf_evsel__close(evsel, n, nthreads); perf_evsel__close(evsel, n, nthreads);
} }
} }
......
...@@ -985,14 +985,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, ...@@ -985,14 +985,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
{ {
int cpu, thread;
if (evsel->system_wide) if (evsel->system_wide)
nthreads = 1; nthreads = 1;
evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
if (evsel->fd) { if (evsel->fd) {
int cpu, thread;
for (cpu = 0; cpu < ncpus; cpu++) { for (cpu = 0; cpu < ncpus; cpu++) {
for (thread = 0; thread < nthreads; thread++) { for (thread = 0; thread < nthreads; thread++) {
FD(evsel, cpu, thread) = -1; FD(evsel, cpu, thread) = -1;
......
...@@ -151,4 +151,5 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); ...@@ -151,4 +151,5 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned);
*/ */
int get_cpuid(char *buffer, size_t sz); int get_cpuid(char *buffer, size_t sz);
char *get_cpuid_str(void);
#endif /* __PERF_HEADER_H */ #endif /* __PERF_HEADER_H */
...@@ -90,6 +90,7 @@ struct intel_pt_decoder { ...@@ -90,6 +90,7 @@ struct intel_pt_decoder {
bool pge; bool pge;
bool have_tma; bool have_tma;
bool have_cyc; bool have_cyc;
bool fixup_last_mtc;
uint64_t pos; uint64_t pos;
uint64_t last_ip; uint64_t last_ip;
uint64_t ip; uint64_t ip;
...@@ -586,10 +587,31 @@ struct intel_pt_calc_cyc_to_tsc_info { ...@@ -586,10 +587,31 @@ struct intel_pt_calc_cyc_to_tsc_info {
uint64_t tsc_timestamp; uint64_t tsc_timestamp;
uint64_t timestamp; uint64_t timestamp;
bool have_tma; bool have_tma;
bool fixup_last_mtc;
bool from_mtc; bool from_mtc;
double cbr_cyc_to_tsc; double cbr_cyc_to_tsc;
}; };
/*
* MTC provides a 8-bit slice of CTC but the TMA packet only provides the lower
* 16 bits of CTC. If mtc_shift > 8 then some of the MTC bits are not in the CTC
* provided by the TMA packet. Fix-up the last_mtc calculated from the TMA
* packet by copying the missing bits from the current MTC assuming the least
* difference between the two, and that the current MTC comes after last_mtc.
*/
static void intel_pt_fixup_last_mtc(uint32_t mtc, int mtc_shift,
uint32_t *last_mtc)
{
uint32_t first_missing_bit = 1U << (16 - mtc_shift);
uint32_t mask = ~(first_missing_bit - 1);
*last_mtc |= mtc & mask;
if (*last_mtc >= mtc) {
*last_mtc -= first_missing_bit;
*last_mtc &= 0xff;
}
}
static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
{ {
struct intel_pt_decoder *decoder = pkt_info->decoder; 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,6 +641,11 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
return 0; return 0;
mtc = pkt_info->packet.payload; mtc = pkt_info->packet.payload;
if (decoder->mtc_shift > 8 && data->fixup_last_mtc) {
data->fixup_last_mtc = false;
intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
&data->last_mtc);
}
if (mtc > data->last_mtc) if (mtc > data->last_mtc)
mtc_delta = mtc - data->last_mtc; mtc_delta = mtc - data->last_mtc;
else else
...@@ -687,6 +714,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) ...@@ -687,6 +714,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
data->ctc_delta = 0; data->ctc_delta = 0;
data->have_tma = true; data->have_tma = true;
data->fixup_last_mtc = true;
return 0; return 0;
...@@ -753,6 +781,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder, ...@@ -753,6 +781,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder,
.tsc_timestamp = decoder->tsc_timestamp, .tsc_timestamp = decoder->tsc_timestamp,
.timestamp = decoder->timestamp, .timestamp = decoder->timestamp,
.have_tma = decoder->have_tma, .have_tma = decoder->have_tma,
.fixup_last_mtc = decoder->fixup_last_mtc,
.from_mtc = from_mtc, .from_mtc = from_mtc,
.cbr_cyc_to_tsc = 0, .cbr_cyc_to_tsc = 0,
}; };
...@@ -1271,6 +1300,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder) ...@@ -1271,6 +1300,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
} }
decoder->ctc_delta = 0; decoder->ctc_delta = 0;
decoder->have_tma = true; decoder->have_tma = true;
decoder->fixup_last_mtc = true;
intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n", intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n",
decoder->ctc_timestamp, decoder->last_mtc, ctc_rem); decoder->ctc_timestamp, decoder->last_mtc, ctc_rem);
} }
...@@ -1285,6 +1315,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder) ...@@ -1285,6 +1315,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
mtc = decoder->packet.payload; mtc = decoder->packet.payload;
if (decoder->mtc_shift > 8 && decoder->fixup_last_mtc) {
decoder->fixup_last_mtc = false;
intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
&decoder->last_mtc);
}
if (mtc > decoder->last_mtc) if (mtc > decoder->last_mtc)
mtc_delta = mtc - decoder->last_mtc; mtc_delta = mtc - decoder->last_mtc;
else else
...@@ -1353,6 +1389,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder) ...@@ -1353,6 +1389,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
timestamp, decoder->timestamp); timestamp, decoder->timestamp);
else else
decoder->timestamp = timestamp; decoder->timestamp = timestamp;
decoder->timestamp_insn_cnt = 0;
} }
/* Walk PSB+ packets when already in sync. */ /* Walk PSB+ packets when already in sync. */
......
...@@ -1745,9 +1745,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ...@@ -1745,9 +1745,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
int max_stack) int max_stack)
{ {
struct ip_callchain *chain = sample->callchain; struct ip_callchain *chain = sample->callchain;
int chain_nr = min(max_stack, (int)chain->nr); int chain_nr = min(max_stack, (int)chain->nr), i;
u8 cpumode = PERF_RECORD_MISC_USER; u8 cpumode = PERF_RECORD_MISC_USER;
int i, j, err;
u64 ip; u64 ip;
for (i = 0; i < chain_nr; i++) { for (i = 0; i < chain_nr; i++) {
...@@ -1758,7 +1757,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ...@@ -1758,7 +1757,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
/* LBR only affects the user callchain */ /* LBR only affects the user callchain */
if (i != chain_nr) { if (i != chain_nr) {
struct branch_stack *lbr_stack = sample->branch_stack; struct branch_stack *lbr_stack = sample->branch_stack;
int lbr_nr = lbr_stack->nr; int lbr_nr = lbr_stack->nr, j;
/* /*
* LBR callstack can only get user call chain. * LBR callstack can only get user call chain.
* The mix_chain_nr is kernel call chain * The mix_chain_nr is kernel call chain
...@@ -1772,6 +1771,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ...@@ -1772,6 +1771,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
int mix_chain_nr = i + 1 + lbr_nr + 1; int mix_chain_nr = i + 1 + lbr_nr + 1;
for (j = 0; j < mix_chain_nr; j++) { for (j = 0; j < mix_chain_nr; j++) {
int err;
if (callchain_param.order == ORDER_CALLEE) { if (callchain_param.order == ORDER_CALLEE) {
if (j < i + 1) if (j < i + 1)
ip = chain->ips[j]; ip = chain->ips[j];
......
...@@ -924,6 +924,7 @@ config_term_avail(int term_type, struct parse_events_error *err) ...@@ -924,6 +924,7 @@ config_term_avail(int term_type, struct parse_events_error *err)
case PARSE_EVENTS__TERM_TYPE_CONFIG1: case PARSE_EVENTS__TERM_TYPE_CONFIG1:
case PARSE_EVENTS__TERM_TYPE_CONFIG2: case PARSE_EVENTS__TERM_TYPE_CONFIG2:
case PARSE_EVENTS__TERM_TYPE_NAME: case PARSE_EVENTS__TERM_TYPE_NAME:
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
return true; return true;
default: default:
if (!err) if (!err)
...@@ -1458,7 +1459,7 @@ comp_pmu(const void *p1, const void *p2) ...@@ -1458,7 +1459,7 @@ comp_pmu(const void *p1, const void *p2)
struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
return strcmp(pmu1->symbol, pmu2->symbol); return strcasecmp(pmu1->symbol, pmu2->symbol);
} }
static void perf_pmu__parse_cleanup(void) static void perf_pmu__parse_cleanup(void)
...@@ -2263,7 +2264,8 @@ void print_symbol_events(const char *event_glob, unsigned type, ...@@ -2263,7 +2264,8 @@ void print_symbol_events(const char *event_glob, unsigned type,
/* /*
* Print the help text for the event symbols: * Print the help text for the event symbols:
*/ */
void print_events(const char *event_glob, bool name_only) void print_events(const char *event_glob, bool name_only, bool quiet_flag,
bool long_desc)
{ {
print_symbol_events(event_glob, PERF_TYPE_HARDWARE, print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX, name_only); event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
...@@ -2273,7 +2275,7 @@ void print_events(const char *event_glob, bool name_only) ...@@ -2273,7 +2275,7 @@ void print_events(const char *event_glob, bool name_only)
print_hwcache_events(event_glob, name_only); print_hwcache_events(event_glob, name_only);
print_pmu_events(event_glob, name_only); print_pmu_events(event_glob, name_only, quiet_flag, long_desc);
if (event_glob != NULL) if (event_glob != NULL)
return; return;
......
...@@ -172,7 +172,8 @@ void parse_events_update_lists(struct list_head *list_event, ...@@ -172,7 +172,8 @@ void parse_events_update_lists(struct list_head *list_event,
void parse_events_evlist_error(struct parse_events_evlist *data, void parse_events_evlist_error(struct parse_events_evlist *data,
int idx, const char *str); int idx, const char *str);
void print_events(const char *event_glob, bool name_only); void print_events(const char *event_glob, bool name_only, bool quiet,
bool long_desc);
struct event_symbol { struct event_symbol {
const char *symbol; const char *symbol;
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "pmu.h" #include "pmu.h"
#include "parse-events.h" #include "parse-events.h"
#include "cpumap.h" #include "cpumap.h"
#include "header.h"
#include "pmu-events/pmu-events.h"
#include "cache.h"
struct perf_pmu_format { struct perf_pmu_format {
char *name; char *name;
...@@ -220,7 +223,8 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, ...@@ -220,7 +223,8 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
} }
static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
char *desc __maybe_unused, char *val) char *desc, char *val, char *long_desc,
char *topic)
{ {
struct perf_pmu_alias *alias; struct perf_pmu_alias *alias;
int ret; int ret;
...@@ -253,6 +257,11 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, ...@@ -253,6 +257,11 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
perf_pmu__parse_snapshot(alias, dir, name); perf_pmu__parse_snapshot(alias, dir, name);
} }
alias->desc = desc ? strdup(desc) : NULL;
alias->long_desc = long_desc ? strdup(long_desc) :
desc ? strdup(desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
list_add_tail(&alias->list, list); list_add_tail(&alias->list, list);
return 0; return 0;
...@@ -269,7 +278,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI ...@@ -269,7 +278,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
buf[ret] = 0; buf[ret] = 0;
return __perf_pmu__new_alias(list, dir, name, NULL, buf); return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL);
} }
static inline bool pmu_alias_info_file(char *name) static inline bool pmu_alias_info_file(char *name)
...@@ -473,6 +482,68 @@ static struct cpu_map *pmu_cpumask(const char *name) ...@@ -473,6 +482,68 @@ static struct cpu_map *pmu_cpumask(const char *name)
return cpus; return cpus;
} }
/*
* Return the CPU id as a raw string.
*
* Each architecture should provide a more precise id string that
* can be use to match the architecture's "mapfile".
*/
char * __weak get_cpuid_str(void)
{
return NULL;
}
/*
* From the pmu_events_map, find the table of PMU events that corresponds
* to the current running CPU. Then, add all PMU events from that table
* as aliases.
*/
static void pmu_add_cpu_aliases(struct list_head *head)
{
int i;
struct pmu_events_map *map;
struct pmu_event *pe;
char *cpuid;
cpuid = getenv("PERF_CPUID");
if (cpuid)
cpuid = strdup(cpuid);
if (!cpuid)
cpuid = get_cpuid_str();
if (!cpuid)
return;
pr_debug("Using CPUID %s\n", cpuid);
i = 0;
while (1) {
map = &pmu_events_map[i++];
if (!map->table)
goto out;
if (!strcmp(map->cpuid, cpuid))
break;
}
/*
* Found a matching PMU events table. Create aliases
*/
i = 0;
while (1) {
pe = &map->table[i++];
if (!pe->name)
break;
/* need type casts to override 'const' */
__perf_pmu__new_alias(head, NULL, (char *)pe->name,
(char *)pe->desc, (char *)pe->event,
(char *)pe->long_desc, (char *)pe->topic);
}
out:
free(cpuid);
}
struct perf_event_attr * __weak struct perf_event_attr * __weak
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
{ {
...@@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name) ...@@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
if (pmu_aliases(name, &aliases)) if (pmu_aliases(name, &aliases))
return NULL; return NULL;
if (!strcmp(name, "cpu"))
pmu_add_cpu_aliases(&aliases);
if (pmu_type(name, &type)) if (pmu_type(name, &type))
return NULL; return NULL;
...@@ -983,21 +1057,63 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, ...@@ -983,21 +1057,63 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
return buf; return buf;
} }
static int cmp_string(const void *a, const void *b) struct sevent {
char *name;
char *desc;
char *topic;
};
static int cmp_sevent(const void *a, const void *b)
{ {
const char * const *as = a; const struct sevent *as = a;
const char * const *bs = b; const struct sevent *bs = b;
return strcmp(*as, *bs);
/* Put extra events last */
if (!!as->desc != !!bs->desc)
return !!as->desc - !!bs->desc;
if (as->topic && bs->topic) {
int n = strcmp(as->topic, bs->topic);
if (n)
return n;
}
return strcmp(as->name, bs->name);
} }
void print_pmu_events(const char *event_glob, bool name_only) static void wordwrap(char *s, int start, int max, int corr)
{
int column = start;
int n;
while (*s) {
int wlen = strcspn(s, " \t");
if (column + wlen >= max && column > start) {
printf("\n%*s", start, "");
column = start + corr;
}
n = printf("%s%.*s", column > start ? " " : "", wlen, s);
if (n <= 0)
break;
s += wlen;
column += n;
while (isspace(*s))
s++;
}
}
void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag,
bool long_desc)
{ {
struct perf_pmu *pmu; struct perf_pmu *pmu;
struct perf_pmu_alias *alias; struct perf_pmu_alias *alias;
char buf[1024]; char buf[1024];
int printed = 0; int printed = 0;
int len, j; int len, j;
char **aliases; struct sevent *aliases;
int numdesc = 0;
int columns = pager_get_columns();
char *topic = NULL;
pmu = NULL; pmu = NULL;
len = 0; len = 0;
...@@ -1007,14 +1123,15 @@ void print_pmu_events(const char *event_glob, bool name_only) ...@@ -1007,14 +1123,15 @@ void print_pmu_events(const char *event_glob, bool name_only)
if (pmu->selectable) if (pmu->selectable)
len++; len++;
} }
aliases = zalloc(sizeof(char *) * len); aliases = zalloc(sizeof(struct sevent) * len);
if (!aliases) if (!aliases)
goto out_enomem; goto out_enomem;
pmu = NULL; pmu = NULL;
j = 0; j = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) { while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) { list_for_each_entry(alias, &pmu->aliases, list) {
char *name = format_alias(buf, sizeof(buf), pmu, alias); char *name = alias->desc ? alias->name :
format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu"); bool is_cpu = !strcmp(pmu->name, "cpu");
if (event_glob != NULL && if (event_glob != NULL &&
...@@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only) ...@@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only)
event_glob)))) event_glob))))
continue; continue;
if (is_cpu && !name_only) if (is_cpu && !name_only && !alias->desc)
name = format_alias_or(buf, sizeof(buf), pmu, alias); name = format_alias_or(buf, sizeof(buf), pmu, alias);
aliases[j] = strdup(name); aliases[j].name = name;
if (aliases[j] == NULL) if (is_cpu && !name_only && !alias->desc)
aliases[j].name = format_alias_or(buf,
sizeof(buf),
pmu, alias);
aliases[j].name = strdup(aliases[j].name);
if (!aliases[j].name)
goto out_enomem; goto out_enomem;
aliases[j].desc = long_desc ? alias->long_desc :
alias->desc;
aliases[j].topic = alias->topic;
j++; j++;
} }
if (pmu->selectable && if (pmu->selectable &&
...@@ -1036,25 +1162,39 @@ void print_pmu_events(const char *event_glob, bool name_only) ...@@ -1036,25 +1162,39 @@ void print_pmu_events(const char *event_glob, bool name_only)
char *s; char *s;
if (asprintf(&s, "%s//", pmu->name) < 0) if (asprintf(&s, "%s//", pmu->name) < 0)
goto out_enomem; goto out_enomem;
aliases[j] = s; aliases[j].name = s;
j++; j++;
} }
} }
len = j; len = j;
qsort(aliases, len, sizeof(char *), cmp_string); qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
if (name_only) { if (name_only) {
printf("%s ", aliases[j]); printf("%s ", aliases[j].name);
continue; continue;
} }
printf(" %-50s [Kernel PMU event]\n", aliases[j]); if (aliases[j].desc && !quiet_flag) {
if (numdesc++ == 0)
printf("\n");
if (aliases[j].topic && (!topic ||
strcmp(topic, aliases[j].topic))) {
printf("%s%s:\n", topic ? "\n" : "",
aliases[j].topic);
topic = aliases[j].topic;
}
printf(" %-50s\n", aliases[j].name);
printf("%*s", 8, "[");
wordwrap(aliases[j].desc, 8, columns, 0);
printf("]\n");
} else
printf(" %-50s [Kernel PMU event]\n", aliases[j].name);
printed++; printed++;
} }
if (printed && pager_in_use()) if (printed && pager_in_use())
printf("\n"); printf("\n");
out_free: out_free:
for (j = 0; j < len; j++) for (j = 0; j < len; j++)
zfree(&aliases[j]); zfree(&aliases[j].name);
zfree(&aliases); zfree(&aliases);
return; return;
......
...@@ -40,6 +40,9 @@ struct perf_pmu_info { ...@@ -40,6 +40,9 @@ struct perf_pmu_info {
struct perf_pmu_alias { struct perf_pmu_alias {
char *name; char *name;
char *desc;
char *long_desc;
char *topic;
struct list_head terms; /* HEAD struct parse_events_term -> list */ struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */ struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1]; char unit[UNIT_MAX_LEN+1];
...@@ -71,7 +74,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); ...@@ -71,7 +74,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
void print_pmu_events(const char *event_glob, bool name_only); void print_pmu_events(const char *event_glob, bool name_only, bool quiet,
bool long_desc);
bool pmu_have_event(const char *pname, const char *name); bool pmu_have_event(const char *pname, const char *name);
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
......
...@@ -213,7 +213,7 @@ static int convert_exec_to_group(const char *exec, char **result) ...@@ -213,7 +213,7 @@ static int convert_exec_to_group(const char *exec, char **result)
goto out; goto out;
} }
for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) { for (ptr2 = ptr1; *ptr2 != '\0'; ptr2++) {
if (!isalnum(*ptr2) && *ptr2 != '_') { if (!isalnum(*ptr2) && *ptr2 != '_') {
*ptr2 = '\0'; *ptr2 = '\0';
break; break;
......
...@@ -66,9 +66,8 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) { ...@@ -66,9 +66,8 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) {
int strbuf_grow(struct strbuf *buf, size_t); int strbuf_grow(struct strbuf *buf, size_t);
static inline int strbuf_setlen(struct strbuf *sb, size_t len) { static inline int strbuf_setlen(struct strbuf *sb, size_t len) {
int ret;
if (!sb->alloc) { if (!sb->alloc) {
ret = strbuf_grow(sb, 0); int ret = strbuf_grow(sb, 0);
if (ret) if (ret)
return ret; return ret;
} }
......
...@@ -14,13 +14,12 @@ ...@@ -14,13 +14,12 @@
int thread__init_map_groups(struct thread *thread, struct machine *machine) int thread__init_map_groups(struct thread *thread, struct machine *machine)
{ {
struct thread *leader;
pid_t pid = thread->pid_; pid_t pid = thread->pid_;
if (pid == thread->tid || pid == -1) { if (pid == thread->tid || pid == -1) {
thread->mg = map_groups__new(machine); thread->mg = map_groups__new(machine);
} else { } else {
leader = __machine__findnew_thread(machine, pid, pid); struct thread *leader = __machine__findnew_thread(machine, pid, pid);
if (leader) { if (leader) {
thread->mg = map_groups__get(leader->mg); thread->mg = map_groups__get(leader->mg);
thread__put(leader); thread__put(leader);
...@@ -130,11 +129,10 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, ...@@ -130,11 +129,10 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
bool exec) bool exec)
{ {
struct comm *new, *curr = thread__comm(thread); struct comm *new, *curr = thread__comm(thread);
int err;
/* Override the default :tid entry */ /* Override the default :tid entry */
if (!thread->comm_set) { if (!thread->comm_set) {
err = comm__override(curr, str, timestamp, exec); int err = comm__override(curr, str, timestamp, exec);
if (err) if (err)
return err; return err;
} else { } else {
...@@ -270,10 +268,9 @@ static int thread__clone_map_groups(struct thread *thread, ...@@ -270,10 +268,9 @@ static int thread__clone_map_groups(struct thread *thread,
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
{ {
int err;
if (parent->comm_set) { if (parent->comm_set) {
const char *comm = thread__comm_str(parent); const char *comm = thread__comm_str(parent);
int err;
if (!comm) if (!comm)
return -ENOMEM; return -ENOMEM;
err = thread__set_comm(thread, comm, timestamp); err = thread__set_comm(thread, comm, timestamp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment