mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 05:06:44 +07:00
Cleanups and fixes for perf/core:
. Short term fix for 'diff' tool breakage related to perf.data files with multiple events. From Jiri Olsa . Cleanup for event id tracepoint reading routine, from Borislav Petkov . 32-bit compilation fixes from Jiri Olsa . Event parsing modifier assignment fixes from Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJPa24hAAoJENZQFvNTUqpARrEQAIXfsXG2X45twEFHJ0oOmvZI 7F28pMFM0OBJdSq/MKyO86+j5wtbtKtjCcVZfVm1ukkeD7KnKQv+iyBw1rEuE/dx IJIIS+zkCEAje7s64diNax/rpvZXp+k92pKKo/JGjalsVcw63VF5n0Ej3JA+n8Qp anKyJd2SJFogoljTzRnFnZ+zsbn5CWVwb3NVRhH8t+jfKKOsmw8Nq5ds5ysuRf8x +Xpm3YAsyKzAJMd05Uo5no+Ne4A5TukySiarZl3wiQ0TrwM9mVxQVAU5iVkAUZkb Bp1dgBOgG1RNudcRSgbq3NuO7TGFby6DqODgXvo1TzpLyixKbWUz3JI0mEya5xCL eBxJ2UTQpruBg+XJ3C0ys74TkMcQUeyxhqbwPu1WhMbP7cIJK1W1hJ728qWRG94a uxFMFlEfc0d9kxlsXVvxUumiyhEw5gPtwdbQf7fo2xpMPQmHCC3yS7DdMO6FBfUW oqPC7gb3aSTmHnKuYvwbT9EcOL27YHU+Y/4sRJ/QfohArgc4h60dUQrE9fWz7dXH /eCXDvMnBs2+z52dNLq/mkoQMP4iFzWqboDNn5GM4jI2LmXd/hxBGL6lb2Vzm8CI QYi5xC4E6VStSkJG2DTtvaYTclWOjo+ZrsGz7sDXQV3MgZ1wiZIWcGiPjeFBWU25 mHWkZT3of/MAbewmeklc =V7d9 -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent Cleanups and fixes for perf/core: . Short term fix for 'diff' tool breakage related to perf.data files with multiple events. From Jiri Olsa . Cleanup for event id tracepoint reading routine, from Borislav Petkov . 32-bit compilation fixes from Jiri Olsa . Event parsing modifier assignment fixes from Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
c5bc437702
@ -0,0 +1,14 @@
|
||||
Where: /sys/bus/event_source/devices/<dev>/format
|
||||
Date: January 2012
|
||||
Kernel Version: 3.3
|
||||
Contact: Jiri Olsa <jolsa@redhat.com>
|
||||
Description:
|
||||
Attribute group to describe the magic bits that go into
|
||||
perf_event_attr::config[012] for a particular pmu.
|
||||
Each attribute of this group defines the 'hardware' bitmask
|
||||
we want to export, so that userspace can deal with sane
|
||||
name/value pairs.
|
||||
|
||||
Example: 'config1:1,6-10,44'
|
||||
Defines contents of attribute that occupies bits 1,6-10,44 of
|
||||
perf_event_attr::config1.
|
@ -1314,6 +1314,11 @@ static void __init pmu_check_apic(void)
|
||||
pr_info("no hardware sampling interrupt available.\n");
|
||||
}
|
||||
|
||||
static struct attribute_group x86_pmu_format_group = {
|
||||
.name = "format",
|
||||
.attrs = NULL,
|
||||
};
|
||||
|
||||
static int __init init_hw_perf_events(void)
|
||||
{
|
||||
struct x86_pmu_quirk *quirk;
|
||||
@ -1388,6 +1393,7 @@ static int __init init_hw_perf_events(void)
|
||||
}
|
||||
|
||||
x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */
|
||||
x86_pmu_format_group.attrs = x86_pmu.format_attrs;
|
||||
|
||||
pr_info("... version: %d\n", x86_pmu.version);
|
||||
pr_info("... bit width: %d\n", x86_pmu.cntval_bits);
|
||||
@ -1668,6 +1674,7 @@ static struct attribute_group x86_pmu_attr_group = {
|
||||
|
||||
static const struct attribute_group *x86_pmu_attr_groups[] = {
|
||||
&x86_pmu_attr_group,
|
||||
&x86_pmu_format_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -339,6 +339,7 @@ struct x86_pmu {
|
||||
* sysfs attrs
|
||||
*/
|
||||
int attr_rdpmc;
|
||||
struct attribute **format_attrs;
|
||||
|
||||
/*
|
||||
* CPU Hotplug hooks
|
||||
|
@ -404,6 +404,21 @@ static void amd_pmu_cpu_dead(int cpu)
|
||||
}
|
||||
}
|
||||
|
||||
PMU_FORMAT_ATTR(event, "config:0-7,32-35");
|
||||
PMU_FORMAT_ATTR(umask, "config:8-15" );
|
||||
PMU_FORMAT_ATTR(edge, "config:18" );
|
||||
PMU_FORMAT_ATTR(inv, "config:23" );
|
||||
PMU_FORMAT_ATTR(cmask, "config:24-31" );
|
||||
|
||||
static struct attribute *amd_format_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static __initconst const struct x86_pmu amd_pmu = {
|
||||
.name = "AMD",
|
||||
.handle_irq = x86_pmu_handle_irq,
|
||||
@ -426,6 +441,8 @@ static __initconst const struct x86_pmu amd_pmu = {
|
||||
.get_event_constraints = amd_get_event_constraints,
|
||||
.put_event_constraints = amd_put_event_constraints,
|
||||
|
||||
.format_attrs = amd_format_attr,
|
||||
|
||||
.cpu_prepare = amd_pmu_cpu_prepare,
|
||||
.cpu_starting = amd_pmu_cpu_starting,
|
||||
.cpu_dead = amd_pmu_cpu_dead,
|
||||
@ -596,6 +613,7 @@ static __initconst const struct x86_pmu amd_pmu_f15h = {
|
||||
.cpu_dead = amd_pmu_cpu_dead,
|
||||
#endif
|
||||
.cpu_starting = amd_pmu_cpu_starting,
|
||||
.format_attrs = amd_format_attr,
|
||||
};
|
||||
|
||||
__init int amd_pmu_init(void)
|
||||
|
@ -1431,6 +1431,24 @@ static void core_pmu_enable_all(int added)
|
||||
}
|
||||
}
|
||||
|
||||
PMU_FORMAT_ATTR(event, "config:0-7" );
|
||||
PMU_FORMAT_ATTR(umask, "config:8-15" );
|
||||
PMU_FORMAT_ATTR(edge, "config:18" );
|
||||
PMU_FORMAT_ATTR(pc, "config:19" );
|
||||
PMU_FORMAT_ATTR(any, "config:21" ); /* v3 + */
|
||||
PMU_FORMAT_ATTR(inv, "config:23" );
|
||||
PMU_FORMAT_ATTR(cmask, "config:24-31" );
|
||||
|
||||
static struct attribute *intel_arch_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_pc.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static __initconst const struct x86_pmu core_pmu = {
|
||||
.name = "core",
|
||||
.handle_irq = x86_pmu_handle_irq,
|
||||
@ -1455,6 +1473,7 @@ static __initconst const struct x86_pmu core_pmu = {
|
||||
.put_event_constraints = intel_put_event_constraints,
|
||||
.event_constraints = intel_core_event_constraints,
|
||||
.guest_get_msrs = core_guest_get_msrs,
|
||||
.format_attrs = intel_arch_formats_attr,
|
||||
};
|
||||
|
||||
struct intel_shared_regs *allocate_shared_regs(int cpu)
|
||||
@ -1553,6 +1572,21 @@ static void intel_pmu_flush_branch_stack(void)
|
||||
intel_pmu_lbr_reset();
|
||||
}
|
||||
|
||||
PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");
|
||||
|
||||
static struct attribute *intel_arch3_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_pc.attr,
|
||||
&format_attr_any.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask.attr,
|
||||
|
||||
&format_attr_offcore_rsp.attr, /* XXX do NHM/WSM + SNB breakout */
|
||||
NULL,
|
||||
};
|
||||
|
||||
static __initconst const struct x86_pmu intel_pmu = {
|
||||
.name = "Intel",
|
||||
.handle_irq = intel_pmu_handle_irq,
|
||||
@ -1576,6 +1610,8 @@ static __initconst const struct x86_pmu intel_pmu = {
|
||||
.get_event_constraints = intel_get_event_constraints,
|
||||
.put_event_constraints = intel_put_event_constraints,
|
||||
|
||||
.format_attrs = intel_arch3_formats_attr,
|
||||
|
||||
.cpu_prepare = intel_pmu_cpu_prepare,
|
||||
.cpu_starting = intel_pmu_cpu_starting,
|
||||
.cpu_dying = intel_pmu_cpu_dying,
|
||||
|
@ -87,6 +87,23 @@ static void p6_pmu_enable_event(struct perf_event *event)
|
||||
(void)checking_wrmsrl(hwc->config_base, val);
|
||||
}
|
||||
|
||||
PMU_FORMAT_ATTR(event, "config:0-7" );
|
||||
PMU_FORMAT_ATTR(umask, "config:8-15" );
|
||||
PMU_FORMAT_ATTR(edge, "config:18" );
|
||||
PMU_FORMAT_ATTR(pc, "config:19" );
|
||||
PMU_FORMAT_ATTR(inv, "config:23" );
|
||||
PMU_FORMAT_ATTR(cmask, "config:24-31" );
|
||||
|
||||
static struct attribute *intel_p6_formats_attr[] = {
|
||||
&format_attr_event.attr,
|
||||
&format_attr_umask.attr,
|
||||
&format_attr_edge.attr,
|
||||
&format_attr_pc.attr,
|
||||
&format_attr_inv.attr,
|
||||
&format_attr_cmask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static __initconst const struct x86_pmu p6_pmu = {
|
||||
.name = "p6",
|
||||
.handle_irq = x86_pmu_handle_irq,
|
||||
@ -115,6 +132,8 @@ static __initconst const struct x86_pmu p6_pmu = {
|
||||
.cntval_mask = (1ULL << 32) - 1,
|
||||
.get_event_constraints = x86_get_event_constraints,
|
||||
.event_constraints = p6_event_constraints,
|
||||
|
||||
.format_attrs = intel_p6_formats_attr,
|
||||
};
|
||||
|
||||
__init int p6_pmu_init(void)
|
||||
|
@ -550,6 +550,7 @@ struct perf_guest_info_callbacks {
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <asm/local.h>
|
||||
|
||||
#define PERF_MAX_STACK_DEPTH 255
|
||||
@ -1291,5 +1292,18 @@ do { \
|
||||
register_cpu_notifier(&fn##_nb); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PMU_FORMAT_ATTR(_name, _format) \
|
||||
static ssize_t \
|
||||
_name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *page) \
|
||||
{ \
|
||||
BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
|
||||
return sprintf(page, _format "\n"); \
|
||||
} \
|
||||
\
|
||||
static struct device_attribute format_attr_##_name = __ATTR_RO(_name)
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_PERF_EVENT_H */
|
||||
|
@ -48,6 +48,9 @@ OPTIONS
|
||||
Only consider these symbols. CSV that understands
|
||||
file://filename entries.
|
||||
|
||||
--symbol-filter=::
|
||||
Only show symbols that match (partially) with this filter.
|
||||
|
||||
-U::
|
||||
--hide-unresolved::
|
||||
Only display entries resolved to a symbol.
|
||||
@ -110,6 +113,8 @@ OPTIONS
|
||||
requires a tty, if one is not present, as when piping to other
|
||||
commands, the stdio interface is used.
|
||||
|
||||
--gtk:: Use the GTK2 interface.
|
||||
|
||||
-k::
|
||||
--vmlinux=<file>::
|
||||
vmlinux pathname
|
||||
|
@ -61,6 +61,8 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
FLEX = $(CROSS_COMPILE)flex
|
||||
BISON= $(CROSS_COMPILE)bison
|
||||
|
||||
# Additional ARCH settings for x86
|
||||
ifeq ($(ARCH),i386)
|
||||
@ -276,6 +278,7 @@ LIB_H += util/build-id.h
|
||||
LIB_H += util/debug.h
|
||||
LIB_H += util/debugfs.h
|
||||
LIB_H += util/sysfs.h
|
||||
LIB_H += util/pmu.h
|
||||
LIB_H += util/event.h
|
||||
LIB_H += util/evsel.h
|
||||
LIB_H += util/evlist.h
|
||||
@ -323,6 +326,7 @@ LIB_OBJS += $(OUTPUT)util/config.o
|
||||
LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/sysfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
LIB_OBJS += $(OUTPUT)util/event.o
|
||||
LIB_OBJS += $(OUTPUT)util/evlist.o
|
||||
@ -359,6 +363,10 @@ LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread_map.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu-flex.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu-bison.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
|
||||
@ -501,6 +509,20 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_GTK2
|
||||
BASIC_CFLAGS += -DNO_GTK2
|
||||
else
|
||||
FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0)
|
||||
ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y)
|
||||
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
|
||||
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
|
||||
else
|
||||
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
|
||||
EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
|
||||
LIB_OBJS += $(OUTPUT)util/gtk/browser.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NO_LIBPERL
|
||||
BASIC_CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
@ -647,6 +669,8 @@ ifndef V
|
||||
QUIET_LINK = @echo ' ' LINK $@;
|
||||
QUIET_MKDIR = @echo ' ' MKDIR $@;
|
||||
QUIET_GEN = @echo ' ' GEN $@;
|
||||
QUIET_FLEX = @echo ' ' FLEX $@;
|
||||
QUIET_BISON = @echo ' ' BISON $@;
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -727,12 +751,19 @@ $(OUTPUT)perf.o perf.spec \
|
||||
$(SCRIPTS) \
|
||||
: $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .c .S .s
|
||||
|
||||
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
|
||||
$(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.o: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
|
||||
|
||||
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
@ -759,6 +790,12 @@ $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
|
||||
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
$(OUTPUT)util/parse-events-flex.o: util/parse-events-flex.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
|
||||
|
||||
$(OUTPUT)util/pmu-flex.o: util/pmu-flex.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
|
||||
|
||||
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
|
||||
@ -795,6 +832,8 @@ help:
|
||||
@echo ' html - make html documentation'
|
||||
@echo ' info - make GNU info documentation (access with info <foo>)'
|
||||
@echo ' pdf - make pdf documentation'
|
||||
@echo ' event-parser - make event parser code'
|
||||
@echo ' pmu-parser - make pmu format parser code'
|
||||
@echo ' TAGS - use etags to make tag information for source browsing'
|
||||
@echo ' tags - use ctags to make tag information for source browsing'
|
||||
@echo ' cscope - use cscope to make interactive browsing database'
|
||||
@ -844,6 +883,14 @@ cscope:
|
||||
$(RM) cscope*
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
|
||||
|
||||
event-parser:
|
||||
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o util/parse-events-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=util/parse-events-flex.h -t util/parse-events.l > util/parse-events-flex.c
|
||||
|
||||
pmu-parser:
|
||||
$(QUIET_BISON)$(BISON) -v util/pmu.y -d -o util/pmu-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=util/pmu-flex.h -t util/pmu.l > util/pmu-flex.c
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
$(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
|
||||
|
@ -24,6 +24,11 @@ static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
static bool show_displacement;
|
||||
|
||||
struct perf_diff {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
static int hists__add_entry(struct hists *self,
|
||||
struct addr_location *al, u64 period)
|
||||
{
|
||||
@ -32,12 +37,14 @@ static int hists__add_entry(struct hists *self,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int diff__process_sample_event(struct perf_tool *tool __used,
|
||||
static int diff__process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_diff *_diff = container_of(tool, struct perf_diff, tool);
|
||||
struct perf_session *session = _diff->session;
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
|
||||
@ -49,24 +56,26 @@ static int diff__process_sample_event(struct perf_tool *tool __used,
|
||||
if (al.filtered || al.sym == NULL)
|
||||
return 0;
|
||||
|
||||
if (hists__add_entry(&evsel->hists, &al, sample->period)) {
|
||||
if (hists__add_entry(&session->hists, &al, sample->period)) {
|
||||
pr_warning("problem incrementing symbol period, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->hists.stats.total_period += sample->period;
|
||||
session->hists.stats.total_period += sample->period;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool perf_diff = {
|
||||
.sample = diff__process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.lost = perf_event__process_lost,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
static struct perf_diff diff = {
|
||||
.tool = {
|
||||
.sample = diff__process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.lost = perf_event__process_lost,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
|
||||
@ -107,12 +116,6 @@ static void hists__resort_entries(struct hists *self)
|
||||
self->entries = tmp;
|
||||
}
|
||||
|
||||
static void hists__set_positions(struct hists *self)
|
||||
{
|
||||
hists__output_resort(self);
|
||||
hists__resort_entries(self);
|
||||
}
|
||||
|
||||
static struct hist_entry *hists__find_entry(struct hists *self,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
@ -146,30 +149,37 @@ static void hists__match(struct hists *older, struct hists *newer)
|
||||
static int __cmd_diff(void)
|
||||
{
|
||||
int ret, i;
|
||||
#define older (session[0])
|
||||
#define newer (session[1])
|
||||
struct perf_session *session[2];
|
||||
|
||||
session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff);
|
||||
session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff);
|
||||
older = perf_session__new(input_old, O_RDONLY, force, false,
|
||||
&diff.tool);
|
||||
newer = perf_session__new(input_new, O_RDONLY, force, false,
|
||||
&diff.tool);
|
||||
if (session[0] == NULL || session[1] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ret = perf_session__process_events(session[i], &perf_diff);
|
||||
diff.session = session[i];
|
||||
ret = perf_session__process_events(session[i], &diff.tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
hists__output_resort(&session[i]->hists);
|
||||
}
|
||||
|
||||
hists__output_resort(&session[1]->hists);
|
||||
if (show_displacement)
|
||||
hists__set_positions(&session[0]->hists);
|
||||
hists__resort_entries(&older->hists);
|
||||
|
||||
hists__match(&session[0]->hists, &session[1]->hists);
|
||||
hists__fprintf(&session[1]->hists, &session[0]->hists,
|
||||
hists__match(&older->hists, &newer->hists);
|
||||
hists__fprintf(&newer->hists, &older->hists,
|
||||
show_displacement, true, 0, 0, stdout);
|
||||
out_delete:
|
||||
for (i = 0; i < 2; ++i)
|
||||
perf_session__delete(session[i]);
|
||||
return ret;
|
||||
#undef older
|
||||
#undef newer
|
||||
}
|
||||
|
||||
static const char * const diff_usage[] = {
|
||||
|
@ -40,7 +40,7 @@ struct perf_report {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool force, use_tui, use_gtk, use_stdio;
|
||||
bool hide_unresolved;
|
||||
bool dont_use_callchains;
|
||||
bool show_full_info;
|
||||
@ -50,6 +50,7 @@ struct perf_report {
|
||||
const char *pretty_printing_style;
|
||||
symbol_filter_t annotate_init;
|
||||
const char *cpu_list;
|
||||
const char *symbol_filter_str;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
@ -400,6 +401,9 @@ static int __cmd_report(struct perf_report *rep)
|
||||
list_for_each_entry(pos, &session->evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
|
||||
if (pos->idx == 0)
|
||||
hists->symbol_filter_str = rep->symbol_filter_str;
|
||||
|
||||
hists__collapse_resort(hists);
|
||||
hists__output_resort(hists);
|
||||
nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
||||
@ -411,8 +415,13 @@ static int __cmd_report(struct perf_report *rep)
|
||||
}
|
||||
|
||||
if (use_browser > 0) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
if (use_browser == 1) {
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
} else if (use_browser == 2) {
|
||||
perf_evlist__gtk_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
} else
|
||||
perf_evlist__tty_browse_hists(session->evlist, rep, help);
|
||||
|
||||
@ -569,6 +578,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
|
||||
"pretty printing style key: normal raw"),
|
||||
OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
|
||||
"Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
@ -591,6 +601,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
"only consider symbols in these comms"),
|
||||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
|
||||
"only show symbols that (partially) match with this filter"),
|
||||
OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
|
||||
"width[,width...]",
|
||||
"don't try to adjust column width, use these fixed values"),
|
||||
@ -624,6 +636,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
use_browser = 0;
|
||||
else if (report.use_tui)
|
||||
use_browser = 1;
|
||||
else if (report.use_gtk)
|
||||
use_browser = 2;
|
||||
|
||||
if (report.inverted_callchain)
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
@ -660,7 +674,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
}
|
||||
|
||||
if (strcmp(report.input_name, "-") != 0) {
|
||||
setup_browser(true);
|
||||
if (report.use_gtk)
|
||||
perf_gtk_setup_browser(argc, argv, true);
|
||||
else
|
||||
setup_browser(true);
|
||||
} else {
|
||||
use_browser = 0;
|
||||
}
|
||||
@ -709,11 +726,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
} else
|
||||
symbol_conf.exclude_other = false;
|
||||
|
||||
/*
|
||||
* Any (unrecognized) arguments left?
|
||||
*/
|
||||
if (argc)
|
||||
usage_with_options(report_usage, options);
|
||||
if (argc) {
|
||||
/*
|
||||
* Special case: if there's an argument left then assume that
|
||||
* it's a symbol filter:
|
||||
*/
|
||||
if (argc > 1)
|
||||
usage_with_options(report_usage, options);
|
||||
|
||||
report.symbol_filter_str = argv[0];
|
||||
}
|
||||
|
||||
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
|
||||
|
||||
|
@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
|
||||
if (system_wide)
|
||||
return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
|
||||
group, group_fd);
|
||||
if (!target_pid && !target_tid) {
|
||||
if (!target_pid && !target_tid && (!group || evsel == first)) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "util/parse-events.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "util/pmu.h"
|
||||
#include "../../include/linux/hw_breakpoint.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
@ -650,7 +651,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist)
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -677,6 +678,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong period",
|
||||
100000 == evsel->attr.sample_period);
|
||||
TEST_ASSERT_VAL("wrong config1",
|
||||
0 == evsel->attr.config1);
|
||||
TEST_ASSERT_VAL("wrong config2",
|
||||
1 == evsel->attr.config2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
@ -858,6 +877,115 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
|
||||
return test__checkevent_genhw(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
|
||||
return test__checkevent_breakpoint(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
|
||||
return test__checkevent_breakpoint_x(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
|
||||
return test__checkevent_breakpoint_r(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
|
||||
return test__checkevent_breakpoint_w(evlist);
|
||||
}
|
||||
|
||||
static int test__checkevent_pmu(struct perf_evlist *evlist)
|
||||
{
|
||||
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1);
|
||||
TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2);
|
||||
TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_list(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
|
||||
|
||||
/* r1 */
|
||||
evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
|
||||
TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
|
||||
/* syscalls:sys_enter_open:k */
|
||||
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong sample_type",
|
||||
(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
|
||||
evsel->attr.sample_type);
|
||||
TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
|
||||
|
||||
/* 1:1:hp */
|
||||
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
|
||||
TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
|
||||
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
|
||||
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
|
||||
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct test__event_st {
|
||||
const char *name;
|
||||
__u32 type;
|
||||
@ -872,7 +1000,7 @@ static struct test__event_st {
|
||||
.check = test__checkevent_tracepoint_multi,
|
||||
},
|
||||
{
|
||||
.name = "r1",
|
||||
.name = "r1a",
|
||||
.check = test__checkevent_raw,
|
||||
},
|
||||
{
|
||||
@ -883,6 +1011,10 @@ static struct test__event_st {
|
||||
.name = "instructions",
|
||||
.check = test__checkevent_symbolic_name,
|
||||
},
|
||||
{
|
||||
.name = "cycles/period=100000,config2/",
|
||||
.check = test__checkevent_symbolic_name_config,
|
||||
},
|
||||
{
|
||||
.name = "faults",
|
||||
.check = test__checkevent_symbolic_alias,
|
||||
@ -916,7 +1048,7 @@ static struct test__event_st {
|
||||
.check = test__checkevent_tracepoint_multi_modifier,
|
||||
},
|
||||
{
|
||||
.name = "r1:kp",
|
||||
.name = "r1a:kp",
|
||||
.check = test__checkevent_raw_modifier,
|
||||
},
|
||||
{
|
||||
@ -935,6 +1067,30 @@ static struct test__event_st {
|
||||
.name = "L1-dcache-load-miss:kp",
|
||||
.check = test__checkevent_genhw_modifier,
|
||||
},
|
||||
{
|
||||
.name = "mem:0:u",
|
||||
.check = test__checkevent_breakpoint_modifier,
|
||||
},
|
||||
{
|
||||
.name = "mem:0:x:k",
|
||||
.check = test__checkevent_breakpoint_x_modifier,
|
||||
},
|
||||
{
|
||||
.name = "mem:0:r:hp",
|
||||
.check = test__checkevent_breakpoint_r_modifier,
|
||||
},
|
||||
{
|
||||
.name = "mem:0:w:up",
|
||||
.check = test__checkevent_breakpoint_w_modifier,
|
||||
},
|
||||
{
|
||||
.name = "cpu/config=10,config1,config2=3,period=1000/u",
|
||||
.check = test__checkevent_pmu,
|
||||
},
|
||||
{
|
||||
.name = "r1,syscalls:sys_enter_open:k,1:1:hp",
|
||||
.check = test__checkevent_list,
|
||||
},
|
||||
};
|
||||
|
||||
#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
|
||||
@ -960,10 +1116,9 @@ static int test__parse_events(void)
|
||||
}
|
||||
|
||||
ret = e->check(evlist);
|
||||
perf_evlist__delete(evlist);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
perf_evlist__delete(evlist);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1462,6 +1617,11 @@ static int test__rdpmc(void)
|
||||
|
||||
#endif
|
||||
|
||||
static int test__perf_pmu(void)
|
||||
{
|
||||
return perf_pmu__test();
|
||||
}
|
||||
|
||||
static struct test {
|
||||
const char *desc;
|
||||
int (*func)(void);
|
||||
@ -1496,6 +1656,10 @@ static struct test {
|
||||
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
|
||||
.func = test__PERF_RECORD,
|
||||
},
|
||||
{
|
||||
.desc = "Test perf pmu format parsing",
|
||||
.func = test__perf_pmu,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
@ -65,6 +65,21 @@ int main(void)
|
||||
endef
|
||||
endif
|
||||
|
||||
ifndef NO_GTK2
|
||||
define SOURCE_GTK2
|
||||
#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
|
||||
#include <gtk/gtk.h>
|
||||
#pragma GCC diagnostic error \"-Wstrict-prototypes\"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
endef
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPERL
|
||||
define SOURCE_PERL_EMBED
|
||||
#include <EXTERN.h>
|
||||
|
@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager);
|
||||
void exit_browser(bool wait_for_ok);
|
||||
#endif
|
||||
|
||||
#ifdef NO_GTK2_SUPPORT
|
||||
static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager)
|
||||
{
|
||||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
}
|
||||
static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {}
|
||||
#else
|
||||
void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager);
|
||||
void perf_gtk_exit_browser(bool wait_for_ok);
|
||||
#endif
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
|
@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evsel *evsel, *first;
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
|
||||
first = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__config(evsel, opts);
|
||||
perf_evsel__config(evsel, opts, first);
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_ID;
|
||||
|
@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type)
|
||||
return size;
|
||||
}
|
||||
|
||||
static void hists__init(struct hists *hists)
|
||||
void hists__init(struct hists *hists)
|
||||
{
|
||||
memset(hists, 0, sizeof(*hists));
|
||||
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
|
||||
@ -63,7 +63,8 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
|
||||
return evsel;
|
||||
}
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
|
||||
struct perf_evsel *first)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
@ -134,7 +135,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
|
||||
if (!opts->target_pid && !opts->target_tid && !opts->system_wide &&
|
||||
(!opts->group || evsel == first)) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
|
@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel);
|
||||
void perf_evsel__delete(struct perf_evsel *evsel);
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts);
|
||||
struct perf_record_opts *opts,
|
||||
struct perf_evsel *first);
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel)
|
||||
return __perf_evsel__sample_size(evsel->attr.sample_type);
|
||||
}
|
||||
|
||||
void hists__init(struct hists *hists);
|
||||
|
||||
#endif /* __PERF_EVSEL_H */
|
||||
|
189
tools/perf/util/gtk/browser.c
Normal file
189
tools/perf/util/gtk/browser.c
Normal file
@ -0,0 +1,189 @@
|
||||
#include "../evlist.h"
|
||||
#include "../cache.h"
|
||||
#include "../evsel.h"
|
||||
#include "../sort.h"
|
||||
#include "../hist.h"
|
||||
#include "gtk.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#define MAX_COLUMNS 32
|
||||
|
||||
void perf_gtk_setup_browser(int argc, const char *argv[],
|
||||
bool fallback_to_pager __used)
|
||||
{
|
||||
gtk_init(&argc, (char ***)&argv);
|
||||
}
|
||||
|
||||
void perf_gtk_exit_browser(bool wait_for_ok __used)
|
||||
{
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static void perf_gtk_signal(int sig)
|
||||
{
|
||||
psignal(sig, "perf");
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static void perf_gtk_resize_window(GtkWidget *window)
|
||||
{
|
||||
GdkRectangle rect;
|
||||
GdkScreen *screen;
|
||||
int monitor;
|
||||
int height;
|
||||
int width;
|
||||
|
||||
screen = gtk_widget_get_screen(window);
|
||||
|
||||
monitor = gdk_screen_get_monitor_at_window(screen, window->window);
|
||||
|
||||
gdk_screen_get_monitor_geometry(screen, monitor, &rect);
|
||||
|
||||
width = rect.width * 3 / 4;
|
||||
height = rect.height * 3 / 4;
|
||||
|
||||
gtk_window_resize(GTK_WINDOW(window), width, height);
|
||||
}
|
||||
|
||||
static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists)
|
||||
{
|
||||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
struct sort_entry *se;
|
||||
GtkListStore *store;
|
||||
struct rb_node *nd;
|
||||
u64 total_period;
|
||||
GtkWidget *view;
|
||||
int col_idx;
|
||||
int nr_cols;
|
||||
|
||||
nr_cols = 0;
|
||||
|
||||
/* The percentage column */
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
store = gtk_list_store_newv(nr_cols, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
/* The percentage column */
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, "Overhead (%)",
|
||||
renderer, "text",
|
||||
col_idx++, NULL);
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, se->se_header,
|
||||
renderer, "text",
|
||||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
|
||||
|
||||
g_object_unref(GTK_TREE_MODEL(store));
|
||||
|
||||
total_period = hists->stats.total_period;
|
||||
|
||||
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
GtkTreeIter iter;
|
||||
double percent;
|
||||
char s[512];
|
||||
|
||||
if (h->filtered)
|
||||
continue;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
percent = (h->period * 100.0) / total_period;
|
||||
|
||||
snprintf(s, ARRAY_SIZE(s), "%.2f", percent);
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
if (se->elide)
|
||||
continue;
|
||||
|
||||
se->se_snprintf(h, s, ARRAY_SIZE(s),
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
}
|
||||
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
||||
const char *help __used,
|
||||
void (*timer) (void *arg)__used,
|
||||
void *arg __used, int delay_secs __used)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *window;
|
||||
|
||||
signal(SIGSEGV, perf_gtk_signal);
|
||||
signal(SIGFPE, perf_gtk_signal);
|
||||
signal(SIGINT, perf_gtk_signal);
|
||||
signal(SIGQUIT, perf_gtk_signal);
|
||||
signal(SIGTERM, perf_gtk_signal);
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(window), "perf report");
|
||||
|
||||
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
|
||||
|
||||
notebook = gtk_notebook_new();
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = event_name(pos);
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *tab_label;
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
|
||||
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
|
||||
perf_gtk_show_hists(scrolled_window, hists);
|
||||
|
||||
tab_label = gtk_label_new(evname);
|
||||
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
|
||||
}
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), notebook);
|
||||
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
perf_gtk_resize_window(window);
|
||||
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
8
tools/perf/util/gtk/gtk.h
Normal file
8
tools/perf/util/gtk/gtk.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _PERF_GTK_H_
|
||||
#define _PERF_GTK_H_ 1
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include <gtk/gtk.h>
|
||||
#pragma GCC diagnostic error "-Wstrict-prototypes"
|
||||
|
||||
#endif /* _PERF_GTK_H_ */
|
@ -1177,7 +1177,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
|
||||
goto error;
|
||||
|
||||
msz = sizeof(attr);
|
||||
if (sz < (ssize_t)msz)
|
||||
if (sz < msz)
|
||||
msz = sz;
|
||||
|
||||
for (i = 0 ; i < nre; i++) {
|
||||
|
@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists,
|
||||
struct hist_entry *he);
|
||||
static bool hists__filter_entry_by_thread(struct hists *hists,
|
||||
struct hist_entry *he);
|
||||
static bool hists__filter_entry_by_symbol(struct hists *hists,
|
||||
struct hist_entry *he);
|
||||
|
||||
enum hist_filter {
|
||||
HIST_FILTER__DSO,
|
||||
HIST_FILTER__THREAD,
|
||||
HIST_FILTER__PARENT,
|
||||
HIST_FILTER__SYMBOL,
|
||||
};
|
||||
|
||||
struct callchain_param callchain_param = {
|
||||
@ -420,6 +423,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
|
||||
{
|
||||
hists__filter_entry_by_dso(hists, he);
|
||||
hists__filter_entry_by_thread(hists, he);
|
||||
hists__filter_entry_by_symbol(hists, he);
|
||||
}
|
||||
|
||||
static void __hists__collapse_resort(struct hists *hists, bool threaded)
|
||||
@ -887,9 +891,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
|
||||
diff = new_percent - old_percent;
|
||||
|
||||
if (fabs(diff) >= 0.01)
|
||||
ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
|
||||
scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
|
||||
else
|
||||
ret += scnprintf(bf, sizeof(bf), " ");
|
||||
scnprintf(bf, sizeof(bf), " ");
|
||||
|
||||
if (sep)
|
||||
ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
|
||||
@ -898,9 +902,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
|
||||
|
||||
if (show_displacement) {
|
||||
if (displacement)
|
||||
ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement);
|
||||
scnprintf(bf, sizeof(bf), "%+4ld", displacement);
|
||||
else
|
||||
ret += scnprintf(bf, sizeof(bf), " ");
|
||||
scnprintf(bf, sizeof(bf), " ");
|
||||
|
||||
if (sep)
|
||||
ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
|
||||
@ -1247,6 +1251,37 @@ void hists__filter_by_thread(struct hists *hists)
|
||||
}
|
||||
}
|
||||
|
||||
static bool hists__filter_entry_by_symbol(struct hists *hists,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
if (hists->symbol_filter_str != NULL &&
|
||||
(!he->ms.sym || strstr(he->ms.sym->name,
|
||||
hists->symbol_filter_str) == NULL)) {
|
||||
he->filtered |= (1 << HIST_FILTER__SYMBOL);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void hists__filter_by_symbol(struct hists *hists)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
hists->nr_entries = hists->stats.total_period = 0;
|
||||
hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
|
||||
hists__reset_col_len(hists);
|
||||
|
||||
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
||||
|
||||
if (hists__filter_entry_by_symbol(hists, h))
|
||||
continue;
|
||||
|
||||
hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL);
|
||||
}
|
||||
}
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
|
||||
{
|
||||
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
|
||||
|
@ -62,6 +62,7 @@ struct hists {
|
||||
const struct thread *thread_filter;
|
||||
const struct dso *dso_filter;
|
||||
const char *uid_filter_str;
|
||||
const char *symbol_filter_str;
|
||||
pthread_mutex_t lock;
|
||||
struct events_stats stats;
|
||||
u64 event_stream;
|
||||
@ -107,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize);
|
||||
|
||||
void hists__filter_by_dso(struct hists *hists);
|
||||
void hists__filter_by_thread(struct hists *hists);
|
||||
void hists__filter_by_symbol(struct hists *hists);
|
||||
|
||||
u16 hists__col_len(struct hists *self, enum hist_column col);
|
||||
void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
|
||||
@ -145,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
int refresh);
|
||||
#endif
|
||||
|
||||
#ifdef NO_GTK2_SUPPORT
|
||||
static inline
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used,
|
||||
const char *help __used,
|
||||
void(*timer)(void *arg) __used,
|
||||
void *arg __used,
|
||||
int refresh __used)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int refresh);
|
||||
#endif
|
||||
|
||||
unsigned int hists__sort_list_width(struct hists *self);
|
||||
|
||||
#endif /* __PERF_HIST_H */
|
||||
|
1917
tools/perf/util/parse-events-bison.c
Normal file
1917
tools/perf/util/parse-events-bison.c
Normal file
File diff suppressed because it is too large
Load Diff
81
tools/perf/util/parse-events-bison.h
Normal file
81
tools/perf/util/parse-events-bison.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* A Bison parser, made by GNU Bison 2.5. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
|
||||
/* Tokens. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
/* Put the tokens into the symbol table, so that GDB and other debuggers
|
||||
know about them. */
|
||||
enum yytokentype {
|
||||
PE_VALUE = 258,
|
||||
PE_VALUE_SYM = 259,
|
||||
PE_RAW = 260,
|
||||
PE_TERM = 261,
|
||||
PE_NAME = 262,
|
||||
PE_MODIFIER_EVENT = 263,
|
||||
PE_MODIFIER_BP = 264,
|
||||
PE_NAME_CACHE_TYPE = 265,
|
||||
PE_NAME_CACHE_OP_RESULT = 266,
|
||||
PE_PREFIX_MEM = 267,
|
||||
PE_PREFIX_RAW = 268,
|
||||
PE_ERROR = 269
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 2068 of yacc.c */
|
||||
#line 46 "util/parse-events.y"
|
||||
|
||||
char *str;
|
||||
unsigned long num;
|
||||
struct list_head *head;
|
||||
struct parse_events__term *term;
|
||||
|
||||
|
||||
|
||||
/* Line 2068 of yacc.c */
|
||||
#line 73 "util/parse-events-bison.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE parse_events_lval;
|
||||
|
||||
|
2272
tools/perf/util/parse-events-flex.c
Normal file
2272
tools/perf/util/parse-events-flex.c
Normal file
File diff suppressed because it is too large
Load Diff
316
tools/perf/util/parse-events-flex.h
Normal file
316
tools/perf/util/parse-events-flex.h
Normal file
@ -0,0 +1,316 @@
|
||||
#ifndef parse_events_HEADER_H
|
||||
#define parse_events_HEADER_H 1
|
||||
#define parse_events_IN_HEADER 1
|
||||
|
||||
#line 6 "util/parse-events-flex.h"
|
||||
|
||||
#define YY_INT_ALIGNED short int
|
||||
|
||||
/* A lexical scanner generated by flex */
|
||||
|
||||
#define FLEX_SCANNER
|
||||
#define YY_FLEX_MAJOR_VERSION 2
|
||||
#define YY_FLEX_MINOR_VERSION 5
|
||||
#define YY_FLEX_SUBMINOR_VERSION 35
|
||||
#if YY_FLEX_SUBMINOR_VERSION > 0
|
||||
#define FLEX_BETA
|
||||
#endif
|
||||
|
||||
/* First, we deal with platform-specific or compiler-specific issues. */
|
||||
|
||||
/* begin standard C headers. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* end standard C headers. */
|
||||
|
||||
/* flex integer type definitions */
|
||||
|
||||
#ifndef FLEXINT_H
|
||||
#define FLEXINT_H
|
||||
|
||||
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
|
||||
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
|
||||
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
*/
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
typedef int8_t flex_int8_t;
|
||||
typedef uint8_t flex_uint8_t;
|
||||
typedef int16_t flex_int16_t;
|
||||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
typedef int flex_int32_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned short int flex_uint16_t;
|
||||
typedef unsigned int flex_uint32_t;
|
||||
#endif /* ! C99 */
|
||||
|
||||
/* Limits of integral types. */
|
||||
#ifndef INT8_MIN
|
||||
#define INT8_MIN (-128)
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-32767-1)
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#endif
|
||||
#ifndef INT8_MAX
|
||||
#define INT8_MAX (127)
|
||||
#endif
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX (32767)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX (2147483647)
|
||||
#endif
|
||||
#ifndef UINT8_MAX
|
||||
#define UINT8_MAX (255U)
|
||||
#endif
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (65535U)
|
||||
#endif
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#endif
|
||||
|
||||
#endif /* ! FLEXINT_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/* The "const" storage-class-modifier is valid. */
|
||||
#define YY_USE_CONST
|
||||
|
||||
#else /* ! __cplusplus */
|
||||
|
||||
/* C99 requires __STDC__ to be defined as 1. */
|
||||
#if defined (__STDC__)
|
||||
|
||||
#define YY_USE_CONST
|
||||
|
||||
#endif /* defined (__STDC__) */
|
||||
#endif /* ! __cplusplus */
|
||||
|
||||
#ifdef YY_USE_CONST
|
||||
#define yyconst const
|
||||
#else
|
||||
#define yyconst
|
||||
#endif
|
||||
|
||||
/* Size of default input buffer. */
|
||||
#ifndef YY_BUF_SIZE
|
||||
#define YY_BUF_SIZE 16384
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
|
||||
#define YY_TYPEDEF_YY_BUFFER_STATE
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
extern int parse_events_leng;
|
||||
|
||||
extern FILE *parse_events_in, *parse_events_out;
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state
|
||||
{
|
||||
FILE *yy_input_file;
|
||||
|
||||
char *yy_ch_buf; /* input buffer */
|
||||
char *yy_buf_pos; /* current position in input buffer */
|
||||
|
||||
/* Size of input buffer in bytes, not including room for EOB
|
||||
* characters.
|
||||
*/
|
||||
yy_size_t yy_buf_size;
|
||||
|
||||
/* Number of characters read into yy_ch_buf, not including EOB
|
||||
* characters.
|
||||
*/
|
||||
int yy_n_chars;
|
||||
|
||||
/* Whether we "own" the buffer - i.e., we know we created it,
|
||||
* and can realloc() it to grow it, and should free() it to
|
||||
* delete it.
|
||||
*/
|
||||
int yy_is_our_buffer;
|
||||
|
||||
/* Whether this is an "interactive" input source; if so, and
|
||||
* if we're using stdio for input, then we want to use getc()
|
||||
* instead of fread(), to make sure we stop fetching input after
|
||||
* each newline.
|
||||
*/
|
||||
int yy_is_interactive;
|
||||
|
||||
/* Whether we're considered to be at the beginning of a line.
|
||||
* If so, '^' rules will be active on the next match, otherwise
|
||||
* not.
|
||||
*/
|
||||
int yy_at_bol;
|
||||
|
||||
int yy_bs_lineno; /**< The line count. */
|
||||
int yy_bs_column; /**< The column count. */
|
||||
|
||||
/* Whether to try to fill the input buffer when we reach the
|
||||
* end of it.
|
||||
*/
|
||||
int yy_fill_buffer;
|
||||
|
||||
int yy_buffer_status;
|
||||
|
||||
};
|
||||
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
|
||||
|
||||
void parse_events_restart (FILE *input_file );
|
||||
void parse_events__switch_to_buffer (YY_BUFFER_STATE new_buffer );
|
||||
YY_BUFFER_STATE parse_events__create_buffer (FILE *file,int size );
|
||||
void parse_events__delete_buffer (YY_BUFFER_STATE b );
|
||||
void parse_events__flush_buffer (YY_BUFFER_STATE b );
|
||||
void parse_events_push_buffer_state (YY_BUFFER_STATE new_buffer );
|
||||
void parse_events_pop_buffer_state (void );
|
||||
|
||||
YY_BUFFER_STATE parse_events__scan_buffer (char *base,yy_size_t size );
|
||||
YY_BUFFER_STATE parse_events__scan_string (yyconst char *yy_str );
|
||||
YY_BUFFER_STATE parse_events__scan_bytes (yyconst char *bytes,int len );
|
||||
|
||||
void *parse_events_alloc (yy_size_t );
|
||||
void *parse_events_realloc (void *,yy_size_t );
|
||||
void parse_events_free (void * );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
extern int parse_events_lineno;
|
||||
|
||||
extern char *parse_events_text;
|
||||
#define yytext_ptr parse_events_text
|
||||
|
||||
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
|
||||
#define INITIAL 0
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_UNISTD_H
|
||||
/* Special case for "unistd.h", since it is non-ANSI. We include it way
|
||||
* down here because we want the user's section 1 to have been scanned first.
|
||||
* The user has a chance to override it with an option.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef YY_EXTRA_TYPE
|
||||
#define YY_EXTRA_TYPE void *
|
||||
#endif
|
||||
|
||||
/* Accessor methods to globals.
|
||||
These are made visible to non-reentrant scanners for convenience. */
|
||||
|
||||
int parse_events_lex_destroy (void );
|
||||
|
||||
int parse_events_get_debug (void );
|
||||
|
||||
void parse_events_set_debug (int debug_flag );
|
||||
|
||||
YY_EXTRA_TYPE parse_events_get_extra (void );
|
||||
|
||||
void parse_events_set_extra (YY_EXTRA_TYPE user_defined );
|
||||
|
||||
FILE *parse_events_get_in (void );
|
||||
|
||||
void parse_events_set_in (FILE * in_str );
|
||||
|
||||
FILE *parse_events_get_out (void );
|
||||
|
||||
void parse_events_set_out (FILE * out_str );
|
||||
|
||||
int parse_events_get_leng (void );
|
||||
|
||||
char *parse_events_get_text (void );
|
||||
|
||||
int parse_events_get_lineno (void );
|
||||
|
||||
void parse_events_set_lineno (int line_number );
|
||||
|
||||
/* Macros after this point can all be overridden by user definitions in
|
||||
* section 1.
|
||||
*/
|
||||
|
||||
#ifndef YY_SKIP_YYWRAP
|
||||
#ifdef __cplusplus
|
||||
extern "C" int parse_events_wrap (void );
|
||||
#else
|
||||
extern int parse_events_wrap (void );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef yytext_ptr
|
||||
static void yy_flex_strncpy (char *,yyconst char *,int );
|
||||
#endif
|
||||
|
||||
#ifdef YY_NEED_STRLEN
|
||||
static int yy_flex_strlen (yyconst char * );
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_INPUT
|
||||
|
||||
#endif
|
||||
|
||||
/* Amount of stuff to slurp up with each read. */
|
||||
#ifndef YY_READ_BUF_SIZE
|
||||
#define YY_READ_BUF_SIZE 8192
|
||||
#endif
|
||||
|
||||
/* Number of entries by which start-condition stack grows. */
|
||||
#ifndef YY_START_STACK_INCR
|
||||
#define YY_START_STACK_INCR 25
|
||||
#endif
|
||||
|
||||
/* Default declaration of generated scanner - a define so the user can
|
||||
* easily add parameters.
|
||||
*/
|
||||
#ifndef YY_DECL
|
||||
#define YY_DECL_IS_OURS 1
|
||||
|
||||
extern int parse_events_lex (void);
|
||||
|
||||
#define YY_DECL int parse_events_lex (void)
|
||||
#endif /* !YY_DECL */
|
||||
|
||||
/* yy_get_previous_state - get the state just before the EOB char was reached */
|
||||
|
||||
#undef YY_NEW_FILE
|
||||
#undef YY_FLUSH_BUFFER
|
||||
#undef yy_set_bol
|
||||
#undef yy_new_buffer
|
||||
#undef yy_set_interactive
|
||||
#undef YY_DO_BEFORE_ACTION
|
||||
|
||||
#ifdef YY_DECL_IS_OURS
|
||||
#undef YY_DECL_IS_OURS
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 121 "util/parse-events.l"
|
||||
|
||||
|
||||
#line 315 "util/parse-events-flex.h"
|
||||
#undef parse_events_IN_HEADER
|
||||
#endif /* parse_events_HEADER_H */
|
@ -11,6 +11,10 @@
|
||||
#include "cache.h"
|
||||
#include "header.h"
|
||||
#include "debugfs.h"
|
||||
#include "parse-events-flex.h"
|
||||
#include "pmu.h"
|
||||
|
||||
#define MAX_NAME_LEN 100
|
||||
|
||||
struct event_symbol {
|
||||
u8 type;
|
||||
@ -19,11 +23,8 @@ struct event_symbol {
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
enum event_result {
|
||||
EVT_FAILED,
|
||||
EVT_HANDLED,
|
||||
EVT_HANDLED_ALL
|
||||
};
|
||||
int parse_events_parse(struct list_head *list, struct list_head *list_tmp,
|
||||
int *idx);
|
||||
|
||||
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
|
||||
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
|
||||
@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
|
||||
static int add_event(struct list_head *list, int *idx,
|
||||
struct perf_event_attr *attr, char *name)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
event_attr_init(attr);
|
||||
|
||||
evsel = perf_evsel__new(attr, (*idx)++);
|
||||
if (!evsel)
|
||||
return -ENOMEM;
|
||||
|
||||
list_add_tail(&evsel->node, list);
|
||||
|
||||
evsel->name = strdup(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
|
||||
{
|
||||
int i, j;
|
||||
int n, longest = -1;
|
||||
@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
|
||||
for (i = 0; i < size; i++) {
|
||||
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
|
||||
n = strlen(names[i][j]);
|
||||
if (n > longest && !strncasecmp(*str, names[i][j], n))
|
||||
if (n > longest && !strncasecmp(str, names[i][j], n))
|
||||
longest = n;
|
||||
}
|
||||
if (longest > 0) {
|
||||
*str += longest;
|
||||
if (longest > 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
|
||||
int parse_events_add_cache(struct list_head *list, int *idx,
|
||||
char *type, char *op_result1, char *op_result2)
|
||||
{
|
||||
const char *s = *str;
|
||||
struct perf_event_attr attr;
|
||||
char name[MAX_NAME_LEN];
|
||||
int cache_type = -1, cache_op = -1, cache_result = -1;
|
||||
char *op_result[2] = { op_result1, op_result2 };
|
||||
int i, n;
|
||||
|
||||
cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
|
||||
/*
|
||||
* No fallback - if we cannot get a clear cache type
|
||||
* then bail out:
|
||||
*/
|
||||
cache_type = parse_aliases(type, hw_cache,
|
||||
PERF_COUNT_HW_CACHE_MAX);
|
||||
if (cache_type == -1)
|
||||
return EVT_FAILED;
|
||||
return -EINVAL;
|
||||
|
||||
while ((cache_op == -1 || cache_result == -1) && *s == '-') {
|
||||
++s;
|
||||
n = snprintf(name, MAX_NAME_LEN, "%s", type);
|
||||
|
||||
for (i = 0; (i < 2) && (op_result[i]); i++) {
|
||||
char *str = op_result[i];
|
||||
|
||||
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
|
||||
|
||||
if (cache_op == -1) {
|
||||
cache_op = parse_aliases(&s, hw_cache_op,
|
||||
PERF_COUNT_HW_CACHE_OP_MAX);
|
||||
cache_op = parse_aliases(str, hw_cache_op,
|
||||
PERF_COUNT_HW_CACHE_OP_MAX);
|
||||
if (cache_op >= 0) {
|
||||
if (!is_cache_op_valid(cache_type, cache_op))
|
||||
return EVT_FAILED;
|
||||
return -EINVAL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache_result == -1) {
|
||||
cache_result = parse_aliases(&s, hw_cache_result,
|
||||
cache_result = parse_aliases(str, hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
if (cache_result >= 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can't parse this as a cache op or result, so back up
|
||||
* to the '-'.
|
||||
*/
|
||||
--s;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
|
||||
if (cache_result == -1)
|
||||
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
|
||||
|
||||
attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
|
||||
attr->type = PERF_TYPE_HW_CACHE;
|
||||
|
||||
*str = s;
|
||||
return EVT_HANDLED;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
|
||||
attr.type = PERF_TYPE_HW_CACHE;
|
||||
return add_event(list, idx, &attr, name);
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_single_tracepoint_event(char *sys_name,
|
||||
const char *evt_name,
|
||||
unsigned int evt_length,
|
||||
struct perf_event_attr *attr,
|
||||
const char **strp)
|
||||
static int add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
char name[MAX_NAME_LEN];
|
||||
char evt_path[MAXPATHLEN];
|
||||
char id_buf[4];
|
||||
u64 id;
|
||||
@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name,
|
||||
|
||||
fd = open(evt_path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return EVT_FAILED;
|
||||
return -1;
|
||||
|
||||
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
||||
close(fd);
|
||||
return EVT_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
id = atoll(id_buf);
|
||||
attr->config = id;
|
||||
attr->type = PERF_TYPE_TRACEPOINT;
|
||||
*strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
|
||||
|
||||
attr->sample_type |= PERF_SAMPLE_RAW;
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.config = id;
|
||||
attr.type = PERF_TYPE_TRACEPOINT;
|
||||
attr.sample_type |= PERF_SAMPLE_RAW;
|
||||
attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
attr.sample_type |= PERF_SAMPLE_CPU;
|
||||
attr.sample_period = 1;
|
||||
|
||||
attr->sample_period = 1;
|
||||
|
||||
|
||||
return EVT_HANDLED;
|
||||
snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
|
||||
return add_event(list, idx, &attr, name);
|
||||
}
|
||||
|
||||
/* sys + ':' + event + ':' + flags*/
|
||||
#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
|
||||
static enum event_result
|
||||
parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
|
||||
const char *evt_exp, char *flags)
|
||||
static int add_tracepoint_multi(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
char evt_path[MAXPATHLEN];
|
||||
struct dirent *evt_ent;
|
||||
DIR *evt_dir;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
|
||||
evt_dir = opendir(evt_path);
|
||||
|
||||
if (!evt_dir) {
|
||||
perror("Can't open event dir");
|
||||
return EVT_FAILED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((evt_ent = readdir(evt_dir))) {
|
||||
char event_opt[MAX_EVOPT_LEN + 1];
|
||||
int len;
|
||||
|
||||
while (!ret && (evt_ent = readdir(evt_dir))) {
|
||||
if (!strcmp(evt_ent->d_name, ".")
|
||||
|| !strcmp(evt_ent->d_name, "..")
|
||||
|| !strcmp(evt_ent->d_name, "enable")
|
||||
|| !strcmp(evt_ent->d_name, "filter"))
|
||||
continue;
|
||||
|
||||
if (!strglobmatch(evt_ent->d_name, evt_exp))
|
||||
if (!strglobmatch(evt_ent->d_name, evt_name))
|
||||
continue;
|
||||
|
||||
len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
|
||||
evt_ent->d_name, flags ? ":" : "",
|
||||
flags ?: "");
|
||||
if (len < 0)
|
||||
return EVT_FAILED;
|
||||
|
||||
if (parse_events(evlist, event_opt, 0))
|
||||
return EVT_FAILED;
|
||||
ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
|
||||
}
|
||||
|
||||
return EVT_HANDLED_ALL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
|
||||
struct perf_event_attr *attr)
|
||||
int parse_events_add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys, char *event)
|
||||
{
|
||||
const char *evt_name;
|
||||
char *flags = NULL, *comma_loc;
|
||||
char sys_name[MAX_EVENT_LENGTH];
|
||||
unsigned int sys_length, evt_length;
|
||||
int ret;
|
||||
|
||||
if (debugfs_valid_mountpoint(tracing_events_path))
|
||||
return 0;
|
||||
ret = debugfs_valid_mountpoint(tracing_events_path);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
evt_name = strchr(*strp, ':');
|
||||
if (!evt_name)
|
||||
return EVT_FAILED;
|
||||
|
||||
sys_length = evt_name - *strp;
|
||||
if (sys_length >= MAX_EVENT_LENGTH)
|
||||
return 0;
|
||||
|
||||
strncpy(sys_name, *strp, sys_length);
|
||||
sys_name[sys_length] = '\0';
|
||||
evt_name = evt_name + 1;
|
||||
|
||||
comma_loc = strchr(evt_name, ',');
|
||||
if (comma_loc) {
|
||||
/* take the event name up to the comma */
|
||||
evt_name = strndup(evt_name, comma_loc - evt_name);
|
||||
}
|
||||
flags = strchr(evt_name, ':');
|
||||
if (flags) {
|
||||
/* split it out: */
|
||||
evt_name = strndup(evt_name, flags - evt_name);
|
||||
flags++;
|
||||
}
|
||||
|
||||
evt_length = strlen(evt_name);
|
||||
if (evt_length >= MAX_EVENT_LENGTH)
|
||||
return EVT_FAILED;
|
||||
if (strpbrk(evt_name, "*?")) {
|
||||
*strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
|
||||
return parse_multiple_tracepoint_event(evlist, sys_name,
|
||||
evt_name, flags);
|
||||
} else {
|
||||
return parse_single_tracepoint_event(sys_name, evt_name,
|
||||
evt_length, attr, strp);
|
||||
}
|
||||
return strpbrk(event, "*?") ?
|
||||
add_tracepoint_multi(list, idx, sys, event) :
|
||||
add_tracepoint(list, idx, sys, event);
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_breakpoint_type(const char *type, const char **strp,
|
||||
struct perf_event_attr *attr)
|
||||
static int
|
||||
parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!type[i])
|
||||
if (!type || !type[i])
|
||||
break;
|
||||
|
||||
switch (type[i]) {
|
||||
@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp,
|
||||
attr->bp_type |= HW_BREAKPOINT_X;
|
||||
break;
|
||||
default:
|
||||
return EVT_FAILED;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!attr->bp_type) /* Default */
|
||||
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
|
||||
|
||||
*strp = type + i;
|
||||
|
||||
return EVT_HANDLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
|
||||
int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
void *ptr, char *type)
|
||||
{
|
||||
const char *target;
|
||||
const char *type;
|
||||
char *endaddr;
|
||||
u64 addr;
|
||||
enum event_result err;
|
||||
struct perf_event_attr attr;
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
target = strchr(*strp, ':');
|
||||
if (!target)
|
||||
return EVT_FAILED;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.bp_addr = (unsigned long) ptr;
|
||||
|
||||
if (strncmp(*strp, "mem", target - *strp) != 0)
|
||||
return EVT_FAILED;
|
||||
|
||||
target++;
|
||||
|
||||
addr = strtoull(target, &endaddr, 0);
|
||||
if (target == endaddr)
|
||||
return EVT_FAILED;
|
||||
|
||||
attr->bp_addr = addr;
|
||||
*strp = endaddr;
|
||||
|
||||
type = strchr(target, ':');
|
||||
|
||||
/* If no type is defined, just rw as default */
|
||||
if (!type) {
|
||||
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
|
||||
} else {
|
||||
err = parse_breakpoint_type(++type, strp, attr);
|
||||
if (err == EVT_FAILED)
|
||||
return EVT_FAILED;
|
||||
}
|
||||
if (parse_breakpoint_type(type, &attr))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We should find a nice way to override the access length
|
||||
* Provide some defaults for now
|
||||
*/
|
||||
if (attr->bp_type == HW_BREAKPOINT_X)
|
||||
attr->bp_len = sizeof(long);
|
||||
if (attr.bp_type == HW_BREAKPOINT_X)
|
||||
attr.bp_len = sizeof(long);
|
||||
else
|
||||
attr->bp_len = HW_BREAKPOINT_LEN_4;
|
||||
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
||||
|
||||
attr->type = PERF_TYPE_BREAKPOINT;
|
||||
attr.type = PERF_TYPE_BREAKPOINT;
|
||||
|
||||
return EVT_HANDLED;
|
||||
snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
|
||||
return add_event(list, idx, &attr, name);
|
||||
}
|
||||
|
||||
static int check_events(const char *str, unsigned int i)
|
||||
static int config_term(struct perf_event_attr *attr,
|
||||
struct parse_events__term *term)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = strlen(event_symbols[i].symbol);
|
||||
if (!strncasecmp(str, event_symbols[i].symbol, n))
|
||||
return n;
|
||||
|
||||
n = strlen(event_symbols[i].alias);
|
||||
if (n) {
|
||||
if (!strncasecmp(str, event_symbols[i].alias, n))
|
||||
return n;
|
||||
switch (term->type) {
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG:
|
||||
attr->config = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG1:
|
||||
attr->config1 = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
|
||||
attr->config2 = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
attr->sample_period = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
|
||||
/*
|
||||
* TODO uncomment when the field is available
|
||||
* attr->branch_sample_type = term->val.num;
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_attr(struct perf_event_attr *attr,
|
||||
struct list_head *head, int fail)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
list_for_each_entry(term, head, list)
|
||||
if (config_term(attr, term) && fail)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
|
||||
int parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
unsigned long type, unsigned long config,
|
||||
struct list_head *head_config)
|
||||
{
|
||||
const char *str = *strp;
|
||||
unsigned int i;
|
||||
int n;
|
||||
struct perf_event_attr attr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
|
||||
n = check_events(str, i);
|
||||
if (n > 0) {
|
||||
attr->type = event_symbols[i].type;
|
||||
attr->config = event_symbols[i].config;
|
||||
*strp = str + n;
|
||||
return EVT_HANDLED;
|
||||
}
|
||||
}
|
||||
return EVT_FAILED;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = type;
|
||||
attr.config = config;
|
||||
|
||||
if (head_config &&
|
||||
config_attr(&attr, head_config, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr,
|
||||
(char *) __event_name(type, config));
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_raw_event(const char **strp, struct perf_event_attr *attr)
|
||||
int parse_events_add_pmu(struct list_head *list, int *idx,
|
||||
char *name, struct list_head *head_config)
|
||||
{
|
||||
const char *str = *strp;
|
||||
u64 config;
|
||||
int n;
|
||||
struct perf_event_attr attr;
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
if (*str != 'r')
|
||||
return EVT_FAILED;
|
||||
n = hex2u64(str + 1, &config);
|
||||
if (n > 0) {
|
||||
const char *end = str + n + 1;
|
||||
if (*end != '\0' && *end != ',' && *end != ':')
|
||||
return EVT_FAILED;
|
||||
pmu = perf_pmu__find(name);
|
||||
if (!pmu)
|
||||
return -EINVAL;
|
||||
|
||||
*strp = end;
|
||||
attr->type = PERF_TYPE_RAW;
|
||||
attr->config = config;
|
||||
return EVT_HANDLED;
|
||||
}
|
||||
return EVT_FAILED;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
/*
|
||||
* Configure hardcoded terms first, no need to check
|
||||
* return value when called with fail == 0 ;)
|
||||
*/
|
||||
config_attr(&attr, head_config, 0);
|
||||
|
||||
if (perf_pmu__config(pmu, &attr, head_config))
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr, (char *) "pmu");
|
||||
}
|
||||
|
||||
static enum event_result
|
||||
parse_numeric_event(const char **strp, struct perf_event_attr *attr)
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all)
|
||||
{
|
||||
const char *str = *strp;
|
||||
char *endp;
|
||||
unsigned long type;
|
||||
u64 config;
|
||||
|
||||
type = strtoul(str, &endp, 0);
|
||||
if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
|
||||
str = endp + 1;
|
||||
config = strtoul(str, &endp, 0);
|
||||
if (endp > str) {
|
||||
attr->type = type;
|
||||
attr->config = config;
|
||||
*strp = endp;
|
||||
return EVT_HANDLED;
|
||||
}
|
||||
}
|
||||
return EVT_FAILED;
|
||||
/*
|
||||
* Called for single event definition. Update the
|
||||
* 'all event' list, and reinit the 'signle event'
|
||||
* list, for next event definition.
|
||||
*/
|
||||
list_splice_tail(list_event, list_all);
|
||||
INIT_LIST_HEAD(list_event);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_event_modifier(const char **strp, struct perf_event_attr *attr)
|
||||
int parse_events_modifier(struct list_head *list, char *str)
|
||||
{
|
||||
const char *str = *strp;
|
||||
struct perf_evsel *evsel;
|
||||
int exclude = 0, exclude_GH = 0;
|
||||
int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
|
||||
|
||||
if (!*str)
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
if (*str == ',')
|
||||
return 0;
|
||||
|
||||
if (*str++ != ':')
|
||||
return -1;
|
||||
|
||||
while (*str) {
|
||||
if (*str == 'u') {
|
||||
if (!exclude)
|
||||
@ -775,111 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
|
||||
|
||||
++str;
|
||||
}
|
||||
if (str < *strp + 2)
|
||||
return -1;
|
||||
|
||||
*strp = str;
|
||||
/*
|
||||
* precise ip:
|
||||
*
|
||||
* 0 - SAMPLE_IP can have arbitrary skid
|
||||
* 1 - SAMPLE_IP must have constant skid
|
||||
* 2 - SAMPLE_IP requested to have 0 skid
|
||||
* 3 - SAMPLE_IP must have 0 skid
|
||||
*
|
||||
* See also PERF_RECORD_MISC_EXACT_IP
|
||||
*/
|
||||
if (precise > 3)
|
||||
return -EINVAL;
|
||||
|
||||
attr->exclude_user = eu;
|
||||
attr->exclude_kernel = ek;
|
||||
attr->exclude_hv = eh;
|
||||
attr->precise_ip = precise;
|
||||
attr->exclude_host = eH;
|
||||
attr->exclude_guest = eG;
|
||||
list_for_each_entry(evsel, list, node) {
|
||||
evsel->attr.exclude_user = eu;
|
||||
evsel->attr.exclude_kernel = ek;
|
||||
evsel->attr.exclude_hv = eh;
|
||||
evsel->attr.precise_ip = precise;
|
||||
evsel->attr.exclude_host = eH;
|
||||
evsel->attr.exclude_guest = eG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each event can have multiple symbolic names.
|
||||
* Symbolic names are (almost) exactly matched.
|
||||
*/
|
||||
static enum event_result
|
||||
parse_event_symbols(struct perf_evlist *evlist, const char **str,
|
||||
struct perf_event_attr *attr)
|
||||
int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
|
||||
{
|
||||
enum event_result ret;
|
||||
LIST_HEAD(list);
|
||||
LIST_HEAD(list_tmp);
|
||||
YY_BUFFER_STATE buffer;
|
||||
int ret, idx = evlist->nr_entries;
|
||||
|
||||
ret = parse_tracepoint_event(evlist, str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
buffer = parse_events__scan_string(str);
|
||||
|
||||
ret = parse_raw_event(str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
ret = parse_events_parse(&list, &list_tmp, &idx);
|
||||
|
||||
ret = parse_numeric_event(str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
parse_events__flush_buffer(buffer);
|
||||
parse_events__delete_buffer(buffer);
|
||||
|
||||
ret = parse_symbolic_event(str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
if (!ret) {
|
||||
int entries = idx - evlist->nr_entries;
|
||||
perf_evlist__splice_list_tail(evlist, &list, entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_generic_hw_event(str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
|
||||
ret = parse_breakpoint_event(str, attr);
|
||||
if (ret != EVT_FAILED)
|
||||
goto modifier;
|
||||
|
||||
fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
|
||||
/*
|
||||
* There are 2 users - builtin-record and builtin-test objects.
|
||||
* Both call perf_evlist__delete in case of error, so we dont
|
||||
* need to bother.
|
||||
*/
|
||||
fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
|
||||
fprintf(stderr, "Run 'perf list' for a list of valid events\n");
|
||||
return EVT_FAILED;
|
||||
|
||||
modifier:
|
||||
if (parse_event_modifier(str, attr) < 0) {
|
||||
fprintf(stderr, "invalid event modifier: '%s'\n", *str);
|
||||
fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n");
|
||||
|
||||
return EVT_FAILED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
enum event_result ret;
|
||||
const char *ostr;
|
||||
|
||||
for (;;) {
|
||||
ostr = str;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
event_attr_init(&attr);
|
||||
ret = parse_event_symbols(evlist, &str, &attr);
|
||||
if (ret == EVT_FAILED)
|
||||
return -1;
|
||||
|
||||
if (!(*str == 0 || *str == ',' || isspace(*str)))
|
||||
return -1;
|
||||
|
||||
if (ret != EVT_HANDLED_ALL) {
|
||||
struct perf_evsel *evsel;
|
||||
evsel = perf_evsel__new(&attr, evlist->nr_entries);
|
||||
if (evsel == NULL)
|
||||
return -1;
|
||||
perf_evlist__add(evlist, evsel);
|
||||
|
||||
evsel->name = calloc(str - ostr + 1, 1);
|
||||
if (!evsel->name)
|
||||
return -1;
|
||||
strncpy(evsel->name, ostr, str - ostr);
|
||||
}
|
||||
|
||||
if (*str == 0)
|
||||
break;
|
||||
if (*str == ',')
|
||||
++str;
|
||||
while (isspace(*str))
|
||||
++str;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_events_option(const struct option *opt, const char *str,
|
||||
int unset __used)
|
||||
{
|
||||
@ -1052,8 +949,6 @@ int print_hwcache_events(const char *event_glob)
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define MAX_NAME_LEN 100
|
||||
|
||||
/*
|
||||
* Print the help text for the event symbols:
|
||||
*/
|
||||
@ -1102,8 +997,12 @@ void print_events(const char *event_glob)
|
||||
|
||||
printf("\n");
|
||||
printf(" %-50s [%s]\n",
|
||||
"rNNN (see 'perf list --help' on how to encode it)",
|
||||
"rNNN",
|
||||
event_type_descriptors[PERF_TYPE_RAW]);
|
||||
printf(" %-50s [%s]\n",
|
||||
"cpu/t1=v1[,t2=v2,t3 ...]/modifier",
|
||||
event_type_descriptors[PERF_TYPE_RAW]);
|
||||
printf(" (see 'perf list --help' on how to encode it)\n");
|
||||
printf("\n");
|
||||
|
||||
printf(" %-50s [%s]\n",
|
||||
@ -1113,3 +1012,51 @@ void print_events(const char *event_glob)
|
||||
|
||||
print_tracepoint_events(NULL, NULL);
|
||||
}
|
||||
|
||||
int parse_events__is_hardcoded_term(struct parse_events__term *term)
|
||||
{
|
||||
return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
|
||||
}
|
||||
|
||||
int parse_events__new_term(struct parse_events__term **_term, int type,
|
||||
char *config, char *str, long num)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
term = zalloc(sizeof(*term));
|
||||
if (!term)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&term->list);
|
||||
term->type = type;
|
||||
term->config = config;
|
||||
|
||||
switch (type) {
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG:
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG1:
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
|
||||
case PARSE_EVENTS__TERM_TYPE_NUM:
|
||||
term->val.num = num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_STR:
|
||||
term->val.str = str;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*_term = term;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parse_events__free_terms(struct list_head *terms)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
|
||||
list_for_each_entry_safe(term, h, terms, list)
|
||||
free(term);
|
||||
|
||||
free(terms);
|
||||
}
|
||||
|
@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
|
||||
|
||||
#define EVENTS_HELP_MAX (128*1024)
|
||||
|
||||
enum {
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG,
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG1,
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG2,
|
||||
PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
PARSE_EVENTS__TERM_TYPE_STR,
|
||||
|
||||
PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX =
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
};
|
||||
|
||||
struct parse_events__term {
|
||||
char *config;
|
||||
union {
|
||||
char *str;
|
||||
long num;
|
||||
} val;
|
||||
int type;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int parse_events__is_hardcoded_term(struct parse_events__term *term);
|
||||
int parse_events__new_term(struct parse_events__term **term, int type,
|
||||
char *config, char *str, long num);
|
||||
void parse_events__free_terms(struct list_head *terms);
|
||||
int parse_events_modifier(struct list_head *list __used, char *str __used);
|
||||
int parse_events_add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys, char *event);
|
||||
int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config,
|
||||
unsigned long config1, unsigned long config2,
|
||||
char *mod);
|
||||
int parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
unsigned long type, unsigned long config,
|
||||
struct list_head *head_config);
|
||||
int parse_events_add_cache(struct list_head *list, int *idx,
|
||||
char *type, char *op_result1, char *op_result2);
|
||||
int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
void *ptr, char *type);
|
||||
int parse_events_add_pmu(struct list_head *list, int *idx,
|
||||
char *pmu , struct list_head *head_config);
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all);
|
||||
void parse_events_error(struct list_head *list_all,
|
||||
struct list_head *list_event,
|
||||
int *idx, char const *msg);
|
||||
|
||||
void print_events(const char *event_glob);
|
||||
void print_events_type(u8 type);
|
||||
void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
|
||||
|
126
tools/perf/util/parse-events.l
Normal file
126
tools/perf/util/parse-events.l
Normal file
@ -0,0 +1,126 @@
|
||||
|
||||
%option prefix="parse_events_"
|
||||
|
||||
%{
|
||||
#include <errno.h>
|
||||
#include "../perf.h"
|
||||
#include "parse-events-bison.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
static int __value(char *str, int base, int token)
|
||||
{
|
||||
long num;
|
||||
|
||||
errno = 0;
|
||||
num = strtoul(str, NULL, base);
|
||||
if (errno)
|
||||
return PE_ERROR;
|
||||
|
||||
parse_events_lval.num = num;
|
||||
return token;
|
||||
}
|
||||
|
||||
static int value(int base)
|
||||
{
|
||||
return __value(parse_events_text, base, PE_VALUE);
|
||||
}
|
||||
|
||||
static int raw(void)
|
||||
{
|
||||
return __value(parse_events_text + 1, 16, PE_RAW);
|
||||
}
|
||||
|
||||
static int str(int token)
|
||||
{
|
||||
parse_events_lval.str = strdup(parse_events_text);
|
||||
return token;
|
||||
}
|
||||
|
||||
static int sym(int type, int config)
|
||||
{
|
||||
parse_events_lval.num = (type << 16) + config;
|
||||
return PE_VALUE_SYM;
|
||||
}
|
||||
|
||||
static int term(int type)
|
||||
{
|
||||
parse_events_lval.num = type;
|
||||
return PE_TERM;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
num_dec [0-9]+
|
||||
num_hex 0x[a-fA-F0-9]+
|
||||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
||||
modifier_event [ukhp]{1,5}
|
||||
modifier_bp [rwx]
|
||||
|
||||
%%
|
||||
cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
|
||||
stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
|
||||
stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
|
||||
instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
|
||||
cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
|
||||
cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
|
||||
branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
|
||||
branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
|
||||
bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
|
||||
cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
|
||||
task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
|
||||
page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
|
||||
minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
|
||||
major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
|
||||
context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
|
||||
cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
|
||||
alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
|
||||
emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
|
||||
|
||||
L1-dcache|l1-d|l1d|L1-data |
|
||||
L1-icache|l1-i|l1i|L1-instruction |
|
||||
LLC|L2 |
|
||||
dTLB|d-tlb|Data-TLB |
|
||||
iTLB|i-tlb|Instruction-TLB |
|
||||
branch|branches|bpu|btb|bpc |
|
||||
node { return str(PE_NAME_CACHE_TYPE); }
|
||||
|
||||
load|loads|read |
|
||||
store|stores|write |
|
||||
prefetch|prefetches |
|
||||
speculative-read|speculative-load |
|
||||
refs|Reference|ops|access |
|
||||
misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
|
||||
|
||||
/*
|
||||
* These are event config hardcoded term names to be specified
|
||||
* within xxx/.../ syntax. So far we dont clash with other names,
|
||||
* so we can put them here directly. In case the we have a conflict
|
||||
* in future, this needs to go into '//' condition block.
|
||||
*/
|
||||
config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
|
||||
config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
|
||||
mem: { return PE_PREFIX_MEM; }
|
||||
r{num_raw_hex} { return raw(); }
|
||||
{num_dec} { return value(10); }
|
||||
{num_hex} { return value(16); }
|
||||
|
||||
{modifier_event} { return str(PE_MODIFIER_EVENT); }
|
||||
{modifier_bp} { return str(PE_MODIFIER_BP); }
|
||||
{name} { return str(PE_NAME); }
|
||||
"/" { return '/'; }
|
||||
- { return '-'; }
|
||||
, { return ','; }
|
||||
: { return ':'; }
|
||||
= { return '='; }
|
||||
|
||||
%%
|
||||
|
||||
int parse_events_wrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
229
tools/perf/util/parse-events.y
Normal file
229
tools/perf/util/parse-events.y
Normal file
@ -0,0 +1,229 @@
|
||||
|
||||
%name-prefix "parse_events_"
|
||||
%parse-param {struct list_head *list_all}
|
||||
%parse-param {struct list_head *list_event}
|
||||
%parse-param {int *idx}
|
||||
|
||||
%{
|
||||
|
||||
#define YYDEBUG 1
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
extern int parse_events_lex (void);
|
||||
|
||||
#define ABORT_ON(val) \
|
||||
do { \
|
||||
if (val) \
|
||||
YYABORT; \
|
||||
} while (0)
|
||||
|
||||
%}
|
||||
|
||||
%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
|
||||
%token PE_NAME
|
||||
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
|
||||
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
|
||||
%token PE_PREFIX_MEM PE_PREFIX_RAW
|
||||
%token PE_ERROR
|
||||
%type <num> PE_VALUE
|
||||
%type <num> PE_VALUE_SYM
|
||||
%type <num> PE_RAW
|
||||
%type <num> PE_TERM
|
||||
%type <str> PE_NAME
|
||||
%type <str> PE_NAME_CACHE_TYPE
|
||||
%type <str> PE_NAME_CACHE_OP_RESULT
|
||||
%type <str> PE_MODIFIER_EVENT
|
||||
%type <str> PE_MODIFIER_BP
|
||||
%type <head> event_config
|
||||
%type <term> event_term
|
||||
|
||||
%union
|
||||
{
|
||||
char *str;
|
||||
unsigned long num;
|
||||
struct list_head *head;
|
||||
struct parse_events__term *term;
|
||||
}
|
||||
%%
|
||||
|
||||
events:
|
||||
events ',' event | event
|
||||
|
||||
event:
|
||||
event_def PE_MODIFIER_EVENT
|
||||
{
|
||||
/*
|
||||
* Apply modifier on all events added by single event definition
|
||||
* (there could be more events added for multiple tracepoint
|
||||
* definitions via '*?'.
|
||||
*/
|
||||
ABORT_ON(parse_events_modifier(list_event, $2));
|
||||
parse_events_update_lists(list_event, list_all);
|
||||
}
|
||||
|
|
||||
event_def
|
||||
{
|
||||
parse_events_update_lists(list_event, list_all);
|
||||
}
|
||||
|
||||
event_def: event_pmu |
|
||||
event_legacy_symbol |
|
||||
event_legacy_cache sep_dc |
|
||||
event_legacy_mem |
|
||||
event_legacy_tracepoint sep_dc |
|
||||
event_legacy_numeric sep_dc |
|
||||
event_legacy_raw sep_dc
|
||||
|
||||
event_pmu:
|
||||
PE_NAME '/' event_config '/'
|
||||
{
|
||||
ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3));
|
||||
parse_events__free_terms($3);
|
||||
}
|
||||
|
||||
event_legacy_symbol:
|
||||
PE_VALUE_SYM '/' event_config '/'
|
||||
{
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3));
|
||||
parse_events__free_terms($3);
|
||||
}
|
||||
|
|
||||
PE_VALUE_SYM sep_slash_dc
|
||||
{
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL));
|
||||
}
|
||||
|
||||
event_legacy_cache:
|
||||
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
|
||||
{
|
||||
ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5));
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
|
||||
{
|
||||
ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL));
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE
|
||||
{
|
||||
ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL));
|
||||
}
|
||||
|
||||
event_legacy_mem:
|
||||
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
|
||||
{
|
||||
ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4));
|
||||
}
|
||||
|
|
||||
PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
{
|
||||
ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL));
|
||||
}
|
||||
|
||||
event_legacy_tracepoint:
|
||||
PE_NAME ':' PE_NAME
|
||||
{
|
||||
ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3));
|
||||
}
|
||||
|
||||
event_legacy_numeric:
|
||||
PE_VALUE ':' PE_VALUE
|
||||
{
|
||||
ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL));
|
||||
}
|
||||
|
||||
event_legacy_raw:
|
||||
PE_RAW
|
||||
{
|
||||
ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
|
||||
}
|
||||
|
||||
event_config:
|
||||
event_config ',' event_term
|
||||
{
|
||||
struct list_head *head = $1;
|
||||
struct parse_events__term *term = $3;
|
||||
|
||||
ABORT_ON(!head);
|
||||
list_add_tail(&term->list, head);
|
||||
$$ = $1;
|
||||
}
|
||||
|
|
||||
event_term
|
||||
{
|
||||
struct list_head *head = malloc(sizeof(*head));
|
||||
struct parse_events__term *term = $1;
|
||||
|
||||
ABORT_ON(!head);
|
||||
INIT_LIST_HEAD(head);
|
||||
list_add_tail(&term->list, head);
|
||||
$$ = head;
|
||||
}
|
||||
|
||||
event_term:
|
||||
PE_NAME '=' PE_NAME
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR,
|
||||
$1, $3, 0));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_NAME '=' PE_VALUE
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
$1, NULL, $3));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_NAME
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
$1, NULL, 1));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_TERM '=' PE_VALUE
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_TERM
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1));
|
||||
$$ = term;
|
||||
}
|
||||
|
||||
sep_dc: ':' |
|
||||
|
||||
sep_slash_dc: '/' | ':' |
|
||||
|
||||
%%
|
||||
|
||||
void parse_events_error(struct list_head *list_all __used,
|
||||
struct list_head *list_event __used,
|
||||
int *idx __used,
|
||||
char const *msg __used)
|
||||
{
|
||||
}
|
1663
tools/perf/util/pmu-bison.c
Normal file
1663
tools/perf/util/pmu-bison.c
Normal file
File diff suppressed because it is too large
Load Diff
73
tools/perf/util/pmu-bison.h
Normal file
73
tools/perf/util/pmu-bison.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* A Bison parser, made by GNU Bison 2.4.3. */
|
||||
|
||||
/* Skeleton interface for Bison's Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
|
||||
2009, 2010 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
|
||||
/* Tokens. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
/* Put the tokens into the symbol table, so that GDB and other debuggers
|
||||
know about them. */
|
||||
enum yytokentype {
|
||||
PP_CONFIG = 258,
|
||||
PP_CONFIG1 = 259,
|
||||
PP_CONFIG2 = 260,
|
||||
PP_VALUE = 261,
|
||||
PP_ERROR = 262
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 31 "util/pmu.y"
|
||||
|
||||
unsigned long num;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
|
||||
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 65 "util/pmu-bison.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE perf_pmu_lval;
|
||||
|
||||
|
1821
tools/perf/util/pmu-flex.c
Normal file
1821
tools/perf/util/pmu-flex.c
Normal file
File diff suppressed because it is too large
Load Diff
316
tools/perf/util/pmu-flex.h
Normal file
316
tools/perf/util/pmu-flex.h
Normal file
@ -0,0 +1,316 @@
|
||||
#ifndef perf_pmu_HEADER_H
|
||||
#define perf_pmu_HEADER_H 1
|
||||
#define perf_pmu_IN_HEADER 1
|
||||
|
||||
#line 6 "util/pmu-flex.h"
|
||||
|
||||
#define YY_INT_ALIGNED short int
|
||||
|
||||
/* A lexical scanner generated by flex */
|
||||
|
||||
#define FLEX_SCANNER
|
||||
#define YY_FLEX_MAJOR_VERSION 2
|
||||
#define YY_FLEX_MINOR_VERSION 5
|
||||
#define YY_FLEX_SUBMINOR_VERSION 35
|
||||
#if YY_FLEX_SUBMINOR_VERSION > 0
|
||||
#define FLEX_BETA
|
||||
#endif
|
||||
|
||||
/* First, we deal with platform-specific or compiler-specific issues. */
|
||||
|
||||
/* begin standard C headers. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* end standard C headers. */
|
||||
|
||||
/* flex integer type definitions */
|
||||
|
||||
#ifndef FLEXINT_H
|
||||
#define FLEXINT_H
|
||||
|
||||
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
|
||||
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
|
||||
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
*/
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
typedef int8_t flex_int8_t;
|
||||
typedef uint8_t flex_uint8_t;
|
||||
typedef int16_t flex_int16_t;
|
||||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
typedef int flex_int32_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned short int flex_uint16_t;
|
||||
typedef unsigned int flex_uint32_t;
|
||||
#endif /* ! C99 */
|
||||
|
||||
/* Limits of integral types. */
|
||||
#ifndef INT8_MIN
|
||||
#define INT8_MIN (-128)
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-32767-1)
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#endif
|
||||
#ifndef INT8_MAX
|
||||
#define INT8_MAX (127)
|
||||
#endif
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX (32767)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX (2147483647)
|
||||
#endif
|
||||
#ifndef UINT8_MAX
|
||||
#define UINT8_MAX (255U)
|
||||
#endif
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (65535U)
|
||||
#endif
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#endif
|
||||
|
||||
#endif /* ! FLEXINT_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/* The "const" storage-class-modifier is valid. */
|
||||
#define YY_USE_CONST
|
||||
|
||||
#else /* ! __cplusplus */
|
||||
|
||||
/* C99 requires __STDC__ to be defined as 1. */
|
||||
#if defined (__STDC__)
|
||||
|
||||
#define YY_USE_CONST
|
||||
|
||||
#endif /* defined (__STDC__) */
|
||||
#endif /* ! __cplusplus */
|
||||
|
||||
#ifdef YY_USE_CONST
|
||||
#define yyconst const
|
||||
#else
|
||||
#define yyconst
|
||||
#endif
|
||||
|
||||
/* Size of default input buffer. */
|
||||
#ifndef YY_BUF_SIZE
|
||||
#define YY_BUF_SIZE 16384
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
|
||||
#define YY_TYPEDEF_YY_BUFFER_STATE
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
extern int perf_pmu_leng;
|
||||
|
||||
extern FILE *perf_pmu_in, *perf_pmu_out;
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state
|
||||
{
|
||||
FILE *yy_input_file;
|
||||
|
||||
char *yy_ch_buf; /* input buffer */
|
||||
char *yy_buf_pos; /* current position in input buffer */
|
||||
|
||||
/* Size of input buffer in bytes, not including room for EOB
|
||||
* characters.
|
||||
*/
|
||||
yy_size_t yy_buf_size;
|
||||
|
||||
/* Number of characters read into yy_ch_buf, not including EOB
|
||||
* characters.
|
||||
*/
|
||||
int yy_n_chars;
|
||||
|
||||
/* Whether we "own" the buffer - i.e., we know we created it,
|
||||
* and can realloc() it to grow it, and should free() it to
|
||||
* delete it.
|
||||
*/
|
||||
int yy_is_our_buffer;
|
||||
|
||||
/* Whether this is an "interactive" input source; if so, and
|
||||
* if we're using stdio for input, then we want to use getc()
|
||||
* instead of fread(), to make sure we stop fetching input after
|
||||
* each newline.
|
||||
*/
|
||||
int yy_is_interactive;
|
||||
|
||||
/* Whether we're considered to be at the beginning of a line.
|
||||
* If so, '^' rules will be active on the next match, otherwise
|
||||
* not.
|
||||
*/
|
||||
int yy_at_bol;
|
||||
|
||||
int yy_bs_lineno; /**< The line count. */
|
||||
int yy_bs_column; /**< The column count. */
|
||||
|
||||
/* Whether to try to fill the input buffer when we reach the
|
||||
* end of it.
|
||||
*/
|
||||
int yy_fill_buffer;
|
||||
|
||||
int yy_buffer_status;
|
||||
|
||||
};
|
||||
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
|
||||
|
||||
void perf_pmu_restart (FILE *input_file );
|
||||
void perf_pmu__switch_to_buffer (YY_BUFFER_STATE new_buffer );
|
||||
YY_BUFFER_STATE perf_pmu__create_buffer (FILE *file,int size );
|
||||
void perf_pmu__delete_buffer (YY_BUFFER_STATE b );
|
||||
void perf_pmu__flush_buffer (YY_BUFFER_STATE b );
|
||||
void perf_pmu_push_buffer_state (YY_BUFFER_STATE new_buffer );
|
||||
void perf_pmu_pop_buffer_state (void );
|
||||
|
||||
YY_BUFFER_STATE perf_pmu__scan_buffer (char *base,yy_size_t size );
|
||||
YY_BUFFER_STATE perf_pmu__scan_string (yyconst char *yy_str );
|
||||
YY_BUFFER_STATE perf_pmu__scan_bytes (yyconst char *bytes,int len );
|
||||
|
||||
void *perf_pmu_alloc (yy_size_t );
|
||||
void *perf_pmu_realloc (void *,yy_size_t );
|
||||
void perf_pmu_free (void * );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
extern int perf_pmu_lineno;
|
||||
|
||||
extern char *perf_pmu_text;
|
||||
#define yytext_ptr perf_pmu_text
|
||||
|
||||
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
|
||||
#define INITIAL 0
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_UNISTD_H
|
||||
/* Special case for "unistd.h", since it is non-ANSI. We include it way
|
||||
* down here because we want the user's section 1 to have been scanned first.
|
||||
* The user has a chance to override it with an option.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef YY_EXTRA_TYPE
|
||||
#define YY_EXTRA_TYPE void *
|
||||
#endif
|
||||
|
||||
/* Accessor methods to globals.
|
||||
These are made visible to non-reentrant scanners for convenience. */
|
||||
|
||||
int perf_pmu_lex_destroy (void );
|
||||
|
||||
int perf_pmu_get_debug (void );
|
||||
|
||||
void perf_pmu_set_debug (int debug_flag );
|
||||
|
||||
YY_EXTRA_TYPE perf_pmu_get_extra (void );
|
||||
|
||||
void perf_pmu_set_extra (YY_EXTRA_TYPE user_defined );
|
||||
|
||||
FILE *perf_pmu_get_in (void );
|
||||
|
||||
void perf_pmu_set_in (FILE * in_str );
|
||||
|
||||
FILE *perf_pmu_get_out (void );
|
||||
|
||||
void perf_pmu_set_out (FILE * out_str );
|
||||
|
||||
int perf_pmu_get_leng (void );
|
||||
|
||||
char *perf_pmu_get_text (void );
|
||||
|
||||
int perf_pmu_get_lineno (void );
|
||||
|
||||
void perf_pmu_set_lineno (int line_number );
|
||||
|
||||
/* Macros after this point can all be overridden by user definitions in
|
||||
* section 1.
|
||||
*/
|
||||
|
||||
#ifndef YY_SKIP_YYWRAP
|
||||
#ifdef __cplusplus
|
||||
extern "C" int perf_pmu_wrap (void );
|
||||
#else
|
||||
extern int perf_pmu_wrap (void );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef yytext_ptr
|
||||
static void yy_flex_strncpy (char *,yyconst char *,int );
|
||||
#endif
|
||||
|
||||
#ifdef YY_NEED_STRLEN
|
||||
static int yy_flex_strlen (yyconst char * );
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_INPUT
|
||||
|
||||
#endif
|
||||
|
||||
/* Amount of stuff to slurp up with each read. */
|
||||
#ifndef YY_READ_BUF_SIZE
|
||||
#define YY_READ_BUF_SIZE 8192
|
||||
#endif
|
||||
|
||||
/* Number of entries by which start-condition stack grows. */
|
||||
#ifndef YY_START_STACK_INCR
|
||||
#define YY_START_STACK_INCR 25
|
||||
#endif
|
||||
|
||||
/* Default declaration of generated scanner - a define so the user can
|
||||
* easily add parameters.
|
||||
*/
|
||||
#ifndef YY_DECL
|
||||
#define YY_DECL_IS_OURS 1
|
||||
|
||||
extern int perf_pmu_lex (void);
|
||||
|
||||
#define YY_DECL int perf_pmu_lex (void)
|
||||
#endif /* !YY_DECL */
|
||||
|
||||
/* yy_get_previous_state - get the state just before the EOB char was reached */
|
||||
|
||||
#undef YY_NEW_FILE
|
||||
#undef YY_FLUSH_BUFFER
|
||||
#undef yy_set_bol
|
||||
#undef yy_new_buffer
|
||||
#undef yy_set_interactive
|
||||
#undef YY_DO_BEFORE_ACTION
|
||||
|
||||
#ifdef YY_DECL_IS_OURS
|
||||
#undef YY_DECL_IS_OURS
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 38 "util/pmu.l"
|
||||
|
||||
|
||||
#line 315 "util/pmu-flex.h"
|
||||
#undef perf_pmu_IN_HEADER
|
||||
#endif /* perf_pmu_HEADER_H */
|
469
tools/perf/util/pmu.c
Normal file
469
tools/perf/util/pmu.c
Normal file
@ -0,0 +1,469 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include "sysfs.h"
|
||||
#include "util.h"
|
||||
#include "pmu.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
int perf_pmu_parse(struct list_head *list, char *name);
|
||||
extern FILE *perf_pmu_in;
|
||||
|
||||
static LIST_HEAD(pmus);
|
||||
|
||||
/*
|
||||
* Parse & process all the sysfs attributes located under
|
||||
* the directory specified in 'dir' parameter.
|
||||
*/
|
||||
static int pmu_format_parse(char *dir, struct list_head *head)
|
||||
{
|
||||
struct dirent *evt_ent;
|
||||
DIR *format_dir;
|
||||
int ret = 0;
|
||||
|
||||
format_dir = opendir(dir);
|
||||
if (!format_dir)
|
||||
return -EINVAL;
|
||||
|
||||
while (!ret && (evt_ent = readdir(format_dir))) {
|
||||
char path[PATH_MAX];
|
||||
char *name = evt_ent->d_name;
|
||||
FILE *file;
|
||||
|
||||
if (!strcmp(name, ".") || !strcmp(name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/%s", dir, name);
|
||||
|
||||
ret = -EINVAL;
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
break;
|
||||
|
||||
perf_pmu_in = file;
|
||||
ret = perf_pmu_parse(head, name);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
closedir(format_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reading/parsing the default pmu format definition, which should be
|
||||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
|
||||
*/
|
||||
static int pmu_format(char *name, struct list_head *format)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs;
|
||||
|
||||
sysfs = sysfs_find_mountpoint();
|
||||
if (!sysfs)
|
||||
return -1;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/bus/event_source/devices/%s/format", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
|
||||
if (pmu_format_parse(path, format))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reading/parsing the default pmu type value, which should be
|
||||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
|
||||
*/
|
||||
static int pmu_type(char *name, __u32 *type)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs;
|
||||
FILE *file;
|
||||
int ret = 0;
|
||||
|
||||
sysfs = sysfs_find_mountpoint();
|
||||
if (!sysfs)
|
||||
return -1;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/bus/event_source/devices/%s/type", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
return -EINVAL;
|
||||
|
||||
if (1 != fscanf(file, "%u", type))
|
||||
ret = -1;
|
||||
|
||||
fclose(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_lookup(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
LIST_HEAD(format);
|
||||
__u32 type;
|
||||
|
||||
/*
|
||||
* The pmu data we store & need consists of the pmu
|
||||
* type value and format definitions. Load both right
|
||||
* now.
|
||||
*/
|
||||
if (pmu_format(name, &format))
|
||||
return NULL;
|
||||
|
||||
if (pmu_type(name, &type))
|
||||
return NULL;
|
||||
|
||||
pmu = zalloc(sizeof(*pmu));
|
||||
if (!pmu)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&pmu->format);
|
||||
list_splice(&format, &pmu->format);
|
||||
pmu->name = strdup(name);
|
||||
pmu->type = type;
|
||||
return pmu;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_find(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
list_for_each_entry(pmu, &pmus, list)
|
||||
if (!strcmp(pmu->name, name))
|
||||
return pmu;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
/*
|
||||
* Once PMU is loaded it stays in the list,
|
||||
* so we keep us from multiple reading/parsing
|
||||
* the pmu format definitions.
|
||||
*/
|
||||
pmu = pmu_find(name);
|
||||
if (pmu)
|
||||
return pmu;
|
||||
|
||||
return pmu_lookup(name);
|
||||
}
|
||||
|
||||
static struct perf_pmu__format*
|
||||
pmu_find_format(struct list_head *formats, char *name)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
|
||||
list_for_each_entry(format, formats, list)
|
||||
if (!strcmp(format->name, name))
|
||||
return format;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns value based on the format definition (format parameter)
|
||||
* and unformated value (value parameter).
|
||||
*
|
||||
* TODO maybe optimize a little ;)
|
||||
*/
|
||||
static __u64 pmu_format_value(unsigned long *format, __u64 value)
|
||||
{
|
||||
unsigned long fbit, vbit;
|
||||
__u64 v = 0;
|
||||
|
||||
for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
|
||||
|
||||
if (!test_bit(fbit, format))
|
||||
continue;
|
||||
|
||||
if (!(value & (1llu << vbit++)))
|
||||
continue;
|
||||
|
||||
v |= (1llu << fbit);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup one of config[12] attr members based on the
|
||||
* user input data - temr parameter.
|
||||
*/
|
||||
static int pmu_config_term(struct list_head *formats,
|
||||
struct perf_event_attr *attr,
|
||||
struct parse_events__term *term)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
__u64 *vp;
|
||||
|
||||
/*
|
||||
* Support only for hardcoded and numnerial terms.
|
||||
* Hardcoded terms should be already in, so nothing
|
||||
* to be done for them.
|
||||
*/
|
||||
if (parse_events__is_hardcoded_term(term))
|
||||
return 0;
|
||||
|
||||
if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
format = pmu_find_format(formats, term->config);
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
switch (format->value) {
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG:
|
||||
vp = &attr->config;
|
||||
break;
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG1:
|
||||
vp = &attr->config1;
|
||||
break;
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG2:
|
||||
vp = &attr->config2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*vp |= pmu_format_value(format->bits, term->val.num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
|
||||
list_for_each_entry_safe(term, h, head_terms, list)
|
||||
if (pmu_config_term(formats, attr, term))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures event's 'attr' parameter based on the:
|
||||
* 1) users input - specified in terms parameter
|
||||
* 2) pmu format definitions - specified by pmu parameter
|
||||
*/
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
attr->type = pmu->type;
|
||||
return pmu_config(&pmu->format, attr, head_terms);
|
||||
}
|
||||
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
|
||||
format = zalloc(sizeof(*format));
|
||||
if (!format)
|
||||
return -ENOMEM;
|
||||
|
||||
format->name = strdup(name);
|
||||
format->value = config;
|
||||
memcpy(format->bits, bits, sizeof(format->bits));
|
||||
|
||||
list_add_tail(&format->list, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_pmu__set_format(unsigned long *bits, long from, long to)
|
||||
{
|
||||
long b;
|
||||
|
||||
if (!to)
|
||||
to = from;
|
||||
|
||||
memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
|
||||
for (b = from; b <= to; b++)
|
||||
set_bit(b, bits);
|
||||
}
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} test_formats[] = {
|
||||
{ "krava01", "config:0-1,62-63\n", },
|
||||
{ "krava02", "config:10-17\n", },
|
||||
{ "krava03", "config:5\n", },
|
||||
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
|
||||
{ "krava12", "config1:63\n", },
|
||||
{ "krava13", "config1:45-47\n", },
|
||||
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
|
||||
{ "krava22", "config2:8,18,48,58\n", },
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava02",
|
||||
.val.num = 170,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava03",
|
||||
.val.num = 1,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava11",
|
||||
.val.num = 27,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava12",
|
||||
.val.num = 1,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava13",
|
||||
.val.num = 2,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava21",
|
||||
.val.num = 119,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava22",
|
||||
.val.num = 11,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava23",
|
||||
.val.num = 2,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
};
|
||||
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
|
||||
|
||||
/*
|
||||
* Prepare format directory data, exported by kernel
|
||||
* at /sys/bus/event_source/devices/<dev>/format.
|
||||
*/
|
||||
static char *test_format_dir_get(void)
|
||||
{
|
||||
static char dir[PATH_MAX];
|
||||
unsigned int i;
|
||||
|
||||
snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
|
||||
snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
|
||||
|
||||
file = fopen(name, "w");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (1 != fwrite(format->value, strlen(format->value), 1, file))
|
||||
break;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Cleanup format directory. */
|
||||
static int test_format_dir_put(char *dir)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
|
||||
if (system(buf))
|
||||
return -1;
|
||||
|
||||
snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
|
||||
return system(buf);
|
||||
}
|
||||
|
||||
static struct list_head *test_terms_list(void)
|
||||
{
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int perf_pmu__test(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
LIST_HEAD(formats);
|
||||
struct list_head *terms = test_terms_list();
|
||||
int ret;
|
||||
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
ret = pmu_format_parse(format, &formats);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = pmu_config(&formats, &attr, terms);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (attr.config != 0xc00000000002a823)
|
||||
break;
|
||||
if (attr.config1 != 0x8000400000000145)
|
||||
break;
|
||||
if (attr.config2 != 0x0400000020041d07)
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
test_format_dir_put(format);
|
||||
return ret;
|
||||
}
|
41
tools/perf/util/pmu.h
Normal file
41
tools/perf/util/pmu.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef __PMU_H
|
||||
#define __PMU_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include "../../../include/linux/perf_event.h"
|
||||
|
||||
enum {
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG1,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG2,
|
||||
};
|
||||
|
||||
#define PERF_PMU_FORMAT_BITS 64
|
||||
|
||||
struct perf_pmu__format {
|
||||
char *name;
|
||||
int value;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu {
|
||||
char *name;
|
||||
__u32 type;
|
||||
struct list_head format;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
|
||||
int perf_pmu_wrap(void);
|
||||
void perf_pmu_error(struct list_head *list, char *name, char const *msg);
|
||||
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits);
|
||||
void perf_pmu__set_format(unsigned long *bits, long from, long to);
|
||||
|
||||
int perf_pmu__test(void);
|
||||
#endif /* __PMU_H */
|
43
tools/perf/util/pmu.l
Normal file
43
tools/perf/util/pmu.l
Normal file
@ -0,0 +1,43 @@
|
||||
%option prefix="perf_pmu_"
|
||||
|
||||
%{
|
||||
#include <stdlib.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "pmu.h"
|
||||
#include "pmu-bison.h"
|
||||
|
||||
static int value(int base)
|
||||
{
|
||||
long num;
|
||||
|
||||
errno = 0;
|
||||
num = strtoul(perf_pmu_text, NULL, base);
|
||||
if (errno)
|
||||
return PP_ERROR;
|
||||
|
||||
perf_pmu_lval.num = num;
|
||||
return PP_VALUE;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
num_dec [0-9]+
|
||||
|
||||
%%
|
||||
|
||||
{num_dec} { return value(10); }
|
||||
config { return PP_CONFIG; }
|
||||
config1 { return PP_CONFIG1; }
|
||||
config2 { return PP_CONFIG2; }
|
||||
- { return '-'; }
|
||||
: { return ':'; }
|
||||
, { return ','; }
|
||||
. { ; }
|
||||
\n { ; }
|
||||
|
||||
%%
|
||||
|
||||
int perf_pmu_wrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
93
tools/perf/util/pmu.y
Normal file
93
tools/perf/util/pmu.y
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
%name-prefix "perf_pmu_"
|
||||
%parse-param {struct list_head *format}
|
||||
%parse-param {char *name}
|
||||
|
||||
%{
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <string.h>
|
||||
#include "pmu.h"
|
||||
|
||||
extern int perf_pmu_lex (void);
|
||||
|
||||
#define ABORT_ON(val) \
|
||||
do { \
|
||||
if (val) \
|
||||
YYABORT; \
|
||||
} while (0)
|
||||
|
||||
%}
|
||||
|
||||
%token PP_CONFIG PP_CONFIG1 PP_CONFIG2
|
||||
%token PP_VALUE PP_ERROR
|
||||
%type <num> PP_VALUE
|
||||
%type <bits> bit_term
|
||||
%type <bits> bits
|
||||
|
||||
%union
|
||||
{
|
||||
unsigned long num;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
format:
|
||||
format format_term
|
||||
|
|
||||
format_term
|
||||
|
||||
format_term:
|
||||
PP_CONFIG ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
$3));
|
||||
}
|
||||
|
|
||||
PP_CONFIG1 ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG1,
|
||||
$3));
|
||||
}
|
||||
|
|
||||
PP_CONFIG2 ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG2,
|
||||
$3));
|
||||
}
|
||||
|
||||
bits:
|
||||
bits ',' bit_term
|
||||
{
|
||||
bitmap_or($$, $1, $3, 64);
|
||||
}
|
||||
|
|
||||
bit_term
|
||||
{
|
||||
memcpy($$, $1, sizeof($1));
|
||||
}
|
||||
|
||||
bit_term:
|
||||
PP_VALUE '-' PP_VALUE
|
||||
{
|
||||
perf_pmu__set_format($$, $1, $3);
|
||||
}
|
||||
|
|
||||
PP_VALUE
|
||||
{
|
||||
perf_pmu__set_format($$, $1, 0);
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
void perf_pmu_error(struct list_head *list __used,
|
||||
char *name __used,
|
||||
char const *msg __used)
|
||||
{
|
||||
}
|
@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
|
||||
INIT_LIST_HEAD(&self->ordered_samples.to_free);
|
||||
machine__init(&self->host_machine, "", HOST_KERNEL_ID);
|
||||
hists__init(&self->hists);
|
||||
|
||||
if (mode == O_RDONLY) {
|
||||
if (perf_session__open(self, force) < 0)
|
||||
|
@ -722,7 +722,7 @@ static char *event_read_name(void)
|
||||
static int event_read_id(void)
|
||||
{
|
||||
char *token;
|
||||
int id;
|
||||
int id = -1;
|
||||
|
||||
if (read_expected_item(EVENT_ITEM, "ID") < 0)
|
||||
return -1;
|
||||
@ -731,15 +731,13 @@ static int event_read_id(void)
|
||||
return -1;
|
||||
|
||||
if (read_expect_type(EVENT_ITEM, &token) < 0)
|
||||
goto fail;
|
||||
goto free;
|
||||
|
||||
id = strtoul(token, NULL, 0);
|
||||
|
||||
free:
|
||||
free_token(token);
|
||||
return id;
|
||||
|
||||
fail:
|
||||
free_token(token);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int field_is_string(struct format_field *field)
|
||||
|
@ -49,6 +49,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
|
||||
const char *format, ...);
|
||||
int ui_browser__help_window(struct ui_browser *browser, const char *text);
|
||||
bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
|
||||
int ui_browser__input_window(const char *title, const char *text, char *input,
|
||||
const char *exit_msg, int delay_sec);
|
||||
|
||||
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
|
||||
unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
|
||||
|
@ -879,6 +879,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
char *options[16];
|
||||
int nr_options = 0;
|
||||
int key = -1;
|
||||
char buf[64];
|
||||
|
||||
if (browser == NULL)
|
||||
return -1;
|
||||
@ -933,6 +934,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
goto zoom_dso;
|
||||
case 't':
|
||||
goto zoom_thread;
|
||||
case 's':
|
||||
if (ui_browser__input_window("Symbol to show",
|
||||
"Please enter the name of symbol you want to see",
|
||||
buf, "ENTER: OK, ESC: Cancel",
|
||||
delay_secs * 2) == K_ENTER) {
|
||||
self->symbol_filter_str = *buf ? buf : NULL;
|
||||
hists__filter_by_symbol(self);
|
||||
hist_browser__reset(browser);
|
||||
}
|
||||
continue;
|
||||
case K_F1:
|
||||
case 'h':
|
||||
case '?':
|
||||
@ -950,7 +961,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
"C Collapse all callchains\n"
|
||||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread");
|
||||
"t Zoom into current Thread\n"
|
||||
"s Filter symbol by name");
|
||||
continue;
|
||||
case K_ENTER:
|
||||
case K_RIGHT:
|
||||
|
@ -16,6 +16,8 @@
|
||||
#define K_TAB '\t'
|
||||
#define K_UNTAB SL_KEY_UNTAB
|
||||
#define K_UP SL_KEY_UP
|
||||
#define K_BKSPC 0x7f
|
||||
#define K_DEL SL_KEY_DELETE
|
||||
|
||||
/* Not really keys */
|
||||
#define K_TIMER -1
|
||||
|
@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[])
|
||||
return popup_menu__run(&menu);
|
||||
}
|
||||
|
||||
int ui_browser__input_window(const char *title, const char *text, char *input,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
int x, y, len, key;
|
||||
int max_len = 60, nr_lines = 0;
|
||||
static char buf[50];
|
||||
const char *t;
|
||||
|
||||
t = text;
|
||||
while (1) {
|
||||
const char *sep = strchr(t, '\n');
|
||||
|
||||
if (sep == NULL)
|
||||
sep = strchr(t, '\0');
|
||||
len = sep - t;
|
||||
if (max_len < len)
|
||||
max_len = len;
|
||||
++nr_lines;
|
||||
if (*sep == '\0')
|
||||
break;
|
||||
t = sep + 1;
|
||||
}
|
||||
|
||||
max_len += 2;
|
||||
nr_lines += 8;
|
||||
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
|
||||
x = SLtt_Screen_Cols / 2 - max_len / 2;
|
||||
|
||||
SLsmg_set_color(0);
|
||||
SLsmg_draw_box(y, x++, nr_lines, max_len);
|
||||
if (title) {
|
||||
SLsmg_gotorc(y, x + 1);
|
||||
SLsmg_write_string((char *)title);
|
||||
}
|
||||
SLsmg_gotorc(++y, x);
|
||||
nr_lines -= 7;
|
||||
max_len -= 2;
|
||||
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
|
||||
nr_lines, max_len, 1);
|
||||
y += nr_lines;
|
||||
len = 5;
|
||||
while (len--) {
|
||||
SLsmg_gotorc(y + len - 1, x);
|
||||
SLsmg_write_nstring((char *)" ", max_len);
|
||||
}
|
||||
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
|
||||
|
||||
SLsmg_gotorc(y + 3, x);
|
||||
SLsmg_write_nstring((char *)exit_msg, max_len);
|
||||
SLsmg_refresh();
|
||||
|
||||
x += 2;
|
||||
len = 0;
|
||||
key = ui__getch(delay_secs);
|
||||
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
|
||||
if (key == K_BKSPC) {
|
||||
if (len == 0)
|
||||
goto next_key;
|
||||
SLsmg_gotorc(y, x + --len);
|
||||
SLsmg_write_char(' ');
|
||||
} else {
|
||||
buf[len] = key;
|
||||
SLsmg_gotorc(y, x + len++);
|
||||
SLsmg_write_char(key);
|
||||
}
|
||||
SLsmg_refresh();
|
||||
|
||||
/* XXX more graceful overflow handling needed */
|
||||
if (len == sizeof(buf) - 1) {
|
||||
ui_helpline__push("maximum size of symbol name reached!");
|
||||
key = K_ENTER;
|
||||
break;
|
||||
}
|
||||
next_key:
|
||||
key = ui__getch(delay_secs);
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
strncpy(input, buf, len+1);
|
||||
return key;
|
||||
}
|
||||
|
||||
int ui__question_window(const char *title, const char *text,
|
||||
const char *exit_msg, int delay_secs)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user