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 ...
This commit is contained in:
commit
c48ce9f190
|
@ -106,7 +106,6 @@
|
|||
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */
|
||||
#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_MCE_RECOVERY ( 3*32+31) /* cpu has recoverable machine checks */
|
||||
|
||||
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
|
||||
#define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */
|
||||
|
|
|
@ -181,11 +181,11 @@ ENDPROC(memcpy_orig)
|
|||
|
||||
#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.
|
||||
* Writes to target are posted and don't generate machine checks.
|
||||
*/
|
||||
ENTRY(memcpy_mcsafe)
|
||||
ENTRY(memcpy_mcsafe_unrolled)
|
||||
cmpl $8, %edx
|
||||
/* Less than 8 bytes? Go to byte copy loop */
|
||||
jb .L_no_whole_words
|
||||
|
@ -273,7 +273,7 @@ ENTRY(memcpy_mcsafe)
|
|||
.L_done_memcpy_trap:
|
||||
xorq %rax, %rax
|
||||
ret
|
||||
ENDPROC(memcpy_mcsafe)
|
||||
ENDPROC(memcpy_mcsafe_unrolled)
|
||||
|
||||
.section .fixup, "ax"
|
||||
/* Return -EFAULT for any failure */
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
hostprogs := fixdep
|
||||
|
||||
fixdep-y := fixdep.o
|
||||
|
|
|
@ -90,3 +90,9 @@ if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
|
|||
# - per object C flags
|
||||
# - 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))
|
||||
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
|
|||
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
|
||||
$(call allow-override,LD,$(CROSS_COMPILE)ld)
|
||||
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
HOSTAR ?= ar
|
||||
|
||||
export HOSTCC HOSTLD HOSTAR
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
|
@ -36,7 +42,7 @@ $(OUTPUT)fixdep-in.o: FORCE
|
|||
$(Q)$(MAKE) $(build)=fixdep
|
||||
|
||||
$(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o
|
||||
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $<
|
||||
$(QUIET_LINK)$(HOSTCC) $(LDFLAGS) -o $@ $<
|
||||
|
||||
FORCE:
|
||||
|
||||
|
|
|
@ -58,6 +58,12 @@ quiet_cmd_mkdir = MKDIR $(dir $@)
|
|||
quiet_cmd_cc_o_c = CC $@
|
||||
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 $@
|
||||
cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $<
|
||||
|
||||
|
@ -70,16 +76,28 @@ quiet_cmd_gen = GEN $@
|
|||
# If there's nothing to link, create empty $@ object.
|
||||
quiet_cmd_ld_multi = LD $@
|
||||
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
|
||||
$(OUTPUT)%.o: %.c FORCE
|
||||
$(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
|
||||
$(call rule_mkdir)
|
||||
$(call if_changed_dep,cc_o_c)
|
||||
$(call if_changed_dep,$(host)cc_o_c)
|
||||
|
||||
$(OUTPUT)%.i: %.c FORCE
|
||||
$(call rule_mkdir)
|
||||
|
@ -119,7 +137,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ;
|
|||
|
||||
$(in-target): $(obj-y) FORCE
|
||||
$(call rule_mkdir)
|
||||
$(call if_changed,ld_multi)
|
||||
$(call if_changed,$(host)ld_multi)
|
||||
|
||||
__build: $(in-target)
|
||||
@:
|
||||
|
|
|
@ -7,7 +7,7 @@ endif
|
|||
|
||||
feature_check = $(eval $(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
|
||||
|
||||
feature_set = $(eval $(feature_set_code))
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
|
||||
|
||||
ifdef CROSS_COMPILE
|
||||
fixdep:
|
||||
else
|
||||
fixdep:
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
|
||||
endif
|
||||
|
||||
.PHONY: fixdep
|
||||
|
|
|
@ -46,11 +46,13 @@ FILES= \
|
|||
test-lzma.bin \
|
||||
test-bpf.bin \
|
||||
test-get_cpuid.bin \
|
||||
test-sdt.bin
|
||||
test-sdt.bin \
|
||||
test-cxx.bin
|
||||
|
||||
FILES := $(addprefix $(OUTPUT),$(FILES))
|
||||
|
||||
CC := $(CROSS_COMPILE)gcc -MD
|
||||
CXX := $(CROSS_COMPILE)g++ -MD
|
||||
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
|
||||
|
||||
all: $(FILES)
|
||||
|
@ -58,6 +60,9 @@ all: $(FILES)
|
|||
__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS)
|
||||
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:
|
||||
|
@ -217,6 +222,9 @@ $(OUTPUT)test-bpf.bin:
|
|||
$(OUTPUT)test-sdt.bin:
|
||||
$(BUILD)
|
||||
|
||||
$(OUTPUT)test-cxx.bin:
|
||||
$(BUILDXX) -std=gnu++11
|
||||
|
||||
-include $(OUTPUT)*.d
|
||||
|
||||
###############################
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#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 {
|
|||
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
|
||||
* @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type
|
||||
* @index: index of the cgroup in the bpf_map
|
||||
|
@ -348,7 +348,7 @@ enum bpf_func_id {
|
|||
* == 1 skb succeeded the cgroup2 descendant test
|
||||
* < 0 error
|
||||
*/
|
||||
BPF_FUNC_skb_in_cgroup,
|
||||
BPF_FUNC_skb_under_cgroup,
|
||||
|
||||
/**
|
||||
* bpf_get_hash_recalc(skb)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "pager.h"
|
||||
#include "run-command.h"
|
||||
#include "sigchain.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
*/
|
||||
|
||||
static int spawned_pager;
|
||||
static int pager_columns;
|
||||
|
||||
void pager_init(const char *pager_env)
|
||||
{
|
||||
|
@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo)
|
|||
void setup_pager(void)
|
||||
{
|
||||
const char *pager = getenv(subcmd_config.pager_env);
|
||||
struct winsize sz;
|
||||
|
||||
if (!isatty(1))
|
||||
return;
|
||||
if (ioctl(1, TIOCGWINSZ, &sz) == 0)
|
||||
pager_columns = sz.ws_col;
|
||||
if (!pager)
|
||||
pager = getenv("PAGER");
|
||||
if (!(pager || access("/usr/bin/pager", X_OK)))
|
||||
|
@ -98,3 +103,14 @@ int pager_in_use(void)
|
|||
{
|
||||
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);
|
|||
|
||||
extern void setup_pager(void);
|
||||
extern int pager_in_use(void);
|
||||
extern int pager_get_columns(void);
|
||||
|
||||
#endif /* __SUBCMD_PAGER_H */
|
||||
|
|
|
@ -622,6 +622,7 @@ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset,
|
|||
|
||||
/* Reset the buffer */
|
||||
kbuffer_load_subbuffer(kbuf, kbuf->subbuffer);
|
||||
data = kbuffer_read_event(kbuf, ts);
|
||||
|
||||
while (kbuf->curr < offset) {
|
||||
data = kbuffer_next_event(kbuf, ts);
|
||||
|
|
|
@ -8,13 +8,23 @@ perf-list - List all symbolic event types
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf list' [hw|sw|cache|tracepoint|pmu|event_glob]
|
||||
'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|event_glob]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the symbolic event types which can be selected in the
|
||||
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
|
||||
---------------
|
||||
|
|
|
@ -28,3 +28,7 @@ To change sampling frequency to 100 Hz: perf record -F 100
|
|||
See assembly instructions with percentage: perf annotate <symbol>
|
||||
If you prefer Intel style assembly, try: perf annotate -M intel
|
||||
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)
|
|||
|
||||
LD += $(EXTRA_LDFLAGS)
|
||||
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
HOSTAR ?= ar
|
||||
|
||||
PKG_CONFIG = $(CROSS_COMPILE)pkg-config
|
||||
|
||||
RM = rm -f
|
||||
|
@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf
|
|||
PERF_IN := $(OUTPUT)perf-in.o
|
||||
|
||||
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
|
||||
export HOSTCC HOSTLD HOSTAR
|
||||
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
|
||||
@(test -f ../../include/uapi/linux/perf_event.h && ( \
|
||||
(diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \
|
||||
|
@ -443,9 +457,18 @@ $(PERF_IN): prepare FORCE
|
|||
|| echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true
|
||||
$(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) \
|
||||
$(PERF_IN) $(LIBS) -o $@
|
||||
$(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@
|
||||
|
||||
$(GTK_IN): fixdep FORCE
|
||||
$(Q)$(MAKE) $(build)=gtk
|
||||
|
@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \
|
|||
ifneq ($(OUTPUT),)
|
||||
%.o: $(OUTPUT)%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
pmu-events/%.o: $(OUTPUT)pmu-events/%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
util/%.o: $(OUTPUT)util/%.o
|
||||
@echo " # Redirected target $@ => $(OUTPUT)$@"
|
||||
bench/%.o: $(OUTPUT)bench/%.o
|
||||
|
@ -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)
|
||||
$(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(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* \
|
||||
$(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
|
||||
$(python-clean)
|
||||
|
||||
|
|
|
@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz)
|
|||
}
|
||||
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,
|
|||
*
|
||||
* 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;
|
||||
|
||||
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,
|
|||
: "a" (op));
|
||||
}
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
static int
|
||||
__get_cpuid(char *buffer, size_t sz, const char *fmt)
|
||||
{
|
||||
unsigned int a, b, c, d, lvl;
|
||||
int family = -1, model = -1, step = -1;
|
||||
|
@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz)
|
|||
if (family >= 0x6)
|
||||
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 */
|
||||
if (strchr(buffer, '$')) {
|
||||
|
@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz)
|
|||
}
|
||||
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 @@
|
|||
#include "util/pmu.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 i;
|
||||
bool raw_dump = false;
|
||||
bool long_desc_flag = false;
|
||||
struct option list_options[] = {
|
||||
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()
|
||||
};
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
|
||||
if (argc == 0) {
|
||||
print_events(NULL, raw_dump);
|
||||
print_events(NULL, raw_dump, !desc_flag, long_desc_flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -61,14 +68,16 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
strcmp(argv[i], "hwcache") == 0)
|
||||
print_hwcache_events(NULL, raw_dump);
|
||||
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)
|
||||
print_sdt_events(NULL, NULL, raw_dump);
|
||||
else if ((sep = strchr(argv[i], ':')) != NULL) {
|
||||
int sep_idx;
|
||||
|
||||
if (sep == NULL) {
|
||||
print_events(argv[i], raw_dump);
|
||||
print_events(argv[i], raw_dump, !desc_flag,
|
||||
long_desc_flag);
|
||||
continue;
|
||||
}
|
||||
sep_idx = sep - argv[i];
|
||||
|
@ -90,7 +99,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
print_symbol_events(s, PERF_TYPE_SOFTWARE,
|
||||
event_symbols_sw, PERF_COUNT_SW_MAX, 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_sdt_events(NULL, s, raw_dump);
|
||||
free(s);
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
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)
|
|
@ -0,0 +1,147 @@
|
|||
|
||||
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'.
|
|
@ -0,0 +1,814 @@
|
|||
#define _XOPEN_SOURCE 500 /* needed for nftw() */
|
||||
#define _GNU_SOURCE /* needed for asprintf() */
|
||||
|
||||
/* Parse event JSON files */
|
||||
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/time.h> /* getrlimit */
|
||||
#include <sys/resource.h> /* getrlimit */
|
||||
#include <ftw.h>
|
||||
#include <sys/stat.h>
|
||||
#include "jsmn.h"
|
||||
#include "json.h"
|
||||
#include "jevents.h"
|
||||
|
||||
#ifndef __maybe_unused
|
||||
#define __maybe_unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
int verbose;
|
||||
char *prog;
|
||||
|
||||
int eprintf(int level, int var, const char *fmt, ...)
|
||||
{
|
||||
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
if (var < level)
|
||||
return 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
ret = vfprintf(stderr, fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((weak)) char *get_cpu_str(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void addfield(char *map, char **dst, const char *sep,
|
||||
const char *a, jsmntok_t *bt)
|
||||
{
|
||||
unsigned int len = strlen(a) + 1 + strlen(sep);
|
||||
int olen = *dst ? strlen(*dst) : 0;
|
||||
int blen = bt ? json_len(bt) : 0;
|
||||
char *out;
|
||||
|
||||
out = realloc(*dst, len + olen + blen);
|
||||
if (!out) {
|
||||
/* Don't add field in this case */
|
||||
return;
|
||||
}
|
||||
*dst = out;
|
||||
|
||||
if (!olen)
|
||||
*(*dst) = 0;
|
||||
else
|
||||
strcat(*dst, sep);
|
||||
strcat(*dst, a);
|
||||
if (bt)
|
||||
strncat(*dst, map + bt->start, blen);
|
||||
}
|
||||
|
||||
static void fixname(char *s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
*s = tolower(*s);
|
||||
}
|
||||
|
||||
static void fixdesc(char *s)
|
||||
{
|
||||
char *e = s + strlen(s);
|
||||
|
||||
/* Remove trailing dots that look ugly in perf list */
|
||||
--e;
|
||||
while (e >= s && isspace(*e))
|
||||
--e;
|
||||
if (*e == '.')
|
||||
*e = 0;
|
||||
}
|
||||
|
||||
static struct msrmap {
|
||||
const char *num;
|
||||
const char *pname;
|
||||
} msrmap[] = {
|
||||
{ "0x3F6", "ldlat=" },
|
||||
{ "0x1A6", "offcore_rsp=" },
|
||||
{ "0x1A7", "offcore_rsp=" },
|
||||
{ "0x3F7", "frontend=" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static struct field {
|
||||
const char *field;
|
||||
const char *kernel;
|
||||
} fields[] = {
|
||||
{ "EventCode", "event=" },
|
||||
{ "UMask", "umask=" },
|
||||
{ "CounterMask", "cmask=" },
|
||||
{ "Invert", "inv=" },
|
||||
{ "AnyThread", "any=" },
|
||||
{ "EdgeDetect", "edge=" },
|
||||
{ "SampleAfterValue", "period=" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void cut_comma(char *map, jsmntok_t *newval)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Cut off everything after comma */
|
||||
for (i = newval->start; i < newval->end; i++) {
|
||||
if (map[i] == ',')
|
||||
newval->end = i;
|
||||
}
|
||||
}
|
||||
|
||||
static int match_field(char *map, jsmntok_t *field, int nz,
|
||||
char **event, jsmntok_t *val)
|
||||
{
|
||||
struct field *f;
|
||||
jsmntok_t newval = *val;
|
||||
|
||||
for (f = fields; f->field; f++)
|
||||
if (json_streq(map, field, f->field) && nz) {
|
||||
cut_comma(map, &newval);
|
||||
addfield(map, event, ",", f->kernel, &newval);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct msrmap *lookup_msr(char *map, jsmntok_t *val)
|
||||
{
|
||||
jsmntok_t newval = *val;
|
||||
static bool warned;
|
||||
int i;
|
||||
|
||||
cut_comma(map, &newval);
|
||||
for (i = 0; msrmap[i].num; i++)
|
||||
if (json_streq(map, &newval, msrmap[i].num))
|
||||
return &msrmap[i];
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
pr_err("%s: Unknown MSR in event file %.*s\n", prog,
|
||||
json_len(val), map + val->start);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define EXPECT(e, t, m) do { if (!(e)) { \
|
||||
jsmntok_t *loc = (t); \
|
||||
if (!(t)->start && (t) > tokens) \
|
||||
loc = (t) - 1; \
|
||||
pr_err("%s:%d: " m ", got %s\n", fn, \
|
||||
json_line(map, loc), \
|
||||
json_name(t)); \
|
||||
goto out_free; \
|
||||
} } while (0)
|
||||
|
||||
#define TOPIC_DEPTH 256
|
||||
static char *topic_array[TOPIC_DEPTH];
|
||||
static int topic_level;
|
||||
|
||||
static char *get_topic(void)
|
||||
{
|
||||
char *tp_old, *tp = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < topic_level + 1; i++) {
|
||||
int n;
|
||||
|
||||
tp_old = tp;
|
||||
n = asprintf(&tp, "%s%s", tp ?: "", topic_array[i]);
|
||||
if (n < 0) {
|
||||
pr_info("%s: asprintf() error %s\n", prog);
|
||||
return NULL;
|
||||
}
|
||||
free(tp_old);
|
||||
}
|
||||
|
||||
for (i = 0; i < (int) strlen(tp); i++) {
|
||||
char c = tp[i];
|
||||
|
||||
if (c == '-')
|
||||
tp[i] = ' ';
|
||||
else if (c == '.') {
|
||||
tp[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
||||
static int add_topic(int level, char *bname)
|
||||
{
|
||||
char *topic;
|
||||
|
||||
level -= 2;
|
||||
|
||||
if (level >= TOPIC_DEPTH)
|
||||
return -EINVAL;
|
||||
|
||||
topic = strdup(bname);
|
||||
if (!topic) {
|
||||
pr_info("%s: strdup() error %s for file %s\n", prog,
|
||||
strerror(errno), bname);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free(topic_array[topic_level]);
|
||||
topic_array[topic_level] = topic;
|
||||
topic_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct perf_entry_data {
|
||||
FILE *outfp;
|
||||
char *topic;
|
||||
};
|
||||
|
||||
static int close_table;
|
||||
|
||||
static void print_events_table_prefix(FILE *fp, const char *tblname)
|
||||
{
|
||||
fprintf(fp, "struct pmu_event %s[] = {\n", tblname);
|
||||
close_table = 1;
|
||||
}
|
||||
|
||||
static int print_events_table_entry(void *data, char *name, char *event,
|
||||
char *desc, char *long_desc)
|
||||
{
|
||||
struct perf_entry_data *pd = data;
|
||||
FILE *outfp = pd->outfp;
|
||||
char *topic = pd->topic;
|
||||
|
||||
/*
|
||||
* TODO: Remove formatting chars after debugging to reduce
|
||||
* string lengths.
|
||||
*/
|
||||
fprintf(outfp, "{\n");
|
||||
|
||||
fprintf(outfp, "\t.name = \"%s\",\n", name);
|
||||
fprintf(outfp, "\t.event = \"%s\",\n", event);
|
||||
fprintf(outfp, "\t.desc = \"%s\",\n", desc);
|
||||
fprintf(outfp, "\t.topic = \"%s\",\n", topic);
|
||||
if (long_desc && long_desc[0])
|
||||
fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc);
|
||||
|
||||
fprintf(outfp, "},\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_events_table_suffix(FILE *outfp)
|
||||
{
|
||||
fprintf(outfp, "{\n");
|
||||
|
||||
fprintf(outfp, "\t.name = 0,\n");
|
||||
fprintf(outfp, "\t.event = 0,\n");
|
||||
fprintf(outfp, "\t.desc = 0,\n");
|
||||
|
||||
fprintf(outfp, "},\n");
|
||||
fprintf(outfp, "};\n");
|
||||
close_table = 0;
|
||||
}
|
||||
|
||||
static struct fixed {
|
||||
const char *name;
|
||||
const char *event;
|
||||
} fixed[] = {
|
||||
{ "inst_retired.any", "event=0xc0" },
|
||||
{ "inst_retired.any_p", "event=0xc0" },
|
||||
{ "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" },
|
||||
{ "cpu_clk_unhalted.thread", "event=0x3c" },
|
||||
{ "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" },
|
||||
{ NULL, NULL},
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle different fixed counter encodings between JSON and perf.
|
||||
*/
|
||||
static char *real_event(const char *name, char *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; fixed[i].name; i++)
|
||||
if (!strcasecmp(name, fixed[i].name))
|
||||
return (char *)fixed[i].event;
|
||||
return event;
|
||||
}
|
||||
|
||||
/* Call func with each event in the json file */
|
||||
int json_events(const char *fn,
|
||||
int (*func)(void *data, char *name, char *event, char *desc,
|
||||
char *long_desc),
|
||||
void *data)
|
||||
{
|
||||
int err = -EIO;
|
||||
size_t size;
|
||||
jsmntok_t *tokens, *tok;
|
||||
int i, j, len;
|
||||
char *map;
|
||||
|
||||
if (!fn)
|
||||
return -ENOENT;
|
||||
|
||||
tokens = parse_json(fn, &map, &size, &len);
|
||||
if (!tokens)
|
||||
return -EIO;
|
||||
EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array");
|
||||
tok = tokens + 1;
|
||||
for (i = 0; i < tokens->size; i++) {
|
||||
char *event = NULL, *desc = NULL, *name = NULL;
|
||||
char *long_desc = NULL;
|
||||
char *extra_desc = NULL;
|
||||
struct msrmap *msr = NULL;
|
||||
jsmntok_t *msrval = NULL;
|
||||
jsmntok_t *precise = NULL;
|
||||
jsmntok_t *obj = tok++;
|
||||
|
||||
EXPECT(obj->type == JSMN_OBJECT, obj, "expected object");
|
||||
for (j = 0; j < obj->size; j += 2) {
|
||||
jsmntok_t *field, *val;
|
||||
int nz;
|
||||
|
||||
field = tok + j;
|
||||
EXPECT(field->type == JSMN_STRING, tok + j,
|
||||
"Expected field name");
|
||||
val = tok + j + 1;
|
||||
EXPECT(val->type == JSMN_STRING, tok + j + 1,
|
||||
"Expected string value");
|
||||
|
||||
nz = !json_streq(map, val, "0");
|
||||
if (match_field(map, field, nz, &event, val)) {
|
||||
/* ok */
|
||||
} else if (json_streq(map, field, "EventName")) {
|
||||
addfield(map, &name, "", "", val);
|
||||
} else if (json_streq(map, field, "BriefDescription")) {
|
||||
addfield(map, &desc, "", "", val);
|
||||
fixdesc(desc);
|
||||
} else if (json_streq(map, field,
|
||||
"PublicDescription")) {
|
||||
addfield(map, &long_desc, "", "", val);
|
||||
fixdesc(long_desc);
|
||||
} else if (json_streq(map, field, "PEBS") && nz) {
|
||||
precise = val;
|
||||
} else if (json_streq(map, field, "MSRIndex") && nz) {
|
||||
msr = lookup_msr(map, val);
|
||||
} else if (json_streq(map, field, "MSRValue")) {
|
||||
msrval = val;
|
||||
} else if (json_streq(map, field, "Errata") &&
|
||||
!json_streq(map, val, "null")) {
|
||||
addfield(map, &extra_desc, ". ",
|
||||
" Spec update: ", val);
|
||||
} else if (json_streq(map, field, "Data_LA") && nz) {
|
||||
addfield(map, &extra_desc, ". ",
|
||||
" Supports address when precise",
|
||||
NULL);
|
||||
}
|
||||
/* ignore unknown fields */
|
||||
}
|
||||
if (precise && desc && !strstr(desc, "(Precise Event)")) {
|
||||
if (json_streq(map, precise, "2"))
|
||||
addfield(map, &extra_desc, " ",
|
||||
"(Must be precise)", NULL);
|
||||
else
|
||||
addfield(map, &extra_desc, " ",
|
||||
"(Precise event)", NULL);
|
||||
}
|
||||
if (desc && extra_desc)
|
||||
addfield(map, &desc, " ", extra_desc, NULL);
|
||||
if (long_desc && extra_desc)
|
||||
addfield(map, &long_desc, " ", extra_desc, NULL);
|
||||
if (msr != NULL)
|
||||
addfield(map, &event, ",", msr->pname, msrval);
|
||||
fixname(name);
|
||||
|
||||
err = func(data, name, real_event(name, event), desc, long_desc);
|
||||
free(event);
|
||||
free(desc);
|
||||
free(name);
|
||||
free(long_desc);
|
||||
free(extra_desc);
|
||||
if (err)
|
||||
break;
|
||||
tok += j;
|
||||
}
|
||||
EXPECT(tok - tokens == len, tok, "unexpected objects at end");
|
||||
err = 0;
|
||||
out_free:
|
||||
free_json(map, size, tokens);
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *file_name_to_table_name(char *fname)
|
||||
{
|
||||
unsigned int i;
|
||||
int n;
|
||||
int c;
|
||||
char *tblname;
|
||||
|
||||
/*
|
||||
* Ensure tablename starts with alphabetic character.
|
||||
* Derive rest of table name from basename of the JSON file,
|
||||
* replacing hyphens and stripping out .json suffix.
|
||||
*/
|
||||
n = asprintf(&tblname, "pme_%s", basename(fname));
|
||||
if (n < 0) {
|
||||
pr_info("%s: asprintf() error %s for file %s\n", prog,
|
||||
strerror(errno), fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlen(tblname); i++) {
|
||||
c = tblname[i];
|
||||
|
||||
if (c == '-')
|
||||
tblname[i] = '_';
|
||||
else if (c == '.') {
|
||||
tblname[i] = '\0';
|
||||
break;
|
||||
} else if (!isalnum(c) && c != '_') {
|
||||
pr_err("%s: Invalid character '%c' in file name %s\n",
|
||||
prog, c, basename(fname));
|
||||
free(tblname);
|
||||
tblname = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tblname;
|
||||
}
|
||||
|
||||
static void print_mapping_table_prefix(FILE *outfp)
|
||||
{
|
||||
fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n");
|
||||
}
|
||||
|
||||
static void print_mapping_table_suffix(FILE *outfp)
|
||||
{
|
||||
/*
|
||||
* Print the terminating, NULL entry.
|
||||
*/
|
||||
fprintf(outfp, "{\n");
|
||||
fprintf(outfp, "\t.cpuid = 0,\n");
|
||||
fprintf(outfp, "\t.version = 0,\n");
|
||||
fprintf(outfp, "\t.type = 0,\n");
|
||||
fprintf(outfp, "\t.table = 0,\n");
|
||||
fprintf(outfp, "},\n");
|
||||
|
||||
/* and finally, the closing curly bracket for the struct */
|
||||
fprintf(outfp, "};\n");
|
||||
}
|
||||
|
||||
static int process_mapfile(FILE *outfp, char *fpath)
|
||||
{
|
||||
int n = 16384;
|
||||
FILE *mapfp;
|
||||
char *save = NULL;
|
||||
char *line, *p;
|
||||
int line_num;
|
||||
char *tblname;
|
||||
|
||||
pr_info("%s: Processing mapfile %s\n", prog, fpath);
|
||||
|
||||
line = malloc(n);
|
||||
if (!line)
|
||||
return -1;
|
||||
|
||||
mapfp = fopen(fpath, "r");
|
||||
if (!mapfp) {
|
||||
pr_info("%s: Error %s opening %s\n", prog, strerror(errno),
|
||||
fpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_mapping_table_prefix(outfp);
|
||||
|
||||
/* Skip first line (header) */
|
||||
p = fgets(line, n, mapfp);
|
||||
if (!p)
|
||||
goto out;
|
||||
|
||||
line_num = 1;
|
||||
while (1) {
|
||||
char *cpuid, *version, *type, *fname;
|
||||
|
||||
line_num++;
|
||||
p = fgets(line, n, mapfp);
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
if (line[0] == '#' || line[0] == '\n')
|
||||
continue;
|
||||
|
||||
if (line[strlen(line)-1] != '\n') {
|
||||
/* TODO Deal with lines longer than 16K */
|
||||
pr_info("%s: Mapfile %s: line %d too long, aborting\n",
|
||||
prog, fpath, line_num);
|
||||
return -1;
|
||||
}
|
||||
line[strlen(line)-1] = '\0';
|
||||
|
||||
cpuid = strtok_r(p, ",", &save);
|
||||
version = strtok_r(NULL, ",", &save);
|
||||
fname = strtok_r(NULL, ",", &save);
|
||||
type = strtok_r(NULL, ",", &save);
|
||||
|
||||
tblname = file_name_to_table_name(fname);
|
||||
fprintf(outfp, "{\n");
|
||||
fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid);
|
||||
fprintf(outfp, "\t.version = \"%s\",\n", version);
|
||||
fprintf(outfp, "\t.type = \"%s\",\n", type);
|
||||
|
||||
/*
|
||||
* CHECK: We can't use the type (eg "core") field in the
|
||||
* table name. For us to do that, we need to somehow tweak
|
||||
* the other caller of file_name_to_table(), process_json()
|
||||
* to determine the type. process_json() file has no way
|
||||
* of knowing these are "core" events unless file name has
|
||||
* core in it. If filename has core in it, we can safely
|
||||
* ignore the type field here also.
|
||||
*/
|
||||
fprintf(outfp, "\t.table = %s\n", tblname);
|
||||
fprintf(outfp, "},\n");
|
||||
}
|
||||
|
||||
out:
|
||||
print_mapping_table_suffix(outfp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we fail to locate/process JSON and map files, create a NULL mapping
|
||||
* table. This would at least allow perf to build even if we can't find/use
|
||||
* the aliases.
|
||||
*/
|
||||
static void create_empty_mapping(const char *output_file)
|
||||
{
|
||||
FILE *outfp;
|
||||
|
||||
pr_info("%s: Creating empty pmu_events_map[] table\n", prog);
|
||||
|
||||
/* Truncate file to clear any partial writes to it */
|
||||
outfp = fopen(output_file, "w");
|
||||
if (!outfp) {
|
||||
perror("fopen()");
|
||||
_Exit(1);
|
||||
}
|
||||
|
||||
fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n");
|
||||
print_mapping_table_prefix(outfp);
|
||||
print_mapping_table_suffix(outfp);
|
||||
fclose(outfp);
|
||||
}
|
||||
|
||||
static int get_maxfds(void)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0)
|
||||
return min((int)rlim.rlim_max / 2, 512);
|
||||
|
||||
return 512;
|
||||
}
|
||||
|
||||
/*
|
||||
* nftw() doesn't let us pass an argument to the processing function,
|
||||
* so use a global variables.
|
||||
*/
|
||||
static FILE *eventsfp;
|
||||
static char *mapfile;
|
||||
|
||||
static int process_one_file(const char *fpath, const struct stat *sb,
|
||||
int typeflag, struct FTW *ftwbuf)
|
||||
{
|
||||
char *tblname, *bname = (char *) fpath + ftwbuf->base;
|
||||
int is_dir = typeflag == FTW_D;
|
||||
int is_file = typeflag == FTW_F;
|
||||
int level = ftwbuf->level;
|
||||
int err = 0;
|
||||
|
||||
pr_debug("%s %d %7jd %-20s %s\n",
|
||||
is_file ? "f" : is_dir ? "d" : "x",
|
||||
level, sb->st_size, bname, fpath);
|
||||
|
||||
/* base dir */
|
||||
if (level == 0)
|
||||
return 0;
|
||||
|
||||
/* model directory, reset topic */
|
||||
if (level == 1 && is_dir) {
|
||||
if (close_table)
|
||||
print_events_table_suffix(eventsfp);
|
||||
|
||||
/*
|
||||
* Drop file name suffix. Replace hyphens with underscores.
|
||||
* Fail if file name contains any alphanum characters besides
|
||||
* underscores.
|
||||
*/
|
||||
tblname = file_name_to_table_name(bname);
|
||||
if (!tblname) {
|
||||
pr_info("%s: Error determining table name for %s\n", prog,
|
||||
bname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_events_table_prefix(eventsfp, tblname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the mapfile name for now. We will process mapfile
|
||||
* after processing all JSON files (so we can write out the
|
||||
* mapping table after all PMU events tables).
|
||||
*
|
||||
* TODO: Allow for multiple mapfiles? Punt for now.
|
||||
*/
|
||||
if (level == 1 && is_file) {
|
||||
if (!strncmp(bname, "mapfile.csv", 11)) {
|
||||
if (mapfile) {
|
||||
pr_info("%s: Many mapfiles? Using %s, ignoring %s\n",
|
||||
prog, mapfile, fpath);
|
||||
} else {
|
||||
mapfile = strdup(fpath);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("%s: Ignoring file %s\n", prog, fpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the file name does not have a .json extension,
|
||||
* ignore it. It could be a readme.txt for instance.
|
||||
*/
|
||||
if (is_file) {
|
||||
char *suffix = bname + strlen(bname) - 5;
|
||||
|
||||
if (strncmp(suffix, ".json", 5)) {
|
||||
pr_info("%s: Ignoring file without .json suffix %s\n", prog,
|
||||
fpath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (level > 1 && add_topic(level, bname))
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Assume all other files are JSON files.
|
||||
*
|
||||
* If mapfile refers to 'power7_core.json', we create a table
|
||||
* named 'power7_core'. Any inconsistencies between the mapfile
|
||||
* and directory tree could result in build failure due to table
|
||||
* names not being found.
|
||||
*
|
||||
* Atleast for now, be strict with processing JSON file names.
|
||||
* i.e. if JSON file name cannot be mapped to C-style table name,
|
||||
* fail.
|
||||
*/
|
||||
if (is_file) {
|
||||
struct perf_entry_data data = {
|
||||
.topic = get_topic(),
|
||||
.outfp = eventsfp,
|
||||
};
|
||||
|
||||
err = json_events(fpath, print_events_table_entry, &data);
|
||||
|
||||
free(data.topic);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Starting in directory 'start_dirname', find the "mapfile.csv" and
|
||||
* the set of JSON files for the architecture 'arch'.
|
||||
*
|
||||
* From each JSON file, create a C-style "PMU events table" from the
|
||||
* JSON file (see struct pmu_event).
|
||||
*
|
||||
* From the mapfile, create a mapping between the CPU revisions and
|
||||
* PMU event tables (see struct pmu_events_map).
|
||||
*
|
||||
* Write out the PMU events tables and the mapping table to pmu-event.c.
|
||||
*
|
||||
* If unable to process the JSON or arch files, create an empty mapping
|
||||
* table so we can continue to build/use perf even if we cannot use the
|
||||
* PMU event aliases.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
int maxfds;
|
||||
char ldirname[PATH_MAX];
|
||||
|
||||
const char *arch;
|
||||
const char *output_file;
|
||||
const char *start_dirname;
|
||||
|
||||
prog = basename(argv[0]);
|
||||
if (argc < 4) {
|
||||
pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog);
|
||||
return 1;
|
||||
}
|
||||
|
||||
arch = argv[1];
|
||||
start_dirname = argv[2];
|
||||
output_file = argv[3];
|
||||
|
||||
if (argc > 4)
|
||||
verbose = atoi(argv[4]);
|
||||
|
||||
eventsfp = fopen(output_file, "w");
|
||||
if (!eventsfp) {
|
||||
pr_err("%s Unable to create required file %s (%s)\n",
|
||||
prog, output_file, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Include pmu-events.h first */
|
||||
fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n");
|
||||
|
||||
sprintf(ldirname, "%s/%s", start_dirname, arch);
|
||||
|
||||
/*
|
||||
* The mapfile allows multiple CPUids to point to the same JSON file,
|
||||
* so, not sure if there is a need for symlinks within the pmu-events
|
||||
* directory.
|
||||
*
|
||||
* For now, treat symlinks of JSON files as regular files and create
|
||||
* separate tables for each symlink (presumably, each symlink refers
|
||||
* to specific version of the CPU).
|
||||
*/
|
||||
|
||||
maxfds = get_maxfds();
|
||||
mapfile = NULL;
|
||||
rc = nftw(ldirname, process_one_file, maxfds, 0);
|
||||
if (rc && verbose) {
|
||||
pr_info("%s: Error walking file tree %s\n", prog, ldirname);
|
||||
goto empty_map;
|
||||
} else if (rc) {
|
||||
goto empty_map;
|
||||
}
|
||||
|
||||
if (close_table)
|
||||
print_events_table_suffix(eventsfp);
|
||||
|
||||
if (!mapfile) {
|
||||
pr_info("%s: No CPU->JSON mapping?\n", prog);
|
||||
goto empty_map;
|
||||
}
|
||||
|
||||
if (process_mapfile(eventsfp, mapfile)) {
|
||||
pr_info("%s: Error processing mapfile %s\n", prog, mapfile);
|
||||
goto empty_map;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
empty_map:
|
||||
fclose(eventsfp);
|
||||
create_empty_mapping(output_file);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#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
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* 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";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#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_ */
|
|
@ -0,0 +1,162 @@
|
|||
/* 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);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#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
|
|
@ -0,0 +1,37 @@
|
|||
#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)
|
|||
static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel, int cpu)
|
||||
{
|
||||
int thread, err;
|
||||
int thread;
|
||||
int nr_threads = perf_evlist__nr_threads(evlist, evsel);
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
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)
|
||||
return err;
|
||||
}
|
||||
|
@ -403,14 +402,14 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
|
|||
struct perf_evsel *evsel,
|
||||
int thread)
|
||||
{
|
||||
int cpu, err;
|
||||
int cpu;
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
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)
|
||||
return err;
|
||||
}
|
||||
|
@ -1606,10 +1605,9 @@ void perf_evlist__close(struct perf_evlist *evlist)
|
|||
struct perf_evsel *evsel;
|
||||
int ncpus = cpu_map__nr(evlist->cpus);
|
||||
int nthreads = thread_map__nr(evlist->threads);
|
||||
int n;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
int cpu, thread;
|
||||
|
||||
if (evsel->system_wide)
|
||||
nthreads = 1;
|
||||
|
||||
evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
|
||||
|
||||
if (evsel->fd) {
|
||||
int cpu, thread;
|
||||
for (cpu = 0; cpu < ncpus; cpu++) {
|
||||
for (thread = 0; thread < nthreads; thread++) {
|
||||
FD(evsel, cpu, thread) = -1;
|
||||
|
|
|
@ -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);
|
||||
|
||||
char *get_cpuid_str(void);
|
||||
#endif /* __PERF_HEADER_H */
|
||||
|
|
|
@ -90,6 +90,7 @@ struct intel_pt_decoder {
|
|||
bool pge;
|
||||
bool have_tma;
|
||||
bool have_cyc;
|
||||
bool fixup_last_mtc;
|
||||
uint64_t pos;
|
||||
uint64_t last_ip;
|
||||
uint64_t ip;
|
||||
|
@ -586,10 +587,31 @@ struct intel_pt_calc_cyc_to_tsc_info {
|
|||
uint64_t tsc_timestamp;
|
||||
uint64_t timestamp;
|
||||
bool have_tma;
|
||||
bool fixup_last_mtc;
|
||||
bool from_mtc;
|
||||
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)
|
||||
{
|
||||
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)
|
|||
return 0;
|
||||
|
||||
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)
|
||||
mtc_delta = mtc - data->last_mtc;
|
||||
else
|
||||
|
@ -687,6 +714,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
|
|||
|
||||
data->ctc_delta = 0;
|
||||
data->have_tma = true;
|
||||
data->fixup_last_mtc = true;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -753,6 +781,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder,
|
|||
.tsc_timestamp = decoder->tsc_timestamp,
|
||||
.timestamp = decoder->timestamp,
|
||||
.have_tma = decoder->have_tma,
|
||||
.fixup_last_mtc = decoder->fixup_last_mtc,
|
||||
.from_mtc = from_mtc,
|
||||
.cbr_cyc_to_tsc = 0,
|
||||
};
|
||||
|
@ -1271,6 +1300,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
|
|||
}
|
||||
decoder->ctc_delta = 0;
|
||||
decoder->have_tma = true;
|
||||
decoder->fixup_last_mtc = true;
|
||||
intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n",
|
||||
decoder->ctc_timestamp, decoder->last_mtc, ctc_rem);
|
||||
}
|
||||
|
@ -1285,6 +1315,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
|
|||
|
||||
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)
|
||||
mtc_delta = mtc - decoder->last_mtc;
|
||||
else
|
||||
|
@ -1353,6 +1389,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
|
|||
timestamp, decoder->timestamp);
|
||||
else
|
||||
decoder->timestamp = timestamp;
|
||||
|
||||
decoder->timestamp_insn_cnt = 0;
|
||||
}
|
||||
|
||||
/* Walk PSB+ packets when already in sync. */
|
||||
|
|
|
@ -1745,9 +1745,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
|||
int max_stack)
|
||||
{
|
||||
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;
|
||||
int i, j, err;
|
||||
u64 ip;
|
||||
|
||||
for (i = 0; i < chain_nr; i++) {
|
||||
|
@ -1758,7 +1757,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
|||
/* LBR only affects the user callchain */
|
||||
if (i != chain_nr) {
|
||||
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.
|
||||
* The mix_chain_nr is kernel call chain
|
||||
|
@ -1772,6 +1771,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
|||
int mix_chain_nr = i + 1 + lbr_nr + 1;
|
||||
|
||||
for (j = 0; j < mix_chain_nr; j++) {
|
||||
int err;
|
||||
if (callchain_param.order == ORDER_CALLEE) {
|
||||
if (j < i + 1)
|
||||
ip = chain->ips[j];
|
||||
|
|
|
@ -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_CONFIG2:
|
||||
case PARSE_EVENTS__TERM_TYPE_NAME:
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
return true;
|
||||
default:
|
||||
if (!err)
|
||||
|
@ -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 *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)
|
||||
|
@ -2263,7 +2264,8 @@ out_enomem:
|
|||
/*
|
||||
* 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,
|
||||
event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
|
||||
|
@ -2273,7 +2275,7 @@ void print_events(const char *event_glob, bool 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)
|
||||
return;
|
||||
|
|
|
@ -172,7 +172,8 @@ void parse_events_update_lists(struct list_head *list_event,
|
|||
void parse_events_evlist_error(struct parse_events_evlist *data,
|
||||
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 {
|
||||
const char *symbol;
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include "pmu.h"
|
||||
#include "parse-events.h"
|
||||
#include "cpumap.h"
|
||||
#include "header.h"
|
||||
#include "pmu-events/pmu-events.h"
|
||||
#include "cache.h"
|
||||
|
||||
struct perf_pmu_format {
|
||||
char *name;
|
||||
|
@ -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,
|
||||
char *desc __maybe_unused, char *val)
|
||||
char *desc, char *val, char *long_desc,
|
||||
char *topic)
|
||||
{
|
||||
struct perf_pmu_alias *alias;
|
||||
int ret;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -269,7 +278,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
|
|||
|
||||
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)
|
||||
|
@ -473,6 +482,68 @@ static struct cpu_map *pmu_cpumask(const char *name)
|
|||
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
|
||||
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
|
||||
{
|
||||
|
@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
|
|||
if (pmu_aliases(name, &aliases))
|
||||
return NULL;
|
||||
|
||||
if (!strcmp(name, "cpu"))
|
||||
pmu_add_cpu_aliases(&aliases);
|
||||
|
||||
if (pmu_type(name, &type))
|
||||
return NULL;
|
||||
|
||||
|
@ -983,21 +1057,63 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
|
|||
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 char * const *bs = b;
|
||||
return strcmp(*as, *bs);
|
||||
const struct sevent *as = a;
|
||||
const struct sevent *bs = b;
|
||||
|
||||
/* 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_alias *alias;
|
||||
char buf[1024];
|
||||
int printed = 0;
|
||||
int len, j;
|
||||
char **aliases;
|
||||
struct sevent *aliases;
|
||||
int numdesc = 0;
|
||||
int columns = pager_get_columns();
|
||||
char *topic = NULL;
|
||||
|
||||
pmu = NULL;
|
||||
len = 0;
|
||||
|
@ -1007,14 +1123,15 @@ void print_pmu_events(const char *event_glob, bool name_only)
|
|||
if (pmu->selectable)
|
||||
len++;
|
||||
}
|
||||
aliases = zalloc(sizeof(char *) * len);
|
||||
aliases = zalloc(sizeof(struct sevent) * len);
|
||||
if (!aliases)
|
||||
goto out_enomem;
|
||||
pmu = NULL;
|
||||
j = 0;
|
||||
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
|
||||
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");
|
||||
|
||||
if (event_glob != NULL &&
|
||||
|
@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only)
|
|||
event_glob))))
|
||||
continue;
|
||||
|
||||
if (is_cpu && !name_only)
|
||||
if (is_cpu && !name_only && !alias->desc)
|
||||
name = format_alias_or(buf, sizeof(buf), pmu, alias);
|
||||
|
||||
aliases[j] = strdup(name);
|
||||
if (aliases[j] == NULL)
|
||||
aliases[j].name = name;
|
||||
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;
|
||||
|
||||
aliases[j].desc = long_desc ? alias->long_desc :
|
||||
alias->desc;
|
||||
aliases[j].topic = alias->topic;
|
||||
j++;
|
||||
}
|
||||
if (pmu->selectable &&
|
||||
|
@ -1036,25 +1162,39 @@ void print_pmu_events(const char *event_glob, bool name_only)
|
|||
char *s;
|
||||
if (asprintf(&s, "%s//", pmu->name) < 0)
|
||||
goto out_enomem;
|
||||
aliases[j] = s;
|
||||
aliases[j].name = s;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
len = j;
|
||||
qsort(aliases, len, sizeof(char *), cmp_string);
|
||||
qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
|
||||
for (j = 0; j < len; j++) {
|
||||
if (name_only) {
|
||||
printf("%s ", aliases[j]);
|
||||
printf("%s ", aliases[j].name);
|
||||
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++;
|
||||
}
|
||||
if (printed && pager_in_use())
|
||||
printf("\n");
|
||||
out_free:
|
||||
for (j = 0; j < len; j++)
|
||||
zfree(&aliases[j]);
|
||||
zfree(&aliases[j].name);
|
||||
zfree(&aliases);
|
||||
return;
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ struct perf_pmu_info {
|
|||
|
||||
struct perf_pmu_alias {
|
||||
char *name;
|
||||
char *desc;
|
||||
char *long_desc;
|
||||
char *topic;
|
||||
struct list_head terms; /* HEAD struct parse_events_term -> list */
|
||||
struct list_head list; /* ELEM */
|
||||
char unit[UNIT_MAX_LEN+1];
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
|||
goto out;
|
||||
}
|
||||
|
||||
for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) {
|
||||
for (ptr2 = ptr1; *ptr2 != '\0'; ptr2++) {
|
||||
if (!isalnum(*ptr2) && *ptr2 != '_') {
|
||||
*ptr2 = '\0';
|
||||
break;
|
||||
|
|
|
@ -66,9 +66,8 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) {
|
|||
int strbuf_grow(struct strbuf *buf, size_t);
|
||||
|
||||
static inline int strbuf_setlen(struct strbuf *sb, size_t len) {
|
||||
int ret;
|
||||
if (!sb->alloc) {
|
||||
ret = strbuf_grow(sb, 0);
|
||||
int ret = strbuf_grow(sb, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -14,13 +14,12 @@
|
|||
|
||||
int thread__init_map_groups(struct thread *thread, struct machine *machine)
|
||||
{
|
||||
struct thread *leader;
|
||||
pid_t pid = thread->pid_;
|
||||
|
||||
if (pid == thread->tid || pid == -1) {
|
||||
thread->mg = map_groups__new(machine);
|
||||
} else {
|
||||
leader = __machine__findnew_thread(machine, pid, pid);
|
||||
struct thread *leader = __machine__findnew_thread(machine, pid, pid);
|
||||
if (leader) {
|
||||
thread->mg = map_groups__get(leader->mg);
|
||||
thread__put(leader);
|
||||
|
@ -130,11 +129,10 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
|
|||
bool exec)
|
||||
{
|
||||
struct comm *new, *curr = thread__comm(thread);
|
||||
int err;
|
||||
|
||||
/* Override the default :tid entry */
|
||||
if (!thread->comm_set) {
|
||||
err = comm__override(curr, str, timestamp, exec);
|
||||
int err = comm__override(curr, str, timestamp, exec);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
|
@ -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 err;
|
||||
|
||||
if (parent->comm_set) {
|
||||
const char *comm = thread__comm_str(parent);
|
||||
int err;
|
||||
if (!comm)
|
||||
return -ENOMEM;
|
||||
err = thread__set_comm(thread, comm, timestamp);
|
||||
|
|
Loading…
Reference in New Issue