提交 00e4db51 编写于 作者: L Linus Torvalds

Merge tag 'perf-tools-2020-08-10' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

Pull perf tools updates from Arnaldo Carvalho de Melo:
 "New features:

   - Introduce controlling how 'perf stat' and 'perf record' works via a
     control file descriptor, allowing starting with events configured
     but disabled until commands are received via the control file
     descriptor. This allows, for instance for tools such as Intel VTune
     to make further use of perf as its Linux platform driver.

   - Improve 'perf record' to to register in a perf.data file header the
     clockid used to help later correlate things like syslog files and
     perf events recorded.

   - Add basic syscall and find_next_bit benchmarks to 'perf bench'.

   - Allow using computed metrics in calculating other metrics. For
     instance:

	  {
	    .metric_expr    = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
	    .metric_name    = "DCache_L2_All_Hits",
	  },
	  {
	    .metric_expr    = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
	    .metric_name    = "DCache_L2_All_Miss",
	  },
	  {
	     .metric_expr    = "dcache_l2_all_hits + dcache_l2_all_miss",
	     .metric_name    = "DCache_L2_All",
	  }

   - Add suport for 'd_ratio', '>' and '<' operators to the expression
     resolver used in calculating metrics in 'perf stat'.

  Support for new kernel features:

   - Support TEXT_POKE and KSYMBOL_TYPE_OOL perf metadata events to cope
     with things like ftrace, trampolines, i.e. changes in the kernel
     text that gets in the way of properly decoding Intel PT hardware
     traces, for instance.

  Intel PT:

   - Add various knobs to reduce the volume of Intel PT traces by
     reducing the level of details such as decoding just some types of
     packets (e.g., FUP/TIP, PSB+), also filtering by time range.

   - Add new itrace options (log flags to the 'd' option, error flags to
     the 'e' one, etc), controlling how Intel PT is transformed into
     perf events, document some missing options (e.g., how to synthesize
     callchains).

  BPF:

   - Properly report BPF errors when parsing events.

   - Do not setup side-band events if LIBBPF is not linked, fixing a
     segfault.

  Libraries:

   - Improvements to the libtraceevent plugin mechanism.

   - Improve libtracevent support for KVM trace events SVM exit reasons.

   - Add a libtracevent plugins for decoding syscalls/sys_enter_futex
     and for tlb_flush.

   - Ensure sample_period is set libpfm4 events in 'perf test'.

   - Fixup libperf namespacing, to make sure what is in libperf has the
     perf_ namespace while what is now only in tools/perf/ doesn't use
     that prefix.

  Arch specific:

   - Improve the testing of vendor events and metrics in 'perf test'.

   - Allow no ARM CoreSight hardware tracer sink to be specified on
     command line.

   - Fix arm_spe_x recording when mixed with other perf events.

   - Add s390 idle functions 'psw_idle' and 'psw_idle_exit' to list of
     idle symbols.

   - List kernel supplied event aliases for arm64 in 'perf list'.

   - Add support for extended register capability in PowerPC 9 and 10.

   - Added nest IMC power9 metric events.

  Miscellaneous:

   - No need to setup sample_regs_intr/sample_regs_user for dummy
     events.

   - Update various copies of kernel headers, some causing perf to
     handle new syscalls, MSRs, etc.

   - Improve usage of flex and yacc, enabling warnings and addressing
     the fallout.

   - Add missing '--output' option to 'perf kmem' so that it can pass it
     along to 'perf record'.

   - 'perf probe' fixes related to adding multiple probes on the same
     address for the same event.

   - Make 'perf probe' warn if the target function is a GNU indirect
     function.

   - Remove //anon mmap events from 'perf inject jit' to fix supporting
     both using ELF files for generated functions and the perf-PID.map
     approaches"

* tag 'perf-tools-2020-08-10' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (144 commits)
  perf record: Skip side-band event setup if HAVE_LIBBPF_SUPPORT is not set
  perf tools powerpc: Add support for extended regs in power10
  perf tools powerpc: Add support for extended register capability
  tools headers UAPI: Sync drm/i915_drm.h with the kernel sources
  tools arch x86: Sync asm/cpufeatures.h with the kernel sources
  tools arch x86: Sync the msr-index.h copy with the kernel sources
  tools headers UAPI: update linux/in.h copy
  tools headers API: Update close_range affected files
  perf script: Add 'tod' field to display time of day
  perf script: Change the 'enum perf_output_field' enumerators to be 64 bits
  perf data: Add support to store time of day in CTF data conversion
  perf tools: Move clockid_res_ns under clock struct
  perf header: Store clock references for -k/--clockid option
  perf tools: Add clockid_name function
  perf clockid: Move parse_clockid() to new clockid object
  tools lib traceevent: Handle possible strdup() error in tep_add_plugin_path() API
  libtraceevent: Fixed description of tep_add_plugin_path() API
  libtraceevent: Fixed type in PRINT_FMT_STING
  libtraceevent: Fixed broken indentation in parse_ip4_print_args()
  libtraceevent: Improve error handling of tep_plugin_add_option() API
  ...
......@@ -48,6 +48,24 @@ enum perf_event_powerpc_regs {
PERF_REG_POWERPC_DSISR,
PERF_REG_POWERPC_SIER,
PERF_REG_POWERPC_MMCRA,
PERF_REG_POWERPC_MAX,
/* Extended registers */
PERF_REG_POWERPC_MMCR0,
PERF_REG_POWERPC_MMCR1,
PERF_REG_POWERPC_MMCR2,
PERF_REG_POWERPC_MMCR3,
PERF_REG_POWERPC_SIER2,
PERF_REG_POWERPC_SIER3,
/* Max regs without the extended regs */
PERF_REG_POWERPC_MAX = PERF_REG_POWERPC_MMCRA + 1,
};
#define PERF_REG_PMU_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1)
/* PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_300 */
#define PERF_REG_PMU_MASK_300 (((1ULL << (PERF_REG_POWERPC_MMCR2 + 1)) - 1) - PERF_REG_PMU_MASK)
/* PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_31 */
#define PERF_REG_PMU_MASK_31 (((1ULL << (PERF_REG_POWERPC_SIER3 + 1)) - 1) - PERF_REG_PMU_MASK)
#define PERF_REG_MAX_ISA_300 (PERF_REG_POWERPC_MMCR2 + 1)
#define PERF_REG_MAX_ISA_31 (PERF_REG_POWERPC_SIER3 + 1)
#endif /* _UAPI_ASM_POWERPC_PERF_REGS_H */
......@@ -96,6 +96,7 @@
#define X86_FEATURE_SYSCALL32 ( 3*32+14) /* "" syscall in IA32 userspace */
#define X86_FEATURE_SYSENTER32 ( 3*32+15) /* "" sysenter in IA32 userspace */
#define X86_FEATURE_REP_GOOD ( 3*32+16) /* REP microcode works well */
/* free ( 3*32+17) */
#define X86_FEATURE_LFENCE_RDTSC ( 3*32+18) /* "" LFENCE synchronizes RDTSC */
#define X86_FEATURE_ACC_POWER ( 3*32+19) /* AMD Accumulated Power Mechanism */
#define X86_FEATURE_NOPL ( 3*32+20) /* The NOPL (0F 1F) instructions */
......@@ -107,6 +108,7 @@
#define X86_FEATURE_EXTD_APICID ( 3*32+26) /* Extended APICID (8 bits) */
#define X86_FEATURE_AMD_DCM ( 3*32+27) /* AMD multi-node processor */
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* P-State hardware coordination feedback capability (APERF/MPERF MSRs) */
/* free ( 3*32+29) */
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */
......@@ -365,7 +367,9 @@
#define X86_FEATURE_SRBDS_CTRL (18*32+ 9) /* "" SRBDS mitigation MSR available */
#define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
#define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
#define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE instruction */
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
#define X86_FEATURE_ARCH_LBR (18*32+19) /* Intel ARCH LBR */
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
#define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */
......
......@@ -149,6 +149,10 @@
#define MSR_LBR_SELECT 0x000001c8
#define MSR_LBR_TOS 0x000001c9
#define MSR_IA32_POWER_CTL 0x000001fc
#define MSR_IA32_POWER_CTL_BIT_EE 19
#define MSR_LBR_NHM_FROM 0x00000680
#define MSR_LBR_NHM_TO 0x000006c0
#define MSR_LBR_CORE_FROM 0x00000040
......@@ -158,7 +162,23 @@
#define LBR_INFO_MISPRED BIT_ULL(63)
#define LBR_INFO_IN_TX BIT_ULL(62)
#define LBR_INFO_ABORT BIT_ULL(61)
#define LBR_INFO_CYC_CNT_VALID BIT_ULL(60)
#define LBR_INFO_CYCLES 0xffff
#define LBR_INFO_BR_TYPE_OFFSET 56
#define LBR_INFO_BR_TYPE (0xfull << LBR_INFO_BR_TYPE_OFFSET)
#define MSR_ARCH_LBR_CTL 0x000014ce
#define ARCH_LBR_CTL_LBREN BIT(0)
#define ARCH_LBR_CTL_CPL_OFFSET 1
#define ARCH_LBR_CTL_CPL (0x3ull << ARCH_LBR_CTL_CPL_OFFSET)
#define ARCH_LBR_CTL_STACK_OFFSET 3
#define ARCH_LBR_CTL_STACK (0x1ull << ARCH_LBR_CTL_STACK_OFFSET)
#define ARCH_LBR_CTL_FILTER_OFFSET 16
#define ARCH_LBR_CTL_FILTER (0x7full << ARCH_LBR_CTL_FILTER_OFFSET)
#define MSR_ARCH_LBR_DEPTH 0x000014cf
#define MSR_ARCH_LBR_FROM_0 0x00001500
#define MSR_ARCH_LBR_TO_0 0x00001600
#define MSR_ARCH_LBR_INFO_0 0x00001200
#define MSR_IA32_PEBS_ENABLE 0x000003f1
#define MSR_PEBS_DATA_CFG 0x000003f2
......@@ -253,8 +273,6 @@
#define MSR_PEBS_FRONTEND 0x000003f7
#define MSR_IA32_POWER_CTL 0x000001fc
#define MSR_IA32_MC0_CTL 0x00000400
#define MSR_IA32_MC0_STATUS 0x00000401
#define MSR_IA32_MC0_ADDR 0x00000402
......@@ -418,7 +436,6 @@
#define MSR_AMD64_PATCH_LEVEL 0x0000008b
#define MSR_AMD64_TSC_RATIO 0xc0000104
#define MSR_AMD64_NB_CFG 0xc001001f
#define MSR_AMD64_CPUID_FN_1 0xc0011004
#define MSR_AMD64_PATCH_LOADER 0xc0010020
#define MSR_AMD_PERF_CTL 0xc0010062
#define MSR_AMD_PERF_STATUS 0xc0010063
......@@ -427,6 +444,7 @@
#define MSR_AMD64_OSVW_STATUS 0xc0010141
#define MSR_AMD_PPIN_CTL 0xc00102f0
#define MSR_AMD_PPIN 0xc00102f1
#define MSR_AMD64_CPUID_FN_1 0xc0011004
#define MSR_AMD64_LS_CFG 0xc0011020
#define MSR_AMD64_DC_CFG 0xc0011022
#define MSR_AMD64_BU_CFG2 0xc001102a
......@@ -466,6 +484,8 @@
#define MSR_F16H_DR0_ADDR_MASK 0xc0011027
/* Fam 15h MSRs */
#define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
#define MSR_F15H_PERF_CTL 0xc0010200
#define MSR_F15H_PERF_CTL0 MSR_F15H_PERF_CTL
#define MSR_F15H_PERF_CTL1 (MSR_F15H_PERF_CTL + 2)
......
......@@ -8,7 +8,7 @@ endif
feature_check = $(eval $(feature_check_code))
define feature_check_code
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CC=$(CC) CXX=$(CXX) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
endef
feature_set = $(eval $(feature_set_code))
......
......@@ -74,8 +74,6 @@ FILES= \
FILES := $(addprefix $(OUTPUT),$(FILES))
CC ?= $(CROSS_COMPILE)gcc
CXX ?= $(CROSS_COMPILE)g++
PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
LLVM_CONFIG ?= llvm-config
CLANG ?= clang
......
......@@ -850,6 +850,8 @@ __SYSCALL(__NR_pidfd_open, sys_pidfd_open)
#define __NR_clone3 435
__SYSCALL(__NR_clone3, sys_clone3)
#endif
#define __NR_close_range 436
__SYSCALL(__NR_close_range, sys_close_range)
#define __NR_openat2 437
__SYSCALL(__NR_openat2, sys_openat2)
......
......@@ -55,7 +55,7 @@ extern "C" {
* cause the related events to not be seen.
*
* I915_RESET_UEVENT - Event is generated just before an attempt to reset the
* the GPU. The value supplied with the event is always 1. NOTE: Disable
* GPU. The value supplied with the event is always 1. NOTE: Disable
* reset via module parameter will cause this event to not be seen.
*/
#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR"
......@@ -1934,7 +1934,7 @@ enum drm_i915_perf_property_id {
/**
* The value specifies which set of OA unit metrics should be
* be configured, defining the contents of any OA unit reports.
* configured, defining the contents of any OA unit reports.
*
* This property is available in perf revision 1.
*/
......
......@@ -123,6 +123,7 @@ struct in_addr {
#define IP_CHECKSUM 23
#define IP_BIND_ADDRESS_NO_PORT 24
#define IP_RECVFRAGSIZE 25
#define IP_RECVERR_RFC4884 26
/* IP_MTU_DISCOVER values */
#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
......
......@@ -383,7 +383,8 @@ struct perf_event_attr {
bpf_event : 1, /* include bpf events */
aux_output : 1, /* generate AUX records instead of events */
cgroup : 1, /* include cgroup events */
__reserved_1 : 31;
text_poke : 1, /* include text poke events */
__reserved_1 : 30;
union {
__u32 wakeup_events; /* wakeup every n events */
......@@ -1041,12 +1042,35 @@ enum perf_event_type {
*/
PERF_RECORD_CGROUP = 19,
/*
* Records changes to kernel text i.e. self-modified code. 'old_len' is
* the number of old bytes, 'new_len' is the number of new bytes. Either
* 'old_len' or 'new_len' may be zero to indicate, for example, the
* addition or removal of a trampoline. 'bytes' contains the old bytes
* followed immediately by the new bytes.
*
* struct {
* struct perf_event_header header;
* u64 addr;
* u16 old_len;
* u16 new_len;
* u8 bytes[];
* struct sample_id sample_id;
* };
*/
PERF_RECORD_TEXT_POKE = 20,
PERF_RECORD_MAX, /* non-ABI */
};
enum perf_record_ksymbol_type {
PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0,
PERF_RECORD_KSYMBOL_TYPE_BPF = 1,
/*
* Out of line code such as kprobe-replaced instructions or optimized
* kprobes or ftrace trampolines.
*/
PERF_RECORD_KSYMBOL_TYPE_OOL = 2,
PERF_RECORD_KSYMBOL_TYPE_MAX /* non-ABI */
};
......
......@@ -8,6 +8,7 @@
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void fdarray__init(struct fdarray *fda, int nr_autogrow)
{
......@@ -19,7 +20,7 @@ void fdarray__init(struct fdarray *fda, int nr_autogrow)
int fdarray__grow(struct fdarray *fda, int nr)
{
void *priv;
struct priv *priv;
int nr_alloc = fda->nr_alloc + nr;
size_t psize = sizeof(fda->priv[0]) * nr_alloc;
size_t size = sizeof(struct pollfd) * nr_alloc;
......@@ -34,6 +35,9 @@ int fdarray__grow(struct fdarray *fda, int nr)
return -ENOMEM;
}
memset(&entries[fda->nr_alloc], 0, sizeof(struct pollfd) * nr);
memset(&priv[fda->nr_alloc], 0, sizeof(fda->priv[0]) * nr);
fda->nr_alloc = nr_alloc;
fda->entries = entries;
fda->priv = priv;
......@@ -69,7 +73,7 @@ void fdarray__delete(struct fdarray *fda)
free(fda);
}
int fdarray__add(struct fdarray *fda, int fd, short revents)
int fdarray__add(struct fdarray *fda, int fd, short revents, enum fdarray_flags flags)
{
int pos = fda->nr;
......@@ -79,6 +83,7 @@ int fdarray__add(struct fdarray *fda, int fd, short revents)
fda->entries[fda->nr].fd = fd;
fda->entries[fda->nr].events = revents;
fda->priv[fda->nr].flags = flags;
fda->nr++;
return pos;
}
......@@ -93,22 +98,22 @@ int fdarray__filter(struct fdarray *fda, short revents,
return 0;
for (fd = 0; fd < fda->nr; ++fd) {
if (!fda->entries[fd].events)
continue;
if (fda->entries[fd].revents & revents) {
if (entry_destructor)
entry_destructor(fda, fd, arg);
fda->entries[fd].revents = fda->entries[fd].events = 0;
continue;
}
if (fd != nr) {
fda->entries[nr] = fda->entries[fd];
fda->priv[nr] = fda->priv[fd];
}
++nr;
if (!(fda->priv[fd].flags & fdarray_flag__nonfilterable))
++nr;
}
return fda->nr = nr;
return nr;
}
int fdarray__poll(struct fdarray *fda, int timeout)
......
......@@ -21,19 +21,27 @@ struct fdarray {
int nr_alloc;
int nr_autogrow;
struct pollfd *entries;
union {
int idx;
void *ptr;
struct priv {
union {
int idx;
void *ptr;
};
unsigned int flags;
} *priv;
};
enum fdarray_flags {
fdarray_flag__default = 0x00000000,
fdarray_flag__nonfilterable = 0x00000001
};
void fdarray__init(struct fdarray *fda, int nr_autogrow);
void fdarray__exit(struct fdarray *fda);
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
void fdarray__delete(struct fdarray *fda);
int fdarray__add(struct fdarray *fda, int fd, short revents);
int fdarray__add(struct fdarray *fda, int fd, short revents, enum fdarray_flags flags);
int fdarray__poll(struct fdarray *fda, int timeout);
int fdarray__filter(struct fdarray *fda, short revents,
void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
......
......@@ -305,9 +305,9 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
}
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
void *ptr, short revent)
void *ptr, short revent, enum fdarray_flags flags)
{
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP, flags);
if (pos >= 0) {
evlist->pollfd.priv[pos].ptr = ptr;
......@@ -488,7 +488,7 @@ mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
revent = !overwrite ? POLLIN : 0;
if (!evsel->system_wide &&
perf_evlist__add_pollfd(evlist, fd, map, revent) < 0) {
perf_evlist__add_pollfd(evlist, fd, map, revent, fdarray_flag__default) < 0) {
perf_mmap__put(map);
return -1;
}
......
......@@ -45,7 +45,7 @@ struct perf_evlist_mmap_ops {
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
void *ptr, short revent);
void *ptr, short revent, enum fdarray_flags flags);
int perf_evlist__mmap_ops(struct perf_evlist *evlist,
struct perf_evlist_mmap_ops *ops,
......
......@@ -111,6 +111,14 @@ struct perf_record_cgroup {
char path[PATH_MAX];
};
struct perf_record_text_poke_event {
struct perf_event_header header;
__u64 addr;
__u16 old_len;
__u16 new_len;
__u8 bytes[];
};
struct perf_record_sample {
struct perf_event_header header;
__u64 array[];
......@@ -367,6 +375,7 @@ union perf_event {
struct perf_record_sample sample;
struct perf_record_bpf_event bpf;
struct perf_record_ksymbol ksymbol;
struct perf_record_text_poke_event text_poke;
struct perf_record_header_attr attr;
struct perf_record_event_update event_update;
struct perf_record_header_event_type event_type;
......
......@@ -3,7 +3,7 @@ libtraceevent(3)
NAME
----
tep_load_plugins, tep_unload_plugins - Load / unload traceevent plugins.
tep_load_plugins, tep_unload_plugins, tep_load_plugins_hook - Load / unload traceevent plugins.
SYNOPSIS
--------
......@@ -13,6 +13,12 @@ SYNOPSIS
struct tep_plugin_list pass:[*]*tep_load_plugins*(struct tep_handle pass:[*]_tep_);
void *tep_unload_plugins*(struct tep_plugin_list pass:[*]_plugin_list_, struct tep_handle pass:[*]_tep_);
void *tep_load_plugins_hook*(struct tep_handle pass:[*]_tep_, const char pass:[*]_suffix_,
void (pass:[*]_load_plugin_)(struct tep_handle pass:[*]tep,
const char pass:[*]path,
const char pass:[*]name,
void pass:[*]data),
void pass:[*]_data_);
--
DESCRIPTION
......@@ -22,11 +28,13 @@ directories. The _tep_ argument is trace event parser context.
The plugin directories are :
[verse]
--
- Directories, specified in _tep_->plugins_dir with priority TEP_PLUGIN_FIRST
- System's plugin directory, defined at the library compile time. It
depends on the library installation prefix and usually is
_(install_preffix)/lib/traceevent/plugins_
- Directory, defined by the environment variable _TRACEEVENT_PLUGIN_DIR_
- User's plugin directory, located at _~/.local/lib/traceevent/plugins_
- Directories, specified in _tep_->plugins_dir with priority TEP_PLUGIN_LAST
--
Loading of plugins can be controlled by the _tep_flags_, using the
_tep_set_flag()_ API:
......@@ -44,6 +52,12 @@ _tep_load_plugins()_. The _tep_ argument is trace event parser context. The
_plugin_list_ is the list of loaded plugins, returned by
the _tep_load_plugins()_ function.
The _tep_load_plugins_hook_ function walks through all directories with plugins
and calls user specified _load_plugin()_ hook for each plugin file. Only files
with given _suffix_ are considered to be plugins. The _data_ is a user specified
context, passed to _load_plugin()_. Directories and the walk order are the same
as in _tep_load_plugins()_ API.
RETURN VALUE
------------
The _tep_load_plugins()_ function returns a list of successfully loaded plugins,
......@@ -63,6 +77,15 @@ if (plugins == NULL) {
}
...
tep_unload_plugins(plugins, tep);
...
void print_plugin(struct tep_handle *tep, const char *path,
const char *name, void *data)
{
pritnf("Found libtraceevent plugin %s/%s\n", path, name);
}
...
tep_load_plugins_hook(tep, ".so", print_plugin, NULL);
...
--
FILES
......
......@@ -13,6 +13,7 @@ struct func_map;
struct func_list;
struct event_handler;
struct func_resolver;
struct tep_plugins_dir;
struct tep_handle {
int ref_count;
......@@ -47,7 +48,6 @@ struct tep_handle {
struct printk_list *printklist;
unsigned int printk_count;
struct tep_event **events;
int nr_events;
struct tep_event **sort_events;
......@@ -81,10 +81,30 @@ struct tep_handle {
/* cache */
struct tep_event *last_event;
struct tep_plugins_dir *plugins_dir;
};
enum tep_print_parse_type {
PRINT_FMT_STRING,
PRINT_FMT_ARG_DIGIT,
PRINT_FMT_ARG_POINTER,
PRINT_FMT_ARG_STRING,
};
struct tep_print_parse {
struct tep_print_parse *next;
char *format;
int ls;
enum tep_print_parse_type type;
struct tep_print_arg *arg;
struct tep_print_arg *len_as_arg;
};
void tep_free_event(struct tep_event *event);
void tep_free_format_field(struct tep_format_field *field);
void tep_free_plugin_paths(struct tep_handle *tep);
unsigned short tep_data2host2(struct tep_handle *tep, unsigned short data);
unsigned int tep_data2host4(struct tep_handle *tep, unsigned int data);
......
此差异已折叠。
/* SPDX-License-Identifier: LGPL-2.1 */
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef _PARSE_EVENTS_H
#define _PARSE_EVENTS_H
......@@ -272,9 +258,12 @@ struct tep_print_arg {
};
};
struct tep_print_parse;
struct tep_print_fmt {
char *format;
struct tep_print_arg *args;
struct tep_print_parse *print_cache;
};
struct tep_event {
......@@ -393,14 +382,29 @@ struct tep_plugin_list;
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
enum tep_plugin_load_priority {
TEP_PLUGIN_FIRST,
TEP_PLUGIN_LAST,
};
int tep_add_plugin_path(struct tep_handle *tep, char *path,
enum tep_plugin_load_priority prio);
struct tep_plugin_list *tep_load_plugins(struct tep_handle *tep);
void tep_unload_plugins(struct tep_plugin_list *plugin_list,
struct tep_handle *tep);
void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
void (*load_plugin)(struct tep_handle *tep,
const char *path,
const char *name,
void *data),
void *data);
char **tep_plugin_list_options(void);
void tep_plugin_free_options_list(char **list);
int tep_plugin_add_options(const char *name,
struct tep_plugin_option *options);
int tep_plugin_add_option(const char *name, const char *val);
void tep_plugin_remove_options(struct tep_plugin_option *options);
void tep_plugin_print_options(struct trace_seq *s);
void tep_print_plugins(struct trace_seq *s,
const char *prefix, const char *suffix,
const struct tep_plugin_list *list);
......
......@@ -13,6 +13,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include "event-parse.h"
#include "event-parse-local.h"
#include "event-utils.h"
......@@ -38,6 +39,12 @@ struct tep_plugin_list {
void *handle;
};
struct tep_plugins_dir {
struct tep_plugins_dir *next;
char *path;
enum tep_plugin_load_priority prio;
};
static void lower_case(char *str)
{
if (!str)
......@@ -247,6 +254,170 @@ void tep_plugin_remove_options(struct tep_plugin_option *options)
}
}
static int parse_option_name(char **option, char **plugin)
{
char *p;
*plugin = NULL;
if ((p = strstr(*option, ":"))) {
*plugin = *option;
*p = '\0';
*option = strdup(p + 1);
if (!*option)
return -1;
}
return 0;
}
static struct tep_plugin_option *
find_registered_option(const char *plugin, const char *option)
{
struct registered_plugin_options *reg;
struct tep_plugin_option *op;
const char *op_plugin;
for (reg = registered_options; reg; reg = reg->next) {
for (op = reg->options; op->name; op++) {
if (op->plugin_alias)
op_plugin = op->plugin_alias;
else
op_plugin = op->file;
if (plugin && strcmp(plugin, op_plugin) != 0)
continue;
if (strcmp(option, op->name) != 0)
continue;
return op;
}
}
return NULL;
}
static int process_option(const char *plugin, const char *option, const char *val)
{
struct tep_plugin_option *op;
op = find_registered_option(plugin, option);
if (!op)
return 0;
return update_option_value(op, val);
}
/**
* tep_plugin_add_option - add an option/val pair to set plugin options
* @name: The name of the option (format: <plugin>:<option> or just <option>)
* @val: (optional) the value for the option
*
* Modify a plugin option. If @val is given than the value of the option
* is set (note, some options just take a boolean, so @val must be either
* "1" or "0" or "true" or "false").
*/
int tep_plugin_add_option(const char *name, const char *val)
{
struct trace_plugin_options *op;
char *option_str;
char *plugin;
option_str = strdup(name);
if (!option_str)
return -ENOMEM;
if (parse_option_name(&option_str, &plugin) < 0)
return -ENOMEM;
/* If the option exists, update the val */
for (op = trace_plugin_options; op; op = op->next) {
/* Both must be NULL or not NULL */
if ((!plugin || !op->plugin) && plugin != op->plugin)
continue;
if (plugin && strcmp(plugin, op->plugin) != 0)
continue;
if (strcmp(op->option, option_str) != 0)
continue;
/* update option */
free(op->value);
if (val) {
op->value = strdup(val);
if (!op->value)
goto out_free;
} else
op->value = NULL;
/* plugin and option_str don't get freed at the end */
free(plugin);
free(option_str);
plugin = op->plugin;
option_str = op->option;
break;
}
/* If not found, create */
if (!op) {
op = malloc(sizeof(*op));
if (!op)
goto out_free;
memset(op, 0, sizeof(*op));
op->plugin = plugin;
op->option = option_str;
if (val) {
op->value = strdup(val);
if (!op->value) {
free(op);
goto out_free;
}
}
op->next = trace_plugin_options;
trace_plugin_options = op;
}
return process_option(plugin, option_str, val);
out_free:
free(plugin);
free(option_str);
return -ENOMEM;
}
static void print_op_data(struct trace_seq *s, const char *name,
const char *op)
{
if (op)
trace_seq_printf(s, "%8s:\t%s\n", name, op);
}
/**
* tep_plugin_print_options - print out the registered plugin options
* @s: The trace_seq descriptor to write the plugin options into
*
* Writes a list of options into trace_seq @s.
*/
void tep_plugin_print_options(struct trace_seq *s)
{
struct registered_plugin_options *reg;
struct tep_plugin_option *op;
for (reg = registered_options; reg; reg = reg->next) {
if (reg != registered_options)
trace_seq_printf(s, "============\n");
for (op = reg->options; op->name; op++) {
if (op != reg->options)
trace_seq_printf(s, "------------\n");
print_op_data(s, "file", op->file);
print_op_data(s, "plugin", op->plugin_alias);
print_op_data(s, "option", op->name);
print_op_data(s, "desc", op->description);
print_op_data(s, "value", op->value);
trace_seq_printf(s, "%8s:\t%d\n", "set", op->set);
}
}
}
/**
* tep_print_plugins - print out the list of plugins loaded
* @s: the trace_seq descripter to write to
......@@ -273,6 +444,7 @@ load_plugin(struct tep_handle *tep, const char *path,
const char *file, void *data)
{
struct tep_plugin_list **plugin_list = data;
struct tep_plugin_option *options;
tep_plugin_load_func func;
struct tep_plugin_list *list;
const char *alias;
......@@ -297,6 +469,16 @@ load_plugin(struct tep_handle *tep, const char *path,
if (!alias)
alias = file;
options = dlsym(handle, TEP_PLUGIN_OPTIONS_NAME);
if (options) {
while (options->name) {
ret = update_option(alias, options);
if (ret < 0)
goto out_free;
options++;
}
}
func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
if (!func) {
warning("could not find func '%s' in plugin '%s'\n%s\n",
......@@ -365,28 +547,53 @@ load_plugins_dir(struct tep_handle *tep, const char *suffix,
closedir(dir);
}
static void
load_plugins(struct tep_handle *tep, const char *suffix,
void (*load_plugin)(struct tep_handle *tep,
const char *path,
const char *name,
void *data),
void *data)
/**
* tep_load_plugins_hook - call a user specified callback to load a plugin
* @tep: handler to traceevent context
* @suffix: filter only plugin files with given suffix
* @load_plugin: user specified callback, called for each plugin file
* @data: custom context, passed to @load_plugin
*
* Searches for traceevent plugin files and calls @load_plugin for each
* The order of plugins search is:
* - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_FIRST
* - Directory, specified at compile time with PLUGIN_TRACEEVENT_DIR
* - Directory, specified by environment variable TRACEEVENT_PLUGIN_DIR
* - In user's home: ~/.local/lib/traceevent/plugins/
* - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_LAST
*
*/
void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
void (*load_plugin)(struct tep_handle *tep,
const char *path,
const char *name,
void *data),
void *data)
{
struct tep_plugins_dir *dir = NULL;
char *home;
char *path;
char *envdir;
int ret;
if (tep->flags & TEP_DISABLE_PLUGINS)
if (tep && tep->flags & TEP_DISABLE_PLUGINS)
return;
if (tep)
dir = tep->plugins_dir;
while (dir) {
if (dir->prio == TEP_PLUGIN_FIRST)
load_plugins_dir(tep, suffix, dir->path,
load_plugin, data);
dir = dir->next;
}
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
if (!(tep->flags & TEP_DISABLE_SYS_PLUGINS))
if (!tep || !(tep->flags & TEP_DISABLE_SYS_PLUGINS))
load_plugins_dir(tep, suffix, PLUGIN_DIR,
load_plugin, data);
#endif
......@@ -415,6 +622,15 @@ load_plugins(struct tep_handle *tep, const char *suffix,
load_plugins_dir(tep, suffix, path, load_plugin, data);
if (tep)
dir = tep->plugins_dir;
while (dir) {
if (dir->prio == TEP_PLUGIN_LAST)
load_plugins_dir(tep, suffix, dir->path,
load_plugin, data);
dir = dir->next;
}
free(path);
}
......@@ -423,10 +639,59 @@ tep_load_plugins(struct tep_handle *tep)
{
struct tep_plugin_list *list = NULL;
load_plugins(tep, ".so", load_plugin, &list);
tep_load_plugins_hook(tep, ".so", load_plugin, &list);
return list;
}
/**
* tep_add_plugin_path - Add a new plugin directory.
* @tep: Trace event handler.
* @path: Path to a directory. All plugin files in that
* directory will be loaded.
*@prio: Load priority of the plugins in that directory.
*
* Returns -1 in case of an error, 0 otherwise.
*/
int tep_add_plugin_path(struct tep_handle *tep, char *path,
enum tep_plugin_load_priority prio)
{
struct tep_plugins_dir *dir;
if (!tep || !path)
return -1;
dir = calloc(1, sizeof(*dir));
if (!dir)
return -1;
dir->path = strdup(path);
if (!dir->path) {
free(dir);
return -1;
}
dir->prio = prio;
dir->next = tep->plugins_dir;
tep->plugins_dir = dir;
return 0;
}
void tep_free_plugin_paths(struct tep_handle *tep)
{
struct tep_plugins_dir *dir;
if (!tep)
return;
dir = tep->plugins_dir;
while (dir) {
tep->plugins_dir = tep->plugins_dir->next;
free(dir->path);
free(dir);
dir = tep->plugins_dir;
}
}
void
tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep)
{
......
/* SPDX-License-Identifier: LGPL-2.1 */
/*
* Copyright (C) 2012 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#ifndef _KBUFFER_H
#define _KBUFFER_H
......
......@@ -5,6 +5,8 @@ plugin_kvm-y += plugin_kvm.o
plugin_mac80211-y += plugin_mac80211.o
plugin_sched_switch-y += plugin_sched_switch.o
plugin_function-y += plugin_function.o
plugin_futex-y += plugin_futex.o
plugin_xen-y += plugin_xen.o
plugin_scsi-y += plugin_scsi.o
plugin_cfg80211-y += plugin_cfg80211.o
plugin_tlb-y += plugin_tlb.o
\ No newline at end of file
......@@ -134,9 +134,11 @@ PLUGINS += plugin_kvm.so
PLUGINS += plugin_mac80211.so
PLUGINS += plugin_sched_switch.so
PLUGINS += plugin_function.so
PLUGINS += plugin_futex.so
PLUGINS += plugin_xen.so
PLUGINS += plugin_scsi.so
PLUGINS += plugin_cfg80211.so
PLUGINS += plugin_tlb.so
PLUGINS := $(addprefix $(OUTPUT),$(PLUGINS))
PLUGINS_IN := $(PLUGINS:.so=-in.o)
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......@@ -49,6 +34,13 @@ struct tep_plugin_option plugin_options[] =
"Try to show function call indents, based on parents",
.set = 1,
},
{
.name = "offset",
.plugin_alias = "ftrace",
.description =
"Show function names as well as their offsets",
.set = 0,
},
{
.name = NULL,
}
......@@ -56,6 +48,7 @@ struct tep_plugin_option plugin_options[] =
static struct tep_plugin_option *ftrace_parent = &plugin_options[0];
static struct tep_plugin_option *ftrace_indent = &plugin_options[1];
static struct tep_plugin_option *ftrace_offset = &plugin_options[2];
static void add_child(struct func_stack *stack, const char *child, int pos)
{
......@@ -123,6 +116,18 @@ static int add_and_get_index(const char *parent, const char *child, int cpu)
return 0;
}
static void show_function(struct trace_seq *s, struct tep_handle *tep,
const char *func, unsigned long long function)
{
unsigned long long offset;
trace_seq_printf(s, "%s", func);
if (ftrace_offset->set) {
offset = tep_find_function_address(tep, function);
trace_seq_printf(s, "+0x%x ", (int)(function - offset));
}
}
static int function_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
......@@ -149,14 +154,14 @@ static int function_handler(struct trace_seq *s, struct tep_record *record,
trace_seq_printf(s, "%*s", index*3, "");
if (func)
trace_seq_printf(s, "%s", func);
show_function(s, tep, func, function);
else
trace_seq_printf(s, "0x%llx", function);
if (ftrace_parent->set) {
trace_seq_printf(s, " <-- ");
if (parent)
trace_seq_printf(s, "%s", parent);
show_function(s, tep, parent, pfunction);
else
trace_seq_printf(s, "0x%llx", pfunction);
}
......@@ -164,11 +169,93 @@ static int function_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
static int
trace_stack_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct tep_format_field *field;
unsigned long long addr;
const char *func;
int long_size;
void *data = record->data;
field = tep_find_any_field(event, "caller");
if (!field) {
trace_seq_printf(s, "<CANT FIND FIELD %s>", "caller");
return 0;
}
trace_seq_puts(s, "<stack trace >\n");
long_size = tep_get_long_size(event->tep);
for (data += field->offset; data < record->data + record->size;
data += long_size) {
addr = tep_read_number(event->tep, data, long_size);
if ((long_size == 8 && addr == (unsigned long long)-1) ||
((int)addr == -1))
break;
func = tep_find_function(event->tep, addr);
if (func)
trace_seq_printf(s, "=> %s (%llx)\n", func, addr);
else
trace_seq_printf(s, "=> %llx\n", addr);
}
return 0;
}
static int
trace_raw_data_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct tep_format_field *field;
unsigned long long id;
int long_size;
void *data = record->data;
if (tep_get_field_val(s, event, "id", record, &id, 1))
return trace_seq_putc(s, '!');
trace_seq_printf(s, "# %llx", id);
field = tep_find_any_field(event, "buf");
if (!field) {
trace_seq_printf(s, "<CANT FIND FIELD %s>", "buf");
return 0;
}
long_size = tep_get_long_size(event->tep);
for (data += field->offset; data < record->data + record->size;
data += long_size) {
int size = sizeof(long);
int left = (record->data + record->size) - data;
int i;
if (size > left)
size = left;
for (i = 0; i < size; i++)
trace_seq_printf(s, " %02x", *(unsigned char *)(data + i));
}
return 0;
}
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
{
tep_register_event_handler(tep, -1, "ftrace", "function",
function_handler, NULL);
tep_register_event_handler(tep, -1, "ftrace", "kernel_stack",
trace_stack_handler, NULL);
tep_register_event_handler(tep, -1, "ftrace", "raw_data",
trace_raw_data_handler, NULL);
tep_plugin_add_options("ftrace", plugin_options);
return 0;
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 National Instruments Corp.
*
* Author: Julia Cartwright <julia@ni.com>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/futex.h>
#include "event-parse.h"
#define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))
struct futex_args {
unsigned long long uaddr;
unsigned long long op;
unsigned long long val;
unsigned long long utime; /* or val2 */
unsigned long long uaddr2;
unsigned long long val3;
};
struct futex_op {
const char *name;
const char *fmt_val;
const char *fmt_utime;
const char *fmt_uaddr2;
const char *fmt_val3;
};
static const struct futex_op futex_op_tbl[] = {
{ "FUTEX_WAIT", " val=0x%08llx", " utime=0x%08llx", NULL, NULL },
{ "FUTEX_WAKE", " val=%llu", NULL, NULL, NULL },
{ "FUTEX_FD", " val=%llu", NULL, NULL, NULL },
{ "FUTEX_REQUEUE", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", NULL },
{ "FUTEX_CMP_REQUEUE", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
{ "FUTEX_WAKE_OP", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
{ "FUTEX_LOCK_PI", NULL, " utime=0x%08llx", NULL, NULL },
{ "FUTEX_UNLOCK_PI", NULL, NULL, NULL, NULL },
{ "FUTEX_TRYLOCK_PI", NULL, NULL, NULL, NULL },
{ "FUTEX_WAIT_BITSET", " val=0x%08llx", " utime=0x%08llx", NULL, " val3=0x%08llx" },
{ "FUTEX_WAKE_BITSET", " val=%llu", NULL, NULL, " val3=0x%08llx" },
{ "FUTEX_WAIT_REQUEUE_PI", " val=0x%08llx", " utime=0x%08llx", " uaddr2=0x%08llx", " val3=0x%08llx" },
{ "FUTEX_CMP_REQUEUE_PI", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
};
static void futex_print(struct trace_seq *s, const struct futex_args *args,
const struct futex_op *fop)
{
trace_seq_printf(s, " uaddr=0x%08llx", args->uaddr);
if (fop->fmt_val)
trace_seq_printf(s, fop->fmt_val, args->val);
if (fop->fmt_utime)
trace_seq_printf(s,fop->fmt_utime, args->utime);
if (fop->fmt_uaddr2)
trace_seq_printf(s, fop->fmt_uaddr2, args->uaddr2);
if (fop->fmt_val3)
trace_seq_printf(s, fop->fmt_val3, args->val3);
}
static int futex_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
const struct futex_op *fop;
struct futex_args args;
unsigned long long cmd;
if (tep_get_field_val(s, event, "uaddr", record, &args.uaddr, 1))
return 1;
if (tep_get_field_val(s, event, "op", record, &args.op, 1))
return 1;
if (tep_get_field_val(s, event, "val", record, &args.val, 1))
return 1;
if (tep_get_field_val(s, event, "utime", record, &args.utime, 1))
return 1;
if (tep_get_field_val(s, event, "uaddr2", record, &args.uaddr2, 1))
return 1;
if (tep_get_field_val(s, event, "val3", record, &args.val3, 1))
return 1;
cmd = args.op & FUTEX_CMD_MASK;
if (cmd >= ARRAY_SIZE(futex_op_tbl))
return 1;
fop = &futex_op_tbl[cmd];
trace_seq_printf(s, "op=%s", fop->name);
if (args.op & FUTEX_PRIVATE_FLAG)
trace_seq_puts(s, "|FUTEX_PRIVATE_FLAG");
if (args.op & FUTEX_CLOCK_REALTIME)
trace_seq_puts(s, "|FUTEX_CLOCK_REALTIME");
futex_print(s, &args, fop);
return 0;
}
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
{
tep_register_event_handler(tep, -1, "syscalls", "sys_enter_futex",
futex_handler, NULL);
return 0;
}
void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
{
tep_unregister_event_handler(tep, -1, "syscalls", "sys_enter_futex",
futex_handler, NULL);
}
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......@@ -155,7 +140,23 @@ static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
_ER(EXIT_WRITE_DR5, 0x035) \
_ER(EXIT_WRITE_DR6, 0x036) \
_ER(EXIT_WRITE_DR7, 0x037) \
_ER(EXIT_EXCP_BASE, 0x040) \
_ER(EXIT_EXCP_DE, 0x040) \
_ER(EXIT_EXCP_DB, 0x041) \
_ER(EXIT_EXCP_BP, 0x043) \
_ER(EXIT_EXCP_OF, 0x044) \
_ER(EXIT_EXCP_BR, 0x045) \
_ER(EXIT_EXCP_UD, 0x046) \
_ER(EXIT_EXCP_NM, 0x047) \
_ER(EXIT_EXCP_DF, 0x048) \
_ER(EXIT_EXCP_TS, 0x04a) \
_ER(EXIT_EXCP_NP, 0x04b) \
_ER(EXIT_EXCP_SS, 0x04c) \
_ER(EXIT_EXCP_GP, 0x04d) \
_ER(EXIT_EXCP_PF, 0x04e) \
_ER(EXIT_EXCP_MF, 0x050) \
_ER(EXIT_EXCP_AC, 0x051) \
_ER(EXIT_EXCP_MC, 0x052) \
_ER(EXIT_EXCP_XF, 0x053) \
_ER(EXIT_INTR, 0x060) \
_ER(EXIT_NMI, 0x061) \
_ER(EXIT_SMI, 0x062) \
......@@ -201,7 +202,10 @@ static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
_ER(EXIT_MONITOR, 0x08a) \
_ER(EXIT_MWAIT, 0x08b) \
_ER(EXIT_MWAIT_COND, 0x08c) \
_ER(EXIT_NPF, 0x400) \
_ER(EXIT_XSETBV, 0x08d) \
_ER(EXIT_NPF, 0x400) \
_ER(EXIT_AVIC_INCOMPLETE_IPI, 0x401) \
_ER(EXIT_AVIC_UNACCELERATED_ACCESS, 0x402) \
_ER(EXIT_ERR, -1)
#define _ER(reason, val) { #reason, val },
......@@ -241,7 +245,7 @@ static const char *find_exit_reason(unsigned isa, int val)
}
if (!strings)
return "UNKNOWN-ISA";
for (i = 0; strings[i].val >= 0; i++)
for (i = 0; strings[i].str; i++)
if (strings[i].val == val)
break;
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
......
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2015 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
enum tlb_flush_reason {
TLB_FLUSH_ON_TASK_SWITCH,
TLB_REMOTE_SHOOTDOWN,
TLB_LOCAL_SHOOTDOWN,
TLB_LOCAL_MM_SHOOTDOWN,
NR_TLB_FLUSH_REASONS,
};
static int tlb_flush_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
unsigned long long val;
trace_seq_printf(s, "pages=");
tep_print_num_field(s, "%ld", event, "pages", record, 1);
if (tep_get_field_val(s, event, "reason", record, &val, 1) < 0)
return -1;
trace_seq_puts(s, " reason=");
switch (val) {
case TLB_FLUSH_ON_TASK_SWITCH:
trace_seq_puts(s, "flush on task switch");
break;
case TLB_REMOTE_SHOOTDOWN:
trace_seq_puts(s, "remote shootdown");
break;
case TLB_LOCAL_SHOOTDOWN:
trace_seq_puts(s, "local shootdown");
break;
case TLB_LOCAL_MM_SHOOTDOWN:
trace_seq_puts(s, "local mm shootdown");
break;
}
trace_seq_printf(s, " (%lld)", val);
return 0;
}
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
{
tep_register_event_handler(tep, -1, "tlb", "tlb_flush",
tlb_flush_handler, NULL);
return 0;
}
void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
{
tep_unregister_event_handler(tep, -1,
"tlb", "tlb_flush",
tlb_flush_handler, NULL);
}
......@@ -18,6 +18,7 @@
l synthesize last branch entries (use with i or x)
L synthesize last branch entries on existing event records
s skip initial number of events
q quicker (less detailed) decoding
The default is all events i.e. the same as --itrace=ibxwpe,
except for perf script where it is --itrace=ce
......@@ -47,3 +48,16 @@
--itrace=i0nss1000000
skips the first million instructions.
The 'e' option may be followed by flags which affect what errors will or
will not be reported. Each flag must be preceded by either '+' or '-'.
The flags are:
o overflow
l trace data lost
If supported, the 'd' option may be followed by flags which affect what
debug messages will or will not be logged. Each flag must be preceded
by either '+' or '-'. The flags are:
a all perf events
If supported, the 'q' option may be repeated to increase the effect.
......@@ -49,6 +49,9 @@ SUBSYSTEM
'sched'::
Scheduler and IPC mechanisms.
'syscall'::
System call performance (throughput).
'mem'::
Memory access performance.
......@@ -137,6 +140,14 @@ Example of *pipe*
59004 ops/sec
---------------------
SUITES FOR 'syscall'
~~~~~~~~~~~~~~~~~~
*basic*::
Suite for evaluating performance of core system call throughput (both usecs/op and ops/sec metrics).
This uses a single thread simply doing getppid(2), which is a simple syscall where the result is not
cached by glibc.
SUITES FOR 'mem'
~~~~~~~~~~~~~~~~
*memcpy*::
......
......@@ -27,6 +27,9 @@ OPTIONS for 'convert'
--to-ctf::
Triggers the CTF conversion, specify the path of CTF data directory.
--tod::
Convert time to wall clock time.
-i::
Specify input perf data file path.
......
......@@ -825,6 +825,7 @@ The letters are:
l synthesize last branch entries (use with i or x)
L synthesize last branch entries on existing event records
s skip initial number of events
q quicker (less detailed) decoding
"Instructions" events look like they were recorded by "perf record -e
instructions".
......@@ -871,11 +872,24 @@ Developer Manuals.
Error events show where the decoder lost the trace. Error events
are quite important. Users must know if what they are seeing is a complete
picture or not.
picture or not. The "e" option may be followed by flags which affect what errors
will or will not be reported. Each flag must be preceded by either '+' or '-'.
The flags supported by Intel PT are:
-o Suppress overflow errors
-l Suppress trace data lost errors
For example, for errors but not overflow or data lost errors:
--itrace=e-o-l
The "d" option will cause the creation of a file "intel_pt.log" containing all
decoded packets and instructions. Note that this option slows down the decoder
and that the resulting file may be very large.
and that the resulting file may be very large. The "d" option may be followed
by flags which affect what debug messages will or will not be logged. Each flag
must be preceded by either '+' or '-'. The flags support by Intel PT are:
-a Suppress logging of perf events
+a Log all perf events
By default, logged perf events are filtered by any specified time ranges, but
flag +a overrides that.
In addition, the period of the "instructions" event can be specified. e.g.
......@@ -956,6 +970,51 @@ at the beginning. This is useful to ignore initialization code.
skips the first million instructions.
The q option changes the way the trace is decoded. The decoding is much faster
but much less detailed. Specifically, with the q option, the decoder does not
decode TNT packets, and does not walk object code, but gets the ip from FUP and
TIP packets. The q option can be used with the b and i options but the period
is not used. The q option decodes more quickly, but is useful only if the
control flow of interest is represented or indicated by FUP, TIP, TIP.PGE, or
TIP.PGD packets (refer below). However the q option could be used to find time
ranges that could then be decoded fully using the --time option.
What will *not* be decoded with the (single) q option:
- direct calls and jmps
- conditional branches
- non-branch instructions
What *will* be decoded with the (single) q option:
- asynchronous branches such as interrupts
- indirect branches
- function return target address *if* the noretcomp config term (refer
config terms section) was used
- start of (control-flow) tracing
- end of (control-flow) tracing, if it is not out of context
- power events, ptwrite, transaction start and abort
- instruction pointer associated with PSB packets
Note the q option does not specify what events will be synthesized e.g. the p
option must be used also to show power events.
Repeating the q option (double-q i.e. qq) results in even faster decoding and even
less detail. The decoder decodes only extended PSB (PSB+) packets, getting the
instruction pointer if there is a FUP packet within PSB+ (i.e. between PSB and
PSBEND). Note PSB packets occur regularly in the trace based on the psb_period
config term (refer config terms section). There will be a FUP packet if the
PSB+ occurs while control flow is being traced.
What will *not* be decoded with the qq option:
- everything except instruction pointer associated with PSB packets
What *will* be decoded with the qq option:
- instruction pointer associated with PSB packets
dump option
~~~~~~~~~~~
......
......@@ -119,6 +119,7 @@ It's also possible to use pmu syntax:
perf record -e r1a8 -a sleep 1
perf record -e cpu/r1a8/ ...
perf record -e cpu/r0x1a8/ ...
You should refer to the processor specific documentation for getting these
details. Some of them are referenced in the SEE ALSO section below.
......
......@@ -407,8 +407,9 @@ if combined with -a or -C options.
-D::
--delay=::
After starting the program, wait msecs before measuring. This is useful to
filter out the startup phase of the program, which is often very different.
After starting the program, wait msecs before measuring (-1: start with events
disabled). This is useful to filter out the startup phase of the program, which
is often very different.
-I::
--intr-regs::
......@@ -626,6 +627,45 @@ option. The -e option and this one can be mixed and matched. Events
can be grouped using the {} notation.
endif::HAVE_LIBPFM[]
--control fd:ctl-fd[,ack-fd]
Listen on ctl-fd descriptor for command to control measurement ('enable': enable events,
'disable': disable events). Measurements can be started with events disabled using
--delay=-1 option. Optionally send control command completion ('ack\n') to ack-fd descriptor
to synchronize with the controlling process. Example of bash shell script to enable and
disable events during measurements:
#!/bin/bash
ctl_dir=/tmp/
ctl_fifo=${ctl_dir}perf_ctl.fifo
test -p ${ctl_fifo} && unlink ${ctl_fifo}
mkfifo ${ctl_fifo}
exec {ctl_fd}<>${ctl_fifo}
ctl_ack_fifo=${ctl_dir}perf_ctl_ack.fifo
test -p ${ctl_ack_fifo} && unlink ${ctl_ack_fifo}
mkfifo ${ctl_ack_fifo}
exec {ctl_fd_ack}<>${ctl_ack_fifo}
perf record -D -1 -e cpu-cycles -a \
--control fd:${ctl_fd},${ctl_fd_ack} \
-- sleep 30 &
perf_pid=$!
sleep 5 && echo 'enable' >&${ctl_fd} && read -u ${ctl_fd_ack} e1 && echo "enabled(${e1})"
sleep 10 && echo 'disable' >&${ctl_fd} && read -u ${ctl_fd_ack} d1 && echo "disabled(${d1})"
exec {ctl_fd_ack}>&-
unlink ${ctl_ack_fifo}
exec {ctl_fd}>&-
unlink ${ctl_fifo}
wait -n ${perf_pid}
exit $?
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1]
......@@ -322,6 +322,10 @@ OPTIONS
--show-cgroup-events
Display cgroup events i.e. events of type PERF_RECORD_CGROUP.
--show-text-poke-events
Display text poke events i.e. events of type PERF_RECORD_TEXT_POKE and
PERF_RECORD_KSYMBOL.
--demangle::
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
......
......@@ -176,6 +176,45 @@ with it. --append may be used here. Examples:
3>results perf stat --log-fd 3 -- $cmd
3>>results perf stat --log-fd 3 --append -- $cmd
--control fd:ctl-fd[,ack-fd]
Listen on ctl-fd descriptor for command to control measurement ('enable': enable events,
'disable': disable events). Measurements can be started with events disabled using
--delay=-1 option. Optionally send control command completion ('ack\n') to ack-fd descriptor
to synchronize with the controlling process. Example of bash shell script to enable and
disable events during measurements:
#!/bin/bash
ctl_dir=/tmp/
ctl_fifo=${ctl_dir}perf_ctl.fifo
test -p ${ctl_fifo} && unlink ${ctl_fifo}
mkfifo ${ctl_fifo}
exec {ctl_fd}<>${ctl_fifo}
ctl_ack_fifo=${ctl_dir}perf_ctl_ack.fifo
test -p ${ctl_ack_fifo} && unlink ${ctl_ack_fifo}
mkfifo ${ctl_ack_fifo}
exec {ctl_fd_ack}<>${ctl_ack_fifo}
perf stat -D -1 -e cpu-cycles -a -I 1000 \
--control fd:${ctl_fd},${ctl_fd_ack} \
-- sleep 30 &
perf_pid=$!
sleep 5 && echo 'enable' >&${ctl_fd} && read -u ${ctl_fd_ack} e1 && echo "enabled(${e1})"
sleep 10 && echo 'disable' >&${ctl_fd} && read -u ${ctl_fd_ack} d1 && echo "disabled(${d1})"
exec {ctl_fd_ack}>&-
unlink ${ctl_ack_fifo}
exec {ctl_fd}>&-
unlink ${ctl_fifo}
wait -n ${perf_pid}
exit $?
--pre::
--post::
Pre and post measurement hooks, e.g.:
......@@ -238,8 +277,9 @@ mode, use --per-node in addition to -a. (system-wide).
-D msecs::
--delay msecs::
After starting the program, wait msecs before measuring. This is useful to
filter out the startup phase of the program, which is often very different.
After starting the program, wait msecs before measuring (-1: start with events
disabled). This is useful to filter out the startup phase of the program,
which is often very different.
-T::
--transaction::
......
......@@ -389,6 +389,19 @@ struct {
Example:
cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
HEADER_CLOCK_DATA = 29,
Contains clock id and its reference time together with wall clock
time taken at the 'same time', both values are in nanoseconds.
The format of data is as below.
struct {
u32 version; /* version = 1 */
u32 clockid;
u64 wall_clock_ns;
u64 clockid_time_ns;
};
other bits are reserved and should ignored for now
HEADER_FEAT_BITS = 256,
......
......@@ -57,17 +57,15 @@ struct auxtrace_record
struct evsel *evsel;
bool found_etm = false;
struct perf_pmu *found_spe = NULL;
static struct perf_pmu **arm_spe_pmus = NULL;
static int nr_spes = 0;
struct perf_pmu **arm_spe_pmus = NULL;
int nr_spes = 0;
int i = 0;
if (!evlist)
return NULL;
cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
if (!arm_spe_pmus)
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
evlist__for_each_entry(evlist, evsel) {
if (cs_etm_pmu &&
......@@ -84,6 +82,7 @@ struct auxtrace_record
}
}
}
free(arm_spe_pmus);
if (found_etm && found_spe) {
pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n");
......
......@@ -243,10 +243,10 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
}
/*
* No sink was provided on the command line - for _now_ treat
* this as an error.
* No sink was provided on the command line - allow the CoreSight
* system to look for a default
*/
return ret;
return 0;
}
static int cs_etm_recording_options(struct auxtrace_record *itr,
......
......@@ -64,7 +64,13 @@ static const char *reg_names[] = {
[PERF_REG_POWERPC_DAR] = "dar",
[PERF_REG_POWERPC_DSISR] = "dsisr",
[PERF_REG_POWERPC_SIER] = "sier",
[PERF_REG_POWERPC_MMCRA] = "mmcra"
[PERF_REG_POWERPC_MMCRA] = "mmcra",
[PERF_REG_POWERPC_MMCR0] = "mmcr0",
[PERF_REG_POWERPC_MMCR1] = "mmcr1",
[PERF_REG_POWERPC_MMCR2] = "mmcr2",
[PERF_REG_POWERPC_MMCR3] = "mmcr3",
[PERF_REG_POWERPC_SIER2] = "sier2",
[PERF_REG_POWERPC_SIER3] = "sier3",
};
static inline const char *perf_reg_name(int id)
......
......@@ -7,17 +7,10 @@
#include <string.h>
#include <linux/stringify.h>
#include "header.h"
#include "utils_header.h"
#include "metricgroup.h"
#include <api/fs/fs.h>
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define SPRN_PVR 0x11F /* Processor Version Register */
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
int
get_cpuid(char *buffer, size_t sz)
{
......
......@@ -6,9 +6,16 @@
#include "../../../util/perf_regs.h"
#include "../../../util/debug.h"
#include "../../../util/event.h"
#include "../../../util/header.h"
#include "../../../perf-sys.h"
#include "utils_header.h"
#include <linux/kernel.h>
#define PVR_POWER9 0x004E
#define PVR_POWER10 0x0080
const struct sample_reg sample_reg_masks[] = {
SMPL_REG(r0, PERF_REG_POWERPC_R0),
SMPL_REG(r1, PERF_REG_POWERPC_R1),
......@@ -55,6 +62,12 @@ const struct sample_reg sample_reg_masks[] = {
SMPL_REG(dsisr, PERF_REG_POWERPC_DSISR),
SMPL_REG(sier, PERF_REG_POWERPC_SIER),
SMPL_REG(mmcra, PERF_REG_POWERPC_MMCRA),
SMPL_REG(mmcr0, PERF_REG_POWERPC_MMCR0),
SMPL_REG(mmcr1, PERF_REG_POWERPC_MMCR1),
SMPL_REG(mmcr2, PERF_REG_POWERPC_MMCR2),
SMPL_REG(mmcr3, PERF_REG_POWERPC_MMCR3),
SMPL_REG(sier2, PERF_REG_POWERPC_SIER2),
SMPL_REG(sier3, PERF_REG_POWERPC_SIER3),
SMPL_REG_END
};
......@@ -163,3 +176,45 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op)
return SDT_ARG_VALID;
}
uint64_t arch__intr_reg_mask(void)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.sample_type = PERF_SAMPLE_REGS_INTR,
.precise_ip = 1,
.disabled = 1,
.exclude_kernel = 1,
};
int fd;
u32 version;
u64 extended_mask = 0, mask = PERF_REGS_MASK;
/*
* Get the PVR value to set the extended
* mask specific to platform.
*/
version = (((mfspr(SPRN_PVR)) >> 16) & 0xFFFF);
if (version == PVR_POWER9)
extended_mask = PERF_REG_PMU_MASK_300;
else if (version == PVR_POWER10)
extended_mask = PERF_REG_PMU_MASK_31;
else
return mask;
attr.sample_regs_intr = extended_mask;
attr.sample_period = 1;
event_attr_init(&attr);
/*
* check if the pmu supports perf extended regs, before
* returning the register mask to sample.
*/
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd != -1) {
close(fd);
mask |= extended_mask;
}
return mask;
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_UTIL_HEADER_H
#define __PERF_UTIL_HEADER_H
#include <linux/stringify.h>
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval; })
#define SPRN_PVR 0x11F /* Processor Version Register */
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
#endif /* __PERF_UTIL_HEADER_H */
......@@ -357,6 +357,7 @@
433 common fspick sys_fspick
434 common pidfd_open sys_pidfd_open
435 common clone3 sys_clone3
436 common close_range sys_close_range
437 common openat2 sys_openat2
438 common pidfd_getfd sys_pidfd_getfd
439 common faccessat2 sys_faccessat2
......
......@@ -837,6 +837,10 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
}
}
if (have_timing_info && !intel_pt_evsel->core.attr.exclude_kernel &&
perf_can_record_text_poke_events() && perf_can_record_cpu_wide())
opts->text_poke = true;
if (intel_pt_evsel) {
/*
* To obtain the auxtrace buffer file descriptor, the auxtrace
......
perf-y += sched-messaging.o
perf-y += sched-pipe.o
perf-y += syscall.o
perf-y += mem-functions.o
perf-y += futex-hash.o
perf-y += futex-wake.o
......@@ -10,6 +11,7 @@ perf-y += epoll-wait.o
perf-y += epoll-ctl.o
perf-y += synthesize.o
perf-y += kallsyms-parse.o
perf-y += find-bit-bench.o
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-lib.o
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
......
......@@ -33,8 +33,10 @@ extern struct timeval bench__start, bench__end, bench__runtime;
int bench_numa(int argc, const char **argv);
int bench_sched_messaging(int argc, const char **argv);
int bench_sched_pipe(int argc, const char **argv);
int bench_syscall_basic(int argc, const char **argv);
int bench_mem_memcpy(int argc, const char **argv);
int bench_mem_memset(int argc, const char **argv);
int bench_mem_find_bit(int argc, const char **argv);
int bench_futex_hash(int argc, const char **argv);
int bench_futex_wake(int argc, const char **argv);
int bench_futex_wake_parallel(int argc, const char **argv);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Benchmark find_next_bit and related bit operations.
*
* Copyright 2020 Google LLC.
*/
#include <stdlib.h>
#include "bench.h"
#include "../util/stat.h"
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/time64.h>
#include <subcmd/parse-options.h>
static unsigned int outer_iterations = 5;
static unsigned int inner_iterations = 100000;
static const struct option options[] = {
OPT_UINTEGER('i', "outer-iterations", &outer_iterations,
"Number of outerer iterations used"),
OPT_UINTEGER('j', "inner-iterations", &inner_iterations,
"Number of outerer iterations used"),
OPT_END()
};
static const char *const bench_usage[] = {
"perf bench mem find_bit <options>",
NULL
};
static unsigned int accumulator;
static unsigned int use_of_val;
static noinline void workload(int val)
{
use_of_val += val;
accumulator++;
}
#if (defined(__i386__) || defined(__x86_64__)) && defined(__GCC_ASM_FLAG_OUTPUTS__)
static bool asm_test_bit(long nr, const unsigned long *addr)
{
bool oldbit;
asm volatile("bt %2,%1"
: "=@ccc" (oldbit)
: "m" (*(unsigned long *)addr), "Ir" (nr) : "memory");
return oldbit;
}
#else
#define asm_test_bit test_bit
#endif
static int do_for_each_set_bit(unsigned int num_bits)
{
unsigned long *to_test = bitmap_alloc(num_bits);
struct timeval start, end, diff;
u64 runtime_us;
struct stats fb_time_stats, tb_time_stats;
double time_average, time_stddev;
unsigned int bit, i, j;
unsigned int set_bits, skip;
unsigned int old;
init_stats(&fb_time_stats);
init_stats(&tb_time_stats);
for (set_bits = 1; set_bits <= num_bits; set_bits <<= 1) {
bitmap_zero(to_test, num_bits);
skip = num_bits / set_bits;
for (i = 0; i < num_bits; i += skip)
set_bit(i, to_test);
for (i = 0; i < outer_iterations; i++) {
old = accumulator;
gettimeofday(&start, NULL);
for (j = 0; j < inner_iterations; j++) {
for_each_set_bit(bit, to_test, num_bits)
workload(bit);
}
gettimeofday(&end, NULL);
assert(old + (inner_iterations * set_bits) == accumulator);
timersub(&end, &start, &diff);
runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
update_stats(&fb_time_stats, runtime_us);
old = accumulator;
gettimeofday(&start, NULL);
for (j = 0; j < inner_iterations; j++) {
for (bit = 0; bit < num_bits; bit++) {
if (asm_test_bit(bit, to_test))
workload(bit);
}
}
gettimeofday(&end, NULL);
assert(old + (inner_iterations * set_bits) == accumulator);
timersub(&end, &start, &diff);
runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
update_stats(&tb_time_stats, runtime_us);
}
printf("%d operations %d bits set of %d bits\n",
inner_iterations, set_bits, num_bits);
time_average = avg_stats(&fb_time_stats);
time_stddev = stddev_stats(&fb_time_stats);
printf(" Average for_each_set_bit took: %.3f usec (+- %.3f usec)\n",
time_average, time_stddev);
time_average = avg_stats(&tb_time_stats);
time_stddev = stddev_stats(&tb_time_stats);
printf(" Average test_bit loop took: %.3f usec (+- %.3f usec)\n",
time_average, time_stddev);
if (use_of_val == accumulator) /* Try to avoid compiler tricks. */
printf("\n");
}
bitmap_free(to_test);
return 0;
}
int bench_mem_find_bit(int argc, const char **argv)
{
int err = 0, i;
argc = parse_options(argc, argv, options, bench_usage, 0);
if (argc) {
usage_with_options(bench_usage, options);
exit(EXIT_FAILURE);
}
for (i = 1; i <= 2048; i <<= 1)
do_for_each_set_bit(i);
return err;
}
/*
*
* syscall.c
*
* syscall: Benchmark for system call performance
*/
#include "../perf.h"
#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../builtin.h"
#include "bench.h"
#include <stdio.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define LOOPS_DEFAULT 10000000
static int loops = LOOPS_DEFAULT;
static const struct option options[] = {
OPT_INTEGER('l', "loop", &loops, "Specify number of loops"),
OPT_END()
};
static const char * const bench_syscall_usage[] = {
"perf bench syscall <options>",
NULL
};
int bench_syscall_basic(int argc, const char **argv)
{
struct timeval start, stop, diff;
unsigned long long result_usec = 0;
int i;
argc = parse_options(argc, argv, options, bench_syscall_usage, 0);
gettimeofday(&start, NULL);
for (i = 0; i < loops; i++)
getppid();
gettimeofday(&stop, NULL);
timersub(&stop, &start, &diff);
switch (bench_format) {
case BENCH_FORMAT_DEFAULT:
printf("# Executed %'d getppid() calls\n", loops);
result_usec = diff.tv_sec * 1000000;
result_usec += diff.tv_usec;
printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
diff.tv_sec,
(unsigned long) (diff.tv_usec/1000));
printf(" %14lf usecs/op\n",
(double)result_usec / (double)loops);
printf(" %'14d ops/sec\n",
(int)((double)loops /
((double)result_usec / (double)1000000)));
break;
case BENCH_FORMAT_SIMPLE:
printf("%lu.%03lu\n",
diff.tv_sec,
(unsigned long) (diff.tv_usec / 1000));
break;
default:
/* reaching here is something disaster */
fprintf(stderr, "Unknown format:%d\n", bench_format);
exit(1);
break;
}
return 0;
}
......@@ -11,6 +11,7 @@
* Available benchmark collection list:
*
* sched ... scheduler and IPC performance
* syscall ... System call performance
* mem ... memory access performance
* numa ... NUMA scheduling and MM performance
* futex ... Futex performance
......@@ -49,9 +50,16 @@ static struct bench sched_benchmarks[] = {
{ NULL, NULL, NULL }
};
static struct bench syscall_benchmarks[] = {
{ "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic },
{ "all", "Run all syscall benchmarks", NULL },
{ NULL, NULL, NULL },
};
static struct bench mem_benchmarks[] = {
{ "memcpy", "Benchmark for memcpy() functions", bench_mem_memcpy },
{ "memset", "Benchmark for memset() functions", bench_mem_memset },
{ "find_bit", "Benchmark for find_bit() functions", bench_mem_find_bit },
{ "all", "Run all memory access benchmarks", NULL },
{ NULL, NULL, NULL }
};
......@@ -90,6 +98,7 @@ struct collection {
static struct collection collections[] = {
{ "sched", "Scheduler and IPC benchmarks", sched_benchmarks },
{ "syscall", "System call benchmarks", syscall_benchmarks },
{ "mem", "Memory access benchmarks", mem_benchmarks },
#ifdef HAVE_LIBNUMA_SUPPORT
{ "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks },
......
......@@ -2582,7 +2582,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
static int setup_callchain(struct evlist *evlist)
{
u64 sample_type = perf_evlist__combined_sample_type(evlist);
u64 sample_type = evlist__combined_sample_type(evlist);
enum perf_call_graph_mode mode = CALLCHAIN_NONE;
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
......
......@@ -65,6 +65,7 @@ static int cmd_data_convert(int argc, const char **argv)
OPT_STRING('i', "input", &input_name, "file", "input file name"),
#ifdef HAVE_LIBBABELTRACE_SUPPORT
OPT_STRING(0, "to-ctf", &to_ctf, NULL, "Convert to CTF format"),
OPT_BOOLEAN(0, "tod", &opts.tod, "Convert time to wall clock time"),
#endif
OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"),
OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"),
......
......@@ -292,7 +292,7 @@ static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
* if jit marker, then inject jit mmaps and generate ELF images
*/
ret = jit_process(inject->session, &inject->output, machine,
event->mmap.filename, sample->pid, &n);
event->mmap.filename, event->mmap.pid, &n);
if (ret < 0)
return ret;
if (ret) {
......@@ -330,7 +330,7 @@ static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
* if jit marker, then inject jit mmaps and generate ELF images
*/
ret = jit_process(inject->session, &inject->output, machine,
event->mmap2.filename, sample->pid, &n);
event->mmap2.filename, event->mmap2.pid, &n);
if (ret < 0)
return ret;
if (ret) {
......
......@@ -1933,7 +1933,8 @@ int cmd_kmem(int argc, const char **argv)
return ret;
argc = parse_options_subcommand(argc, argv, kmem_options,
kmem_subcommands, kmem_usage, 0);
kmem_subcommands, kmem_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc)
usage_with_options(kmem_usage, kmem_options);
......
......@@ -1319,7 +1319,7 @@ static struct evlist *kvm_live_event_list(void)
*name = '\0';
name++;
if (perf_evlist__add_newtp(evlist, sys, name, NULL)) {
if (evlist__add_newtp(evlist, sys, name, NULL)) {
pr_err("Failed to add %s tracepoint to the list\n", *events_tp);
free(tp);
goto out;
......
......@@ -46,6 +46,7 @@
#include "util/bpf-event.h"
#include "util/util.h"
#include "util/pfm.h"
#include "util/clockid.h"
#include "asm/bug.h"
#include "perf.h"
......@@ -70,6 +71,7 @@
#include <linux/time64.h>
#include <linux/zalloc.h>
#include <linux/bitmap.h>
#include <sys/time.h>
struct switch_output {
bool enabled;
......@@ -765,6 +767,43 @@ static int record__auxtrace_init(struct record *rec __maybe_unused)
#endif
static int record__config_text_poke(struct evlist *evlist)
{
struct evsel *evsel;
int err;
/* Nothing to do if text poke is already configured */
evlist__for_each_entry(evlist, evsel) {
if (evsel->core.attr.text_poke)
return 0;
}
err = parse_events(evlist, "dummy:u", NULL);
if (err)
return err;
evsel = evlist__last(evlist);
evsel->core.attr.freq = 0;
evsel->core.attr.sample_period = 1;
evsel->core.attr.text_poke = 1;
evsel->core.attr.ksymbol = 1;
evsel->core.system_wide = true;
evsel->no_aux_samples = true;
evsel->immediate = true;
/* Text poke must be collected on all CPUs */
perf_cpu_map__put(evsel->core.own_cpus);
evsel->core.own_cpus = perf_cpu_map__new(NULL);
perf_cpu_map__put(evsel->core.cpus);
evsel->core.cpus = perf_cpu_map__get(evsel->core.own_cpus);
evsel__set_sample_bit(evsel, TIME);
return 0;
}
static bool record__kcore_readable(struct machine *machine)
{
char kcore[PATH_MAX];
......@@ -855,7 +894,7 @@ static int record__open(struct record *rec)
pos = perf_evlist__get_tracking_event(evlist);
if (!evsel__is_dummy_event(pos)) {
/* Set up dummy event. */
if (perf_evlist__add_dummy(evlist))
if (evlist__add_dummy(evlist))
return -ENOMEM;
pos = evlist__last(evlist);
perf_evlist__set_tracking_event(evlist, pos);
......@@ -1166,6 +1205,9 @@ static void record__init_features(struct record *rec)
if (!(rec->opts.use_clockid && rec->opts.clockid_res_ns))
perf_header__clear_feat(&session->header, HEADER_CLOCKID);
if (!rec->opts.use_clockid)
perf_header__clear_feat(&session->header, HEADER_CLOCK_DATA);
perf_header__clear_feat(&session->header, HEADER_DIR_FORMAT);
if (!record__comp_enabled(rec))
perf_header__clear_feat(&session->header, HEADER_COMPRESSED);
......@@ -1489,7 +1531,7 @@ static int record__setup_sb_evlist(struct record *rec)
evlist__set_cb(rec->sb_evlist, record__process_signal_event, rec);
rec->thread_id = pthread_self();
}
#ifdef HAVE_LIBBPF_SUPPORT
if (!opts->no_bpf_event) {
if (rec->sb_evlist == NULL) {
rec->sb_evlist = evlist__new();
......@@ -1505,7 +1547,7 @@ static int record__setup_sb_evlist(struct record *rec)
return -1;
}
}
#endif
if (perf_evlist__start_sb_thread(rec->sb_evlist, &rec->opts.target)) {
pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
opts->no_bpf_event = true;
......@@ -1514,6 +1556,43 @@ static int record__setup_sb_evlist(struct record *rec)
return 0;
}
static int record__init_clock(struct record *rec)
{
struct perf_session *session = rec->session;
struct timespec ref_clockid;
struct timeval ref_tod;
u64 ref;
if (!rec->opts.use_clockid)
return 0;
if (rec->opts.use_clockid && rec->opts.clockid_res_ns)
session->header.env.clock.clockid_res_ns = rec->opts.clockid_res_ns;
session->header.env.clock.clockid = rec->opts.clockid;
if (gettimeofday(&ref_tod, NULL) != 0) {
pr_err("gettimeofday failed, cannot set reference time.\n");
return -1;
}
if (clock_gettime(rec->opts.clockid, &ref_clockid)) {
pr_err("clock_gettime failed, cannot set reference time.\n");
return -1;
}
ref = (u64) ref_tod.tv_sec * NSEC_PER_SEC +
(u64) ref_tod.tv_usec * NSEC_PER_USEC;
session->header.env.clock.tod_ns = ref;
ref = (u64) ref_clockid.tv_sec * NSEC_PER_SEC +
(u64) ref_clockid.tv_nsec;
session->header.env.clock.clockid_ns = ref;
return 0;
}
static int __cmd_record(struct record *rec, int argc, const char **argv)
{
int err;
......@@ -1527,6 +1606,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
bool disabled = false, draining = false;
int fd;
float ratio = 0;
enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED;
atexit(record__sig_exit);
signal(SIGCHLD, sig_handler);
......@@ -1593,10 +1673,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
return -1;
}
record__init_features(rec);
if (record__init_clock(rec))
return -1;
if (rec->opts.use_clockid && rec->opts.clockid_res_ns)
session->header.env.clockid_res_ns = rec->opts.clockid_res_ns;
record__init_features(rec);
if (forks) {
err = perf_evlist__prepare_workload(rec->evlist, &opts->target,
......@@ -1646,7 +1726,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
* Normally perf_session__new would do this, but it doesn't have the
* evlist.
*/
if (rec->tool.ordered_events && !perf_evlist__sample_id_all(rec->evlist)) {
if (rec->tool.ordered_events && !evlist__sample_id_all(rec->evlist)) {
pr_warning("WARNING: No sample_id_all support, falling back to unordered processing\n");
rec->tool.ordered_events = false;
}
......@@ -1748,9 +1828,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
perf_evlist__start_workload(rec->evlist);
}
if (evlist__initialize_ctlfd(rec->evlist, opts->ctl_fd, opts->ctl_fd_ack))
goto out_child;
if (opts->initial_delay) {
usleep(opts->initial_delay * USEC_PER_MSEC);
evlist__enable(rec->evlist);
pr_info(EVLIST_DISABLED_MSG);
if (opts->initial_delay > 0) {
usleep(opts->initial_delay * USEC_PER_MSEC);
evlist__enable(rec->evlist);
pr_info(EVLIST_ENABLED_MSG);
}
}
trigger_ready(&auxtrace_snapshot_trigger);
......@@ -1842,6 +1929,21 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
draining = true;
}
if (evlist__ctlfd_process(rec->evlist, &cmd) > 0) {
switch (cmd) {
case EVLIST_CTL_CMD_ENABLE:
pr_info(EVLIST_ENABLED_MSG);
break;
case EVLIST_CTL_CMD_DISABLE:
pr_info(EVLIST_DISABLED_MSG);
break;
case EVLIST_CTL_CMD_ACK:
case EVLIST_CTL_CMD_UNSUPPORTED:
default:
break;
}
}
/*
* When perf is starting the traced process, at the end events
* die with the process and we wait for that. Thus no need to
......@@ -1875,6 +1977,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
record__synthesize_workload(rec, true);
out_child:
evlist__finalize_ctlfd(rec->evlist);
record__mmap_read_all(rec, true);
record__aio_mmap_read_sync(rec);
......@@ -2041,103 +2144,6 @@ static int perf_record_config(const char *var, const char *value, void *cb)
return 0;
}
struct clockid_map {
const char *name;
int clockid;
};
#define CLOCKID_MAP(n, c) \
{ .name = n, .clockid = (c), }
#define CLOCKID_END { .name = NULL, }
/*
* Add the missing ones, we need to build on many distros...
*/
#ifndef CLOCK_MONOTONIC_RAW
#define CLOCK_MONOTONIC_RAW 4
#endif
#ifndef CLOCK_BOOTTIME
#define CLOCK_BOOTTIME 7
#endif
#ifndef CLOCK_TAI
#define CLOCK_TAI 11
#endif
static const struct clockid_map clockids[] = {
/* available for all events, NMI safe */
CLOCKID_MAP("monotonic", CLOCK_MONOTONIC),
CLOCKID_MAP("monotonic_raw", CLOCK_MONOTONIC_RAW),
/* available for some events */
CLOCKID_MAP("realtime", CLOCK_REALTIME),
CLOCKID_MAP("boottime", CLOCK_BOOTTIME),
CLOCKID_MAP("tai", CLOCK_TAI),
/* available for the lazy */
CLOCKID_MAP("mono", CLOCK_MONOTONIC),
CLOCKID_MAP("raw", CLOCK_MONOTONIC_RAW),
CLOCKID_MAP("real", CLOCK_REALTIME),
CLOCKID_MAP("boot", CLOCK_BOOTTIME),
CLOCKID_END,
};
static int get_clockid_res(clockid_t clk_id, u64 *res_ns)
{
struct timespec res;
*res_ns = 0;
if (!clock_getres(clk_id, &res))
*res_ns = res.tv_nsec + res.tv_sec * NSEC_PER_SEC;
else
pr_warning("WARNING: Failed to determine specified clock resolution.\n");
return 0;
}
static int parse_clockid(const struct option *opt, const char *str, int unset)
{
struct record_opts *opts = (struct record_opts *)opt->value;
const struct clockid_map *cm;
const char *ostr = str;
if (unset) {
opts->use_clockid = 0;
return 0;
}
/* no arg passed */
if (!str)
return 0;
/* no setting it twice */
if (opts->use_clockid)
return -1;
opts->use_clockid = true;
/* if its a number, we're done */
if (sscanf(str, "%d", &opts->clockid) == 1)
return get_clockid_res(opts->clockid, &opts->clockid_res_ns);
/* allow a "CLOCK_" prefix to the name */
if (!strncasecmp(str, "CLOCK_", 6))
str += 6;
for (cm = clockids; cm->name; cm++) {
if (!strcasecmp(str, cm->name)) {
opts->clockid = cm->clockid;
return get_clockid_res(opts->clockid,
&opts->clockid_res_ns);
}
}
opts->use_clockid = false;
ui__warning("unknown clockid %s, check man page\n", ostr);
return -1;
}
static int record__parse_affinity(const struct option *opt, const char *str, int unset)
{
......@@ -2224,6 +2230,33 @@ static int record__parse_mmap_pages(const struct option *opt,
return ret;
}
static int parse_control_option(const struct option *opt,
const char *str,
int unset __maybe_unused)
{
char *comma = NULL, *endptr = NULL;
struct record_opts *config = (struct record_opts *)opt->value;
if (strncmp(str, "fd:", 3))
return -EINVAL;
config->ctl_fd = strtoul(&str[3], &endptr, 0);
if (endptr == &str[3])
return -EINVAL;
comma = strchr(str, ',');
if (comma) {
if (endptr != comma)
return -EINVAL;
config->ctl_fd_ack = strtoul(comma + 1, &endptr, 0);
if (endptr == comma + 1 || *endptr != '\0')
return -EINVAL;
}
return 0;
}
static void switch_output_size_warn(struct record *rec)
{
u64 wakeup_size = evlist__mmap_size(rec->opts.mmap_pages);
......@@ -2360,6 +2393,8 @@ static struct record record = {
},
.mmap_flush = MMAP_FLUSH_DEFAULT,
.nr_threads_synthesize = 1,
.ctl_fd = -1,
.ctl_fd_ack = -1,
},
.tool = {
.sample = process_sample_event,
......@@ -2462,8 +2497,8 @@ static struct option __record_options[] = {
OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
"monitor event in cgroup name only",
parse_cgroups),
OPT_UINTEGER('D', "delay", &record.opts.initial_delay,
"ms to wait before starting measurement after program start"),
OPT_INTEGER('D', "delay", &record.opts.initial_delay,
"ms to wait before starting measurement after program start (-1: start with events disabled)"),
OPT_BOOLEAN(0, "kcore", &record.opts.kcore, "copy /proc/kcore"),
OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
"user to profile"),
......@@ -2561,6 +2596,10 @@ static struct option __record_options[] = {
"libpfm4 event selector. use 'perf list' to list available events",
parse_libpfm_events_option),
#endif
OPT_CALLBACK(0, "control", &record.opts, "fd:ctl-fd[,ack-fd]",
"Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n"
"\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.",
parse_control_option),
OPT_END()
};
......@@ -2722,7 +2761,7 @@ int cmd_record(int argc, const char **argv)
record.opts.tail_synthesize = true;
if (rec->evlist->core.nr_entries == 0 &&
__perf_evlist__add_default(rec->evlist, !record.opts.no_samples) < 0) {
__evlist__add_default(rec->evlist, !record.opts.no_samples) < 0) {
pr_err("Not enough memory for event selector list\n");
goto out;
}
......@@ -2766,6 +2805,14 @@ int cmd_record(int argc, const char **argv)
if (rec->opts.full_auxtrace)
rec->buildid_all = true;
if (rec->opts.text_poke) {
err = record__config_text_poke(rec->evlist);
if (err) {
pr_err("record__config_text_poke failed, error %d\n", err);
goto out;
}
}
if (record_opts__config(&rec->opts)) {
err = -EINVAL;
goto out;
......
......@@ -338,7 +338,7 @@ static int process_read_event(struct perf_tool *tool,
static int report__setup_sample_type(struct report *rep)
{
struct perf_session *session = rep->session;
u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
u64 sample_type = evlist__combined_sample_type(session->evlist);
bool is_pipe = perf_data__is_pipe(session->data);
if (session->itrace_synth_opts->callchain ||
......@@ -410,8 +410,7 @@ static int report__setup_sample_type(struct report *rep)
}
/* ??? handle more cases than just ANY? */
if (!(perf_evlist__combined_branch_type(session->evlist) &
PERF_SAMPLE_BRANCH_ANY))
if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY))
rep->nonany_branch_mode = true;
#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT)
......@@ -1093,7 +1092,7 @@ static int process_attr(struct perf_tool *tool __maybe_unused,
* Check if we need to enable callchains based
* on events sample_type.
*/
sample_type = perf_evlist__combined_sample_type(*pevlist);
sample_type = evlist__combined_sample_type(*pevlist);
callchain_param_setup(sample_type);
return 0;
}
......@@ -1389,7 +1388,7 @@ int cmd_report(int argc, const char **argv)
has_br_stack = perf_header__has_feat(&session->header,
HEADER_BRANCH_STACK);
if (perf_evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER)
if (evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER)
has_br_stack = false;
setup_forced_leader(&report, session->evlist);
......
......@@ -82,38 +82,64 @@ static bool native_arch;
unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
enum perf_output_field {
PERF_OUTPUT_COMM = 1U << 0,
PERF_OUTPUT_TID = 1U << 1,
PERF_OUTPUT_PID = 1U << 2,
PERF_OUTPUT_TIME = 1U << 3,
PERF_OUTPUT_CPU = 1U << 4,
PERF_OUTPUT_EVNAME = 1U << 5,
PERF_OUTPUT_TRACE = 1U << 6,
PERF_OUTPUT_IP = 1U << 7,
PERF_OUTPUT_SYM = 1U << 8,
PERF_OUTPUT_DSO = 1U << 9,
PERF_OUTPUT_ADDR = 1U << 10,
PERF_OUTPUT_SYMOFFSET = 1U << 11,
PERF_OUTPUT_SRCLINE = 1U << 12,
PERF_OUTPUT_PERIOD = 1U << 13,
PERF_OUTPUT_IREGS = 1U << 14,
PERF_OUTPUT_BRSTACK = 1U << 15,
PERF_OUTPUT_BRSTACKSYM = 1U << 16,
PERF_OUTPUT_DATA_SRC = 1U << 17,
PERF_OUTPUT_WEIGHT = 1U << 18,
PERF_OUTPUT_BPF_OUTPUT = 1U << 19,
PERF_OUTPUT_CALLINDENT = 1U << 20,
PERF_OUTPUT_INSN = 1U << 21,
PERF_OUTPUT_INSNLEN = 1U << 22,
PERF_OUTPUT_BRSTACKINSN = 1U << 23,
PERF_OUTPUT_BRSTACKOFF = 1U << 24,
PERF_OUTPUT_SYNTH = 1U << 25,
PERF_OUTPUT_PHYS_ADDR = 1U << 26,
PERF_OUTPUT_UREGS = 1U << 27,
PERF_OUTPUT_METRIC = 1U << 28,
PERF_OUTPUT_MISC = 1U << 29,
PERF_OUTPUT_SRCCODE = 1U << 30,
PERF_OUTPUT_IPC = 1U << 31,
PERF_OUTPUT_COMM = 1ULL << 0,
PERF_OUTPUT_TID = 1ULL << 1,
PERF_OUTPUT_PID = 1ULL << 2,
PERF_OUTPUT_TIME = 1ULL << 3,
PERF_OUTPUT_CPU = 1ULL << 4,
PERF_OUTPUT_EVNAME = 1ULL << 5,
PERF_OUTPUT_TRACE = 1ULL << 6,
PERF_OUTPUT_IP = 1ULL << 7,
PERF_OUTPUT_SYM = 1ULL << 8,
PERF_OUTPUT_DSO = 1ULL << 9,
PERF_OUTPUT_ADDR = 1ULL << 10,
PERF_OUTPUT_SYMOFFSET = 1ULL << 11,
PERF_OUTPUT_SRCLINE = 1ULL << 12,
PERF_OUTPUT_PERIOD = 1ULL << 13,
PERF_OUTPUT_IREGS = 1ULL << 14,
PERF_OUTPUT_BRSTACK = 1ULL << 15,
PERF_OUTPUT_BRSTACKSYM = 1ULL << 16,
PERF_OUTPUT_DATA_SRC = 1ULL << 17,
PERF_OUTPUT_WEIGHT = 1ULL << 18,
PERF_OUTPUT_BPF_OUTPUT = 1ULL << 19,
PERF_OUTPUT_CALLINDENT = 1ULL << 20,
PERF_OUTPUT_INSN = 1ULL << 21,
PERF_OUTPUT_INSNLEN = 1ULL << 22,
PERF_OUTPUT_BRSTACKINSN = 1ULL << 23,
PERF_OUTPUT_BRSTACKOFF = 1ULL << 24,
PERF_OUTPUT_SYNTH = 1ULL << 25,
PERF_OUTPUT_PHYS_ADDR = 1ULL << 26,
PERF_OUTPUT_UREGS = 1ULL << 27,
PERF_OUTPUT_METRIC = 1ULL << 28,
PERF_OUTPUT_MISC = 1ULL << 29,
PERF_OUTPUT_SRCCODE = 1ULL << 30,
PERF_OUTPUT_IPC = 1ULL << 31,
PERF_OUTPUT_TOD = 1ULL << 32,
};
struct perf_script {
struct perf_tool tool;
struct perf_session *session;
bool show_task_events;
bool show_mmap_events;
bool show_switch_events;
bool show_namespace_events;
bool show_lost_events;
bool show_round_events;
bool show_bpf_events;
bool show_cgroup_events;
bool show_text_poke_events;
bool allocated;
bool per_event_dump;
bool stitch_lbr;
struct evswitch evswitch;
struct perf_cpu_map *cpus;
struct perf_thread_map *threads;
int name_width;
const char *time_str;
struct perf_time_interval *ptime_range;
int range_size;
int range_num;
};
struct output_option {
......@@ -152,6 +178,7 @@ struct output_option {
{.str = "misc", .field = PERF_OUTPUT_MISC},
{.str = "srccode", .field = PERF_OUTPUT_SRCCODE},
{.str = "ipc", .field = PERF_OUTPUT_IPC},
{.str = "tod", .field = PERF_OUTPUT_TOD},
};
enum {
......@@ -388,7 +415,7 @@ static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char *
return evsel__do_check_stype(evsel, sample_type, sample_msg, field, false);
}
static int perf_evsel__check_attr(struct evsel *evsel, struct perf_session *session)
static int evsel__check_attr(struct evsel *evsel, struct perf_session *session)
{
struct perf_event_attr *attr = &evsel->core.attr;
bool allow_user_set;
......@@ -443,8 +470,7 @@ static int perf_evsel__check_attr(struct evsel *evsel, struct perf_session *sess
return -EINVAL;
}
if (PRINT_FIELD(BRSTACKINSN) && !allow_user_set &&
!(perf_evlist__combined_branch_type(session->evlist) &
PERF_SAMPLE_BRANCH_ANY)) {
!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) {
pr_err("Display of branch stack assembler requested, but non all-branch filter set\n"
"Hint: run 'perf record -b ...'\n");
return -EINVAL;
......@@ -503,6 +529,7 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
*/
static int perf_session__check_output_opt(struct perf_session *session)
{
bool tod = false;
unsigned int j;
struct evsel *evsel;
......@@ -522,13 +549,14 @@ static int perf_session__check_output_opt(struct perf_session *session)
}
if (evsel && output[j].fields &&
perf_evsel__check_attr(evsel, session))
evsel__check_attr(evsel, session))
return -1;
if (evsel == NULL)
continue;
set_print_ip_opts(&evsel->core.attr);
tod |= output[j].fields & PERF_OUTPUT_TOD;
}
if (!no_callchain) {
......@@ -569,13 +597,17 @@ static int perf_session__check_output_opt(struct perf_session *session)
}
}
if (tod && !session->header.env.clock.enabled) {
pr_err("Can't provide 'tod' time, missing clock data. "
"Please record with -k/--clockid option.\n");
return -1;
}
out:
return 0;
}
static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
FILE *fp
)
FILE *fp)
{
unsigned i = 0, r;
int printed = 0;
......@@ -593,6 +625,56 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
return printed;
}
#define DEFAULT_TOD_FMT "%F %H:%M:%S"
static char*
tod_scnprintf(struct perf_script *script, char *buf, int buflen,
u64 timestamp)
{
u64 tod_ns, clockid_ns;
struct perf_env *env;
unsigned long nsec;
struct tm ltime;
char date[64];
time_t sec;
buf[0] = '\0';
if (buflen < 64 || !script)
return buf;
env = &script->session->header.env;
if (!env->clock.enabled) {
scnprintf(buf, buflen, "disabled");
return buf;
}
clockid_ns = env->clock.clockid_ns;
tod_ns = env->clock.tod_ns;
if (timestamp > clockid_ns)
tod_ns += timestamp - clockid_ns;
else
tod_ns -= clockid_ns - timestamp;
sec = (time_t) (tod_ns / NSEC_PER_SEC);
nsec = tod_ns - sec * NSEC_PER_SEC;
if (localtime_r(&sec, &ltime) == NULL) {
scnprintf(buf, buflen, "failed");
} else {
strftime(date, sizeof(date), DEFAULT_TOD_FMT, &ltime);
if (symbol_conf.nanosecs) {
snprintf(buf, buflen, "%s.%09lu", date, nsec);
} else {
snprintf(buf, buflen, "%s.%06lu",
date, nsec / NSEC_PER_USEC);
}
}
return buf;
}
static int perf_sample__fprintf_iregs(struct perf_sample *sample,
struct perf_event_attr *attr, FILE *fp)
{
......@@ -607,7 +689,8 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample,
attr->sample_regs_user, fp);
}
static int perf_sample__fprintf_start(struct perf_sample *sample,
static int perf_sample__fprintf_start(struct perf_script *script,
struct perf_sample *sample,
struct thread *thread,
struct evsel *evsel,
u32 type, FILE *fp)
......@@ -616,6 +699,7 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
unsigned long secs;
unsigned long long nsecs;
int printed = 0;
char tstr[128];
if (PRINT_FIELD(COMM)) {
if (latency_format)
......@@ -684,6 +768,11 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
printed += ret;
}
if (PRINT_FIELD(TOD)) {
tod_scnprintf(script, tstr, sizeof(tstr), sample->time);
printed += fprintf(fp, "%s ", tstr);
}
if (PRINT_FIELD(TIME)) {
u64 t = sample->time;
if (reltime) {
......@@ -1668,31 +1757,7 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample,
return 0;
}
struct perf_script {
struct perf_tool tool;
struct perf_session *session;
bool show_task_events;
bool show_mmap_events;
bool show_switch_events;
bool show_namespace_events;
bool show_lost_events;
bool show_round_events;
bool show_bpf_events;
bool show_cgroup_events;
bool allocated;
bool per_event_dump;
bool stitch_lbr;
struct evswitch evswitch;
struct perf_cpu_map *cpus;
struct perf_thread_map *threads;
int name_width;
const char *time_str;
struct perf_time_interval *ptime_range;
int range_size;
int range_num;
};
static int perf_evlist__max_name_len(struct evlist *evlist)
static int evlist__max_name_len(struct evlist *evlist)
{
struct evsel *evsel;
int max = 0;
......@@ -1739,7 +1804,7 @@ static void script_print_metric(struct perf_stat_config *config __maybe_unused,
if (!fmt)
return;
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel,
PERF_RECORD_SAMPLE, mctx->fp);
fputs("\tmetric: ", mctx->fp);
if (color)
......@@ -1754,7 +1819,7 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused,
{
struct metric_ctx *mctx = ctx;
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel,
PERF_RECORD_SAMPLE, mctx->fp);
fputs("\tmetric: ", mctx->fp);
}
......@@ -1865,7 +1930,7 @@ static void process_event(struct perf_script *script,
++es->samples;
perf_sample__fprintf_start(sample, thread, evsel,
perf_sample__fprintf_start(script, sample, thread, evsel,
PERF_RECORD_SAMPLE, fp);
if (PRINT_FIELD(PERIOD))
......@@ -1875,7 +1940,7 @@ static void process_event(struct perf_script *script,
const char *evname = evsel__name(evsel);
if (!script->name_width)
script->name_width = perf_evlist__max_name_len(script->session->evlist);
script->name_width = evlist__max_name_len(script->session->evlist);
fprintf(fp, "%*s: ", script->name_width, evname ?: "[unknown]");
}
......@@ -2120,7 +2185,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
}
if (evsel->core.attr.sample_type) {
err = perf_evsel__check_attr(evsel, scr->session);
err = evsel__check_attr(evsel, scr->session);
if (err)
return err;
}
......@@ -2129,7 +2194,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
* Check if we need to enable callchains based
* on events sample_type.
*/
sample_type = perf_evlist__combined_sample_type(evlist);
sample_type = evlist__combined_sample_type(evlist);
callchain_param_setup(sample_type);
/* Enable fields for callchain entries */
......@@ -2174,11 +2239,11 @@ static int print_event_with_time(struct perf_tool *tool,
thread = machine__findnew_thread(machine, pid, tid);
if (thread && evsel) {
perf_sample__fprintf_start(sample, thread, evsel,
perf_sample__fprintf_start(script, sample, thread, evsel,
event->header.type, stdout);
}
perf_event__fprintf(event, stdout);
perf_event__fprintf(event, machine, stdout);
thread__put(thread);
......@@ -2313,7 +2378,7 @@ process_finished_round_event(struct perf_tool *tool __maybe_unused,
struct ordered_events *oe __maybe_unused)
{
perf_event__fprintf(event, stdout);
perf_event__fprintf(event, NULL, stdout);
return 0;
}
......@@ -2330,6 +2395,18 @@ process_bpf_events(struct perf_tool *tool __maybe_unused,
sample->tid);
}
static int process_text_poke_events(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine)
{
if (perf_event__process_text_poke(tool, event, sample, machine) < 0)
return -1;
return print_event(tool, event, sample, machine, sample->pid,
sample->tid);
}
static void sig_handler(int sig __maybe_unused)
{
session_done = 1;
......@@ -2438,6 +2515,10 @@ static int __cmd_script(struct perf_script *script)
script->tool.ksymbol = process_bpf_events;
script->tool.bpf = process_bpf_events;
}
if (script->show_text_poke_events) {
script->tool.ksymbol = process_bpf_events;
script->tool.text_poke = process_text_poke_events;
}
if (perf_script__setup_per_event_dump(script)) {
pr_err("Couldn't create the per event dump files\n");
......@@ -3171,7 +3252,7 @@ static int have_cmd(int argc, const char **argv)
static void script__setup_sample_type(struct perf_script *script)
{
struct perf_session *session = script->session;
u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
u64 sample_type = evlist__combined_sample_type(session->evlist);
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
......@@ -3423,7 +3504,7 @@ int cmd_script(int argc, const char **argv)
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
"addr,symoff,srcline,period,iregs,uregs,brstack,"
"brstacksym,flags,bpf-output,brstackinsn,brstackoff,"
"callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc",
"callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc,tod",
parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
......@@ -3474,6 +3555,8 @@ int cmd_script(int argc, const char **argv)
"Show round events (if recorded)"),
OPT_BOOLEAN('\0', "show-bpf-events", &script.show_bpf_events,
"Show bpf related events (if recorded)"),
OPT_BOOLEAN('\0', "show-text-poke-events", &script.show_text_poke_events,
"Show text poke related events (if recorded)"),
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
"Dump trace output to files named by the monitored events"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
......
......@@ -188,6 +188,8 @@ static struct perf_stat_config stat_config = {
.metric_only_len = METRIC_ONLY_LEN,
.walltime_nsecs_stats = &walltime_nsecs_stats,
.big_num = true,
.ctl_fd = -1,
.ctl_fd_ack = -1
};
static bool cpus_map_matched(struct evsel *a, struct evsel *b)
......@@ -475,18 +477,38 @@ static void process_interval(void)
print_counters(&rs, 0, NULL);
}
static bool handle_interval(unsigned int interval, int *times)
{
if (interval) {
process_interval();
if (interval_count && !(--(*times)))
return true;
}
return false;
}
static void enable_counters(void)
{
if (stat_config.initial_delay)
if (stat_config.initial_delay < 0) {
pr_info(EVLIST_DISABLED_MSG);
return;
}
if (stat_config.initial_delay > 0) {
pr_info(EVLIST_DISABLED_MSG);
usleep(stat_config.initial_delay * USEC_PER_MSEC);
}
/*
* We need to enable counters only if:
* - we don't have tracee (attaching to task or cpu)
* - we have initial delay configured
*/
if (!target__none(&target) || stat_config.initial_delay)
if (!target__none(&target) || stat_config.initial_delay) {
evlist__enable(evsel_list);
if (stat_config.initial_delay > 0)
pr_info(EVLIST_ENABLED_MSG);
}
}
static void disable_counters(void)
......@@ -540,6 +562,86 @@ static bool is_target_alive(struct target *_target,
return false;
}
static void process_evlist(struct evlist *evlist, unsigned int interval)
{
enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED;
if (evlist__ctlfd_process(evlist, &cmd) > 0) {
switch (cmd) {
case EVLIST_CTL_CMD_ENABLE:
pr_info(EVLIST_ENABLED_MSG);
if (interval)
process_interval();
break;
case EVLIST_CTL_CMD_DISABLE:
if (interval)
process_interval();
pr_info(EVLIST_DISABLED_MSG);
break;
case EVLIST_CTL_CMD_ACK:
case EVLIST_CTL_CMD_UNSUPPORTED:
default:
break;
}
}
}
static void compute_tts(struct timespec *time_start, struct timespec *time_stop,
int *time_to_sleep)
{
int tts = *time_to_sleep;
struct timespec time_diff;
diff_timespec(&time_diff, time_stop, time_start);
tts -= time_diff.tv_sec * MSEC_PER_SEC +
time_diff.tv_nsec / NSEC_PER_MSEC;
if (tts < 0)
tts = 0;
*time_to_sleep = tts;
}
static int dispatch_events(bool forks, int timeout, int interval, int *times)
{
int child_exited = 0, status = 0;
int time_to_sleep, sleep_time;
struct timespec time_start, time_stop;
if (interval)
sleep_time = interval;
else if (timeout)
sleep_time = timeout;
else
sleep_time = 1000;
time_to_sleep = sleep_time;
while (!done) {
if (forks)
child_exited = waitpid(child_pid, &status, WNOHANG);
else
child_exited = !is_target_alive(&target, evsel_list->core.threads) ? 1 : 0;
if (child_exited)
break;
clock_gettime(CLOCK_MONOTONIC, &time_start);
if (!(evlist__poll(evsel_list, time_to_sleep) > 0)) { /* poll timeout or EINTR */
if (timeout || handle_interval(interval, times))
break;
time_to_sleep = sleep_time;
} else { /* fd revent */
process_evlist(evsel_list, interval);
clock_gettime(CLOCK_MONOTONIC, &time_stop);
compute_tts(&time_start, &time_stop, &time_to_sleep);
}
}
return status;
}
enum counter_recovery {
COUNTER_SKIP,
COUNTER_RETRY,
......@@ -603,7 +705,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
char msg[BUFSIZ];
unsigned long long t0, t1;
struct evsel *counter;
struct timespec ts;
size_t l;
int status = 0;
const bool forks = (argc > 0);
......@@ -612,17 +713,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
int i, cpu;
bool second_pass = false;
if (interval) {
ts.tv_sec = interval / USEC_PER_MSEC;
ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
} else if (timeout) {
ts.tv_sec = timeout / USEC_PER_MSEC;
ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC;
} else {
ts.tv_sec = 1;
ts.tv_nsec = 0;
}
if (forks) {
if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe,
workload_exec_failed_signal) < 0) {
......@@ -779,16 +869,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
perf_evlist__start_workload(evsel_list);
enable_counters();
if (interval || timeout) {
while (!waitpid(child_pid, &status, WNOHANG)) {
nanosleep(&ts, NULL);
if (timeout)
break;
process_interval();
if (interval_count && !(--times))
break;
}
}
if (interval || timeout || evlist__ctlfd_initialized(evsel_list))
status = dispatch_events(forks, timeout, interval, &times);
if (child_pid != -1) {
if (timeout)
kill(child_pid, SIGTERM);
......@@ -805,18 +887,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
psignal(WTERMSIG(status), argv[0]);
} else {
enable_counters();
while (!done) {
nanosleep(&ts, NULL);
if (!is_target_alive(&target, evsel_list->core.threads))
break;
if (timeout)
break;
if (interval) {
process_interval();
if (interval_count && !(--times))
break;
}
}
status = dispatch_events(forks, timeout, interval, &times);
}
disable_counters();
......@@ -970,6 +1041,33 @@ static int parse_metric_groups(const struct option *opt,
&stat_config.metric_events);
}
static int parse_control_option(const struct option *opt,
const char *str,
int unset __maybe_unused)
{
char *comma = NULL, *endptr = NULL;
struct perf_stat_config *config = (struct perf_stat_config *)opt->value;
if (strncmp(str, "fd:", 3))
return -EINVAL;
config->ctl_fd = strtoul(&str[3], &endptr, 0);
if (endptr == &str[3])
return -EINVAL;
comma = strchr(str, ',');
if (comma) {
if (endptr != comma)
return -EINVAL;
config->ctl_fd_ack = strtoul(comma + 1, &endptr, 0);
if (endptr == comma + 1 || *endptr != '\0')
return -EINVAL;
}
return 0;
}
static struct option stat_options[] = {
OPT_BOOLEAN('T', "transaction", &transaction_run,
"hardware transaction statistics"),
......@@ -1041,8 +1139,8 @@ static struct option stat_options[] = {
"aggregate counts per thread", AGGR_THREAD),
OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode,
"aggregate counts per numa node", AGGR_NODE),
OPT_UINTEGER('D', "delay", &stat_config.initial_delay,
"ms to wait before starting measurement after program start"),
OPT_INTEGER('D', "delay", &stat_config.initial_delay,
"ms to wait before starting measurement after program start (-1: start with events disabled)"),
OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL,
"Only print computed metrics. No raw values", enable_metric_only),
OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group,
......@@ -1071,6 +1169,10 @@ static struct option stat_options[] = {
"libpfm4 event selector. use 'perf list' to list available events",
parse_libpfm_events_option),
#endif
OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd]",
"Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n"
"\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.",
parse_control_option),
OPT_END()
};
......@@ -1679,19 +1781,17 @@ static int add_default_attributes(void)
if (target__has_cpu(&target))
default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK;
if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
return -1;
if (pmu_have_event("cpu", "stalled-cycles-frontend")) {
if (perf_evlist__add_default_attrs(evsel_list,
frontend_attrs) < 0)
if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0)
return -1;
}
if (pmu_have_event("cpu", "stalled-cycles-backend")) {
if (perf_evlist__add_default_attrs(evsel_list,
backend_attrs) < 0)
if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0)
return -1;
}
if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
if (evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
return -1;
}
......@@ -1701,21 +1801,21 @@ static int add_default_attributes(void)
return 0;
/* Append detailed run extra attributes: */
if (perf_evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
if (evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
return -1;
if (detailed_run < 2)
return 0;
/* Append very detailed run extra attributes: */
if (perf_evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
if (evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
return -1;
if (detailed_run < 3)
return 0;
/* Append very, very detailed run extra attributes: */
return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
return evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
}
static const char * const stat_record_usage[] = {
......@@ -2242,6 +2342,9 @@ int cmd_stat(int argc, const char **argv)
signal(SIGALRM, skip_signal);
signal(SIGABRT, skip_signal);
if (evlist__initialize_ctlfd(evsel_list, stat_config.ctl_fd, stat_config.ctl_fd_ack))
goto out;
status = 0;
for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) {
if (stat_config.run_count != 1 && verbose > 0)
......@@ -2261,6 +2364,8 @@ int cmd_stat(int argc, const char **argv)
if (!forever && status != -1 && (!interval || stat_config.summary))
print_counters(NULL, argc, argv);
evlist__finalize_ctlfd(evsel_list);
if (STAT_RECORD) {
/*
* We synthesize the kernel mmap record just so that older tools
......@@ -2307,6 +2412,7 @@ int cmd_stat(int argc, const char **argv)
evlist__delete(evsel_list);
metricgroup__rblist_exit(&stat_config.metric_events);
runtime_stat_delete(&stat_config);
return status;
......
......@@ -1627,7 +1627,7 @@ int cmd_top(int argc, const char **argv)
goto out_delete_evlist;
if (!top.evlist->core.nr_entries &&
perf_evlist__add_default(top.evlist) < 0) {
evlist__add_default(top.evlist) < 0) {
pr_err("Not enough memory for event selector list\n");
goto out_delete_evlist;
}
......
......@@ -3917,8 +3917,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
}
if (trace->sched &&
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
trace__sched_stat_runtime))
evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime))
goto out_error_sched_stat_runtime;
/*
* If a global cgroup was set, apply it to all the events without an
......@@ -4150,11 +4149,11 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out_error;
out_error_mmap:
perf_evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf));
evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf));
goto out_error;
out_error_open:
perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
out_error:
fprintf(trace->output, "%s\n", errbuf);
......@@ -4813,7 +4812,7 @@ int cmd_trace(int argc, const char **argv)
"per thread proc mmap processing timeout in ms"),
OPT_CALLBACK('G', "cgroup", &trace, "name", "monitor event in cgroup name only",
trace__parse_cgroups),
OPT_UINTEGER('D', "delay", &trace.opts.initial_delay,
OPT_INTEGER('D', "delay", &trace.opts.initial_delay,
"ms to wait before starting measurement after program "
"start"),
OPTS_EVSWITCH(&trace.evswitch),
......
......@@ -15,5 +15,40 @@
"MetricExpr": "(hv_24x7@PM_PB_CYC\\,chip\\=?@ )",
"MetricName": "PowerBUS_Frequency",
"ScaleUnit": "2.5e-7GHz"
},
{
"MetricExpr" : "nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT23@",
"MetricName" : "mcs01-read",
"MetricGroup" : "memory_bw",
"ScaleUnit": "6.1e-5MB"
},
{
"MetricExpr" : "nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT23@",
"MetricName" : "mcs23-read",
"MetricGroup" : "memory_bw",
"ScaleUnit": "6.1e-5MB"
},
{
"MetricExpr" : "nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT23@",
"MetricName" : "mcs01-write",
"MetricGroup" : "memory_bw",
"ScaleUnit": "6.1e-5MB"
},
{
"MetricExpr" : "nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT23@",
"MetricName" : "mcs23-write",
"MetricGroup" : "memory-bandwidth",
"ScaleUnit": "6.1e-5MB"
},
{
"MetricExpr" : "nest_powerbus0_imc@PM_PB_CYC@",
"MetricName" : "powerbus_freq",
"ScaleUnit": "1e-9GHz"
},
{
"MetricExpr" : "(nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT23@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT23@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT23@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT23@)",
"MetricName" : "Memory-bandwidth-MCS",
"MetricGroup" : "memory_bw",
"ScaleUnit": "6.1e-5MB"
}
]
......@@ -59,6 +59,7 @@ perf-y += genelf.o
perf-y += api-io.o
perf-y += demangle-java-test.o
perf-y += pfm.o
perf-y += parse-metric.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
......
......@@ -53,6 +53,7 @@ Following tests are defined (with perf commands):
perf record -i kill (test-record-no-inherit)
perf record -n kill (test-record-no-samples)
perf record -c 100 -P kill (test-record-period)
perf record -c 1 --pfm-events=cycles:period=2 (test-record-pfm-period)
perf record -R kill (test-record-raw)
perf stat -e cycles kill (test-stat-basic)
perf stat kill (test-stat-default)
......
[config]
command = record
args = --no-bpf-event -c 10000 --pfm-events=cycles:period=77777 kill >/dev/null 2>&1
ret = 1
[event:base-record]
sample_period=77777
sample_type=7
freq=0
......@@ -337,6 +337,10 @@ static struct test generic_tests[] = {
.desc = "Demangle Java",
.func = test__demangle_java,
},
{
.desc = "Parse and process metrics",
.func = test__parse_metric,
},
{
.func = NULL,
},
......
......@@ -678,7 +678,7 @@ static int do_test_code_reading(bool try_kcore)
if (verbose > 0) {
char errbuf[512];
perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
pr_debug("perf_evlist__open() failed!\n%s\n", errbuf);
}
......
......@@ -18,14 +18,15 @@ static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
{
struct expr_id_data *val_ptr;
const char *p;
double val, *val_ptr;
double val;
int ret;
struct expr_parse_ctx ctx;
expr__ctx_init(&ctx);
expr__add_id(&ctx, strdup("FOO"), 1);
expr__add_id(&ctx, strdup("BAR"), 2);
expr__add_id_val(&ctx, strdup("FOO"), 1);
expr__add_id_val(&ctx, strdup("BAR"), 2);
ret = test(&ctx, "1+1", 2);
ret |= test(&ctx, "FOO+BAR", 3);
......@@ -39,6 +40,14 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
ret |= test(&ctx, "1+1 if 3*4 else 0", 2);
ret |= test(&ctx, "1.1 + 2.1", 3.2);
ret |= test(&ctx, ".1 + 2.", 2.1);
ret |= test(&ctx, "d_ratio(1, 2)", 0.5);
ret |= test(&ctx, "d_ratio(2.5, 0)", 0);
ret |= test(&ctx, "1.1 < 2.2", 1);
ret |= test(&ctx, "2.2 > 1.1", 1);
ret |= test(&ctx, "1.1 < 1.1", 0);
ret |= test(&ctx, "2.2 > 2.2", 0);
ret |= test(&ctx, "2.2 < 1.1", 0);
ret |= test(&ctx, "1.1 > 2.2", 0);
if (ret)
return ret;
......
......@@ -12,6 +12,7 @@ static void fdarray__init_revents(struct fdarray *fda, short revents)
for (fd = 0; fd < fda->nr; ++fd) {
fda->entries[fd].fd = fda->nr - fd;
fda->entries[fd].events = revents;
fda->entries[fd].revents = revents;
}
}
......@@ -29,7 +30,7 @@ static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE
int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_unused)
{
int nr_fds, expected_fd[2], fd, err = TEST_FAIL;
int nr_fds, err = TEST_FAIL;
struct fdarray *fda = fdarray__new(5, 5);
if (fda == NULL) {
......@@ -55,7 +56,6 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
fdarray__init_revents(fda, POLLHUP);
fda->entries[2].revents = POLLIN;
expected_fd[0] = fda->entries[2].fd;
pr_debug("\nfiltering all but fda->entries[2]:");
fdarray__fprintf_prefix(fda, "before", stderr);
......@@ -66,17 +66,9 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
goto out_delete;
}
if (fda->entries[0].fd != expected_fd[0]) {
pr_debug("\nfda->entries[0].fd=%d != %d\n",
fda->entries[0].fd, expected_fd[0]);
goto out_delete;
}
fdarray__init_revents(fda, POLLHUP);
fda->entries[0].revents = POLLIN;
expected_fd[0] = fda->entries[0].fd;
fda->entries[3].revents = POLLIN;
expected_fd[1] = fda->entries[3].fd;
pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):");
fdarray__fprintf_prefix(fda, "before", stderr);
......@@ -88,14 +80,6 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
goto out_delete;
}
for (fd = 0; fd < 2; ++fd) {
if (fda->entries[fd].fd != expected_fd[fd]) {
pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd,
fda->entries[fd].fd, expected_fd[fd]);
goto out_delete;
}
}
pr_debug("\n");
err = 0;
......@@ -128,7 +112,7 @@ int test__fdarray__add(struct test *test __maybe_unused, int subtest __maybe_unu
}
#define FDA_ADD(_idx, _fd, _revents, _nr) \
if (fdarray__add(fda, _fd, _revents) < 0) { \
if (fdarray__add(fda, _fd, _revents, fdarray_flag__default) < 0) { \
pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \
__LINE__,_fd, _revents); \
goto out_delete; \
......
......@@ -631,6 +631,34 @@ static int test__checkterms_simple(struct list_head *terms)
TEST_ASSERT_VAL("wrong val", term->val.num == 1);
TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "umask"));
/*
* read
*
* The perf_pmu__test_parse_init injects 'read' term into
* perf_pmu_events_list, so 'read' is evaluated as read term
* and not as raw event with 'ead' hex value.
*/
term = list_entry(term->list.next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
TEST_ASSERT_VAL("wrong type val",
term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
TEST_ASSERT_VAL("wrong val", term->val.num == 1);
TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "read"));
/*
* r0xead
*
* To be still able to pass 'ead' value with 'r' syntax,
* we added support to parse 'r0xHEX' event.
*/
term = list_entry(term->list.next, struct parse_events_term, list);
TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
TEST_ASSERT_VAL("wrong type val",
term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
TEST_ASSERT_VAL("wrong val", term->val.num == 0xead);
TEST_ASSERT_VAL("wrong config", !term->config);
return 0;
}
......@@ -1766,6 +1794,11 @@ static struct evlist_test test__events_pmu[] = {
.check = test__checkevent_raw_pmu,
.id = 4,
},
{
.name = "software/r0x1a/",
.check = test__checkevent_raw_pmu,
.id = 4,
},
};
struct terms_test {
......@@ -1776,7 +1809,7 @@ struct terms_test {
static struct terms_test test__terms[] = {
[0] = {
.str = "config=10,config1,config2=3,umask=1",
.str = "config=10,config1,config2=3,umask=1,read,r0xead",
.check = test__checkterms_simple,
},
};
......@@ -1836,6 +1869,13 @@ static int test_term(struct terms_test *t)
INIT_LIST_HEAD(&terms);
/*
* The perf_pmu__test_parse_init prepares perf_pmu_events_list
* which gets freed in parse_events_terms.
*/
if (perf_pmu__test_parse_init())
return -1;
ret = parse_events_terms(&terms, t->str);
if (ret) {
pr_debug("failed to parse terms '%s', err %d\n",
......
此差异已折叠。
......@@ -185,14 +185,14 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
err = perf_evlist__parse_sample(evlist, event, &sample);
if (err < 0) {
if (verbose > 0)
perf_event__fprintf(event, stderr);
perf_event__fprintf(event, NULL, stderr);
pr_debug("Couldn't parse sample\n");
goto out_delete_evlist;
}
if (verbose > 0) {
pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
perf_event__fprintf(event, stderr);
perf_event__fprintf(event, NULL, stderr);
}
if (prev_time > sample.time) {
......
此差异已折叠。
......@@ -121,6 +121,7 @@ int test__demangle_java(struct test *test, int subtest);
int test__pfm(struct test *test, int subtest);
const char *test__pfm_subtest_get_desc(int subtest);
int test__pfm_subtest_get_nr(void);
int test__parse_metric(struct test *test, int subtest);
bool test__bp_signal_is_supported(void);
bool test__bp_account_is_supported(void);
......
......@@ -209,7 +209,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
ui_browser__mark_fused(browser,
pcnt_width + 3 + notes->widths.addr + width,
from - 1,
to > from ? true : false);
to > from);
}
}
......
......@@ -128,6 +128,7 @@ perf-y += expr-bison.o
perf-y += expr.o
perf-y += branch.o
perf-y += mem2node.o
perf-y += clockid.o
perf-$(CONFIG_LIBBPF) += bpf-loader.o
perf-$(CONFIG_LIBBPF) += bpf_map.o
......@@ -191,36 +192,60 @@ CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))"
# avoid compiler warnings in 32-bit mode
CFLAGS_genelf_debug.o += -Wno-packed
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-flex.h: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(call rule_mkdir)
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/parse-events-flex.c \
--header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) $<
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
$(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/parse-events-bison.h: util/parse-events.y
$(call rule_mkdir)
$(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
-o $(OUTPUT)util/parse-events-bison.c -p parse_events_
$(OUTPUT)util/expr-flex.c: util/expr.l $(OUTPUT)util/expr-bison.c
$(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-flex.h: util/expr.l $(OUTPUT)util/expr-bison.c
$(call rule_mkdir)
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/expr-flex.h $(PARSER_DEBUG_FLEX) util/expr.l
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/expr-flex.c \
--header-file=$(OUTPUT)util/expr-flex.h $(PARSER_DEBUG_FLEX) $<
$(OUTPUT)util/expr-bison.c: util/expr.y
$(OUTPUT)util/expr-bison.c $(OUTPUT)util/expr-bison.h: util/expr.y
$(call rule_mkdir)
$(Q)$(call echo-cmd,bison)$(BISON) -v util/expr.y -d $(PARSER_DEBUG_BISON) -o $@ -p expr_
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
-o $(OUTPUT)util/expr-bison.c -p expr_
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
$(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-flex.h: util/pmu.l $(OUTPUT)util/pmu-bison.c
$(call rule_mkdir)
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/pmu-flex.c \
--header-file=$(OUTPUT)util/pmu-flex.h $(PARSER_DEBUG_FLEX) $<
$(OUTPUT)util/pmu-bison.c: util/pmu.y
$(OUTPUT)util/pmu-bison.c $(OUTPUT)util/pmu-bison.h: util/pmu.y
$(call rule_mkdir)
$(Q)$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
CFLAGS_parse-events-flex.o += -w
CFLAGS_pmu-flex.o += -w
CFLAGS_expr-flex.o += -w
CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w
CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
CFLAGS_expr-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
-o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
FLEX_GE_26 := $(shell expr $(shell $(FLEX) --version | sed -e 's/flex \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 26)
ifeq ($(FLEX_GE_26),1)
flex_flags := -Wno-switch-enum -Wno-switch-default -Wno-unused-function -Wno-redundant-decls -Wno-sign-compare -Wno-unused-parameter -Wno-missing-prototypes -Wno-missing-declarations
CC_HASNT_MISLEADING_INDENTATION := $(shell echo "int main(void) { return 0 }" | $(CC) -Werror -Wno-misleading-indentation -o /dev/null -xc - 2>&1 | grep -q -- -Wno-misleading-indentation ; echo $$?)
ifeq ($(CC_HASNT_MISLEADING_INDENTATION), 1)
flex_flags += -Wno-misleading-indentation
endif
else
flex_flags := -w
endif
CFLAGS_parse-events-flex.o += $(flex_flags)
CFLAGS_pmu-flex.o += $(flex_flags)
CFLAGS_expr-flex.o += $(flex_flags)
bison_flags := -DYYENABLE_NLS=0
BISON_GE_35 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 35)
ifeq ($(BISON_GE_35),1)
bison_flags += -Wno-unused-parameter -Wno-nested-externs -Wno-implicit-function-declaration -Wno-switch-enum
else
bison_flags += -w
endif
CFLAGS_parse-events-bison.o += $(bison_flags)
CFLAGS_pmu-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
CFLAGS_expr-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
......
......@@ -1621,6 +1621,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
char *build_id_filename;
char *build_id_path = NULL;
char *pos;
int len;
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
!dso__is_kcore(dso))
......@@ -1649,10 +1650,16 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
if (pos && strlen(pos) < SBUILD_ID_SIZE - 2)
dirname(build_id_path);
if (dso__is_kcore(dso) ||
readlink(build_id_path, linkname, sizeof(linkname)) < 0 ||
strstr(linkname, DSO__NAME_KALLSYMS) ||
access(filename, R_OK)) {
if (dso__is_kcore(dso))
goto fallback;
len = readlink(build_id_path, linkname, sizeof(linkname) - 1);
if (len < 0)
goto fallback;
linkname[len] = '\0';
if (strstr(linkname, DSO__NAME_KALLSYMS) ||
access(filename, R_OK)) {
fallback:
/*
* If we don't have build-ids or the build-id file isn't in the
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_CLOCKID_H
#define __PERF_CLOCKID_H
struct option;
int parse_clockid(const struct option *opt, const char *str, int unset);
const char *clockid_name(clockid_t clk_id);
#endif
此差异已折叠。
......@@ -5,6 +5,7 @@
struct perf_data_convert_opts {
bool force;
bool all;
bool tod;
};
#endif /* __DATA_CONVERT_H */
......@@ -208,6 +208,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
case DSO_BINARY_TYPE__JAVA_JIT:
case DSO_BINARY_TYPE__BPF_PROG_INFO:
case DSO_BINARY_TYPE__BPF_IMAGE:
case DSO_BINARY_TYPE__OOL:
case DSO_BINARY_TYPE__NOT_FOUND:
ret = -1;
break;
......@@ -898,6 +899,8 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
*ret = bpf_read(dso, cache_offset, cache->data);
else if (dso->binary_type == DSO_BINARY_TYPE__OOL)
*ret = DSO__DATA_CACHE_SIZE;
else
*ret = file_read(dso, machine, cache_offset, cache->data);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册