Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf events changes from Ingo Molnar:

 "- kernel side:

   - Intel uncore PMU support for Nehalem and Sandy Bridge CPUs, we
     support both the events available via the MSR and via the PCI
     access space.

   - various uprobes cleanups and restructurings

   - PMU driver quirks by microcode version and required x86 microcode
     loader cleanups/robustization

   - various tracing robustness updates

   - static keys: remove obsolete static_branch()

  - tooling side:

   - GTK browser improvements

   - perf report browser: support screenshots to file

   - more automated tests

   - perf kvm improvements

   - perf bench refinements

   - build environment improvements

   - pipe mode improvements

   - libtraceevent updates, we have now hopefully merged most bits with
     the out of tree forked code base

  ... and many other goodies."

* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (138 commits)
  tracing: Check for allocation failure in __tracing_open()
  perf/x86: Fix intel_perfmon_event_mapformatting
  jump label: Remove static_branch()
  tracepoint: Use static_key_false(), since static_branch() is deprecated
  perf/x86: Uncore filter support for SandyBridge-EP
  perf/x86: Detect number of instances of uncore CBox
  perf/x86: Fix event constraint for SandyBridge-EP C-Box
  perf/x86: Use 0xff as pseudo code for fixed uncore event
  perf/x86: Save a few bytes in 'struct x86_pmu'
  perf/x86: Add a microcode revision check for SNB-PEBS
  perf/x86: Improve debug output in check_hw_exists()
  perf/x86/amd: Unify AMD's generic and family 15h pmus
  perf/x86: Move Intel specific code to intel_pmu_init()
  perf/x86: Rename Intel specific macros
  perf/x86: Fix USER/KERNEL tagging of samples
  perf tools: Split event symbols arrays to hw and sw parts
  perf tools: Split out PE_VALUE_SYM parsing token to SW and HW tokens
  perf tools: Add empty rule for new line in event syntax parsing
  perf test: Use ARRAY_SIZE in parse events tests
  tools lib traceevent: Cleanup realloc use
  ...
This commit is contained in:
Linus Torvalds 2012-07-22 11:10:36 -07:00
commit 2eafeb6a41
109 changed files with 5800 additions and 1724 deletions

View File

@ -313,8 +313,8 @@ struct kvm_pmu {
u64 counter_bitmask[2]; u64 counter_bitmask[2];
u64 global_ctrl_mask; u64 global_ctrl_mask;
u8 version; u8 version;
struct kvm_pmc gp_counters[X86_PMC_MAX_GENERIC]; struct kvm_pmc gp_counters[INTEL_PMC_MAX_GENERIC];
struct kvm_pmc fixed_counters[X86_PMC_MAX_FIXED]; struct kvm_pmc fixed_counters[INTEL_PMC_MAX_FIXED];
struct irq_work irq_work; struct irq_work irq_work;
u64 reprogram_pmi; u64 reprogram_pmi;
}; };

View File

@ -115,8 +115,8 @@ notrace static inline int native_write_msr_safe(unsigned int msr,
extern unsigned long long native_read_tsc(void); extern unsigned long long native_read_tsc(void);
extern int native_rdmsr_safe_regs(u32 regs[8]); extern int rdmsr_safe_regs(u32 regs[8]);
extern int native_wrmsr_safe_regs(u32 regs[8]); extern int wrmsr_safe_regs(u32 regs[8]);
static __always_inline unsigned long long __native_read_tsc(void) static __always_inline unsigned long long __native_read_tsc(void)
{ {
@ -187,43 +187,6 @@ static inline int rdmsrl_safe(unsigned msr, unsigned long long *p)
return err; return err;
} }
static inline int rdmsrl_amd_safe(unsigned msr, unsigned long long *p)
{
u32 gprs[8] = { 0 };
int err;
gprs[1] = msr;
gprs[7] = 0x9c5a203a;
err = native_rdmsr_safe_regs(gprs);
*p = gprs[0] | ((u64)gprs[2] << 32);
return err;
}
static inline int wrmsrl_amd_safe(unsigned msr, unsigned long long val)
{
u32 gprs[8] = { 0 };
gprs[0] = (u32)val;
gprs[1] = msr;
gprs[2] = val >> 32;
gprs[7] = 0x9c5a203a;
return native_wrmsr_safe_regs(gprs);
}
static inline int rdmsr_safe_regs(u32 regs[8])
{
return native_rdmsr_safe_regs(regs);
}
static inline int wrmsr_safe_regs(u32 regs[8])
{
return native_wrmsr_safe_regs(regs);
}
#define rdtscl(low) \ #define rdtscl(low) \
((low) = (u32)__native_read_tsc()) ((low) = (u32)__native_read_tsc())
@ -237,6 +200,8 @@ do { \
(high) = (u32)(_l >> 32); \ (high) = (u32)(_l >> 32); \
} while (0) } while (0)
#define rdpmcl(counter, val) ((val) = native_read_pmc(counter))
#define rdtscp(low, high, aux) \ #define rdtscp(low, high, aux) \
do { \ do { \
unsigned long long _val = native_read_tscp(&(aux)); \ unsigned long long _val = native_read_tscp(&(aux)); \
@ -248,8 +213,7 @@ do { \
#endif /* !CONFIG_PARAVIRT */ #endif /* !CONFIG_PARAVIRT */
#define wrmsrl_safe(msr, val) wrmsr_safe((msr), (u32)(val), \
#define checking_wrmsrl(msr, val) wrmsr_safe((msr), (u32)(val), \
(u32)((val) >> 32)) (u32)((val) >> 32))
#define write_tsc(val1, val2) wrmsr(MSR_IA32_TSC, (val1), (val2)) #define write_tsc(val1, val2) wrmsr(MSR_IA32_TSC, (val1), (val2))

View File

@ -128,21 +128,11 @@ static inline u64 paravirt_read_msr(unsigned msr, int *err)
return PVOP_CALL2(u64, pv_cpu_ops.read_msr, msr, err); return PVOP_CALL2(u64, pv_cpu_ops.read_msr, msr, err);
} }
static inline int paravirt_rdmsr_regs(u32 *regs)
{
return PVOP_CALL1(int, pv_cpu_ops.rdmsr_regs, regs);
}
static inline int paravirt_write_msr(unsigned msr, unsigned low, unsigned high) static inline int paravirt_write_msr(unsigned msr, unsigned low, unsigned high)
{ {
return PVOP_CALL3(int, pv_cpu_ops.write_msr, msr, low, high); return PVOP_CALL3(int, pv_cpu_ops.write_msr, msr, low, high);
} }
static inline int paravirt_wrmsr_regs(u32 *regs)
{
return PVOP_CALL1(int, pv_cpu_ops.wrmsr_regs, regs);
}
/* These should all do BUG_ON(_err), but our headers are too tangled. */ /* These should all do BUG_ON(_err), but our headers are too tangled. */
#define rdmsr(msr, val1, val2) \ #define rdmsr(msr, val1, val2) \
do { \ do { \
@ -176,9 +166,6 @@ do { \
_err; \ _err; \
}) })
#define rdmsr_safe_regs(regs) paravirt_rdmsr_regs(regs)
#define wrmsr_safe_regs(regs) paravirt_wrmsr_regs(regs)
static inline int rdmsrl_safe(unsigned msr, unsigned long long *p) static inline int rdmsrl_safe(unsigned msr, unsigned long long *p)
{ {
int err; int err;
@ -186,32 +173,6 @@ static inline int rdmsrl_safe(unsigned msr, unsigned long long *p)
*p = paravirt_read_msr(msr, &err); *p = paravirt_read_msr(msr, &err);
return err; return err;
} }
static inline int rdmsrl_amd_safe(unsigned msr, unsigned long long *p)
{
u32 gprs[8] = { 0 };
int err;
gprs[1] = msr;
gprs[7] = 0x9c5a203a;
err = paravirt_rdmsr_regs(gprs);
*p = gprs[0] | ((u64)gprs[2] << 32);
return err;
}
static inline int wrmsrl_amd_safe(unsigned msr, unsigned long long val)
{
u32 gprs[8] = { 0 };
gprs[0] = (u32)val;
gprs[1] = msr;
gprs[2] = val >> 32;
gprs[7] = 0x9c5a203a;
return paravirt_wrmsr_regs(gprs);
}
static inline u64 paravirt_read_tsc(void) static inline u64 paravirt_read_tsc(void)
{ {
@ -252,6 +213,8 @@ do { \
high = _l >> 32; \ high = _l >> 32; \
} while (0) } while (0)
#define rdpmcl(counter, val) ((val) = paravirt_read_pmc(counter))
static inline unsigned long long paravirt_rdtscp(unsigned int *aux) static inline unsigned long long paravirt_rdtscp(unsigned int *aux)
{ {
return PVOP_CALL1(u64, pv_cpu_ops.read_tscp, aux); return PVOP_CALL1(u64, pv_cpu_ops.read_tscp, aux);

View File

@ -153,9 +153,7 @@ struct pv_cpu_ops {
/* MSR, PMC and TSR operations. /* MSR, PMC and TSR operations.
err = 0/-EFAULT. wrmsr returns 0/-EFAULT. */ err = 0/-EFAULT. wrmsr returns 0/-EFAULT. */
u64 (*read_msr)(unsigned int msr, int *err); u64 (*read_msr)(unsigned int msr, int *err);
int (*rdmsr_regs)(u32 *regs);
int (*write_msr)(unsigned int msr, unsigned low, unsigned high); int (*write_msr)(unsigned int msr, unsigned low, unsigned high);
int (*wrmsr_regs)(u32 *regs);
u64 (*read_tsc)(void); u64 (*read_tsc)(void);
u64 (*read_pmc)(int counter); u64 (*read_pmc)(int counter);

View File

@ -5,11 +5,10 @@
* Performance event hw details: * Performance event hw details:
*/ */
#define X86_PMC_MAX_GENERIC 32 #define INTEL_PMC_MAX_GENERIC 32
#define X86_PMC_MAX_FIXED 3 #define INTEL_PMC_MAX_FIXED 3
#define INTEL_PMC_IDX_FIXED 32
#define X86_PMC_IDX_GENERIC 0
#define X86_PMC_IDX_FIXED 32
#define X86_PMC_IDX_MAX 64 #define X86_PMC_IDX_MAX 64
#define MSR_ARCH_PERFMON_PERFCTR0 0xc1 #define MSR_ARCH_PERFMON_PERFCTR0 0xc1
@ -48,8 +47,7 @@
(X86_RAW_EVENT_MASK | \ (X86_RAW_EVENT_MASK | \
AMD64_EVENTSEL_EVENT) AMD64_EVENTSEL_EVENT)
#define AMD64_NUM_COUNTERS 4 #define AMD64_NUM_COUNTERS 4
#define AMD64_NUM_COUNTERS_F15H 6 #define AMD64_NUM_COUNTERS_CORE 6
#define AMD64_NUM_COUNTERS_MAX AMD64_NUM_COUNTERS_F15H
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8)
@ -121,16 +119,16 @@ struct x86_pmu_capability {
/* Instr_Retired.Any: */ /* Instr_Retired.Any: */
#define MSR_ARCH_PERFMON_FIXED_CTR0 0x309 #define MSR_ARCH_PERFMON_FIXED_CTR0 0x309
#define X86_PMC_IDX_FIXED_INSTRUCTIONS (X86_PMC_IDX_FIXED + 0) #define INTEL_PMC_IDX_FIXED_INSTRUCTIONS (INTEL_PMC_IDX_FIXED + 0)
/* CPU_CLK_Unhalted.Core: */ /* CPU_CLK_Unhalted.Core: */
#define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a #define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a
#define X86_PMC_IDX_FIXED_CPU_CYCLES (X86_PMC_IDX_FIXED + 1) #define INTEL_PMC_IDX_FIXED_CPU_CYCLES (INTEL_PMC_IDX_FIXED + 1)
/* CPU_CLK_Unhalted.Ref: */ /* CPU_CLK_Unhalted.Ref: */
#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b
#define X86_PMC_IDX_FIXED_REF_CYCLES (X86_PMC_IDX_FIXED + 2) #define INTEL_PMC_IDX_FIXED_REF_CYCLES (INTEL_PMC_IDX_FIXED + 2)
#define X86_PMC_MSK_FIXED_REF_CYCLES (1ULL << X86_PMC_IDX_FIXED_REF_CYCLES) #define INTEL_PMC_MSK_FIXED_REF_CYCLES (1ULL << INTEL_PMC_IDX_FIXED_REF_CYCLES)
/* /*
* We model BTS tracing as another fixed-mode PMC. * We model BTS tracing as another fixed-mode PMC.
@ -139,7 +137,7 @@ struct x86_pmu_capability {
* values are used by actual fixed events and higher values are used * values are used by actual fixed events and higher values are used
* to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr. * to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr.
*/ */
#define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) #define INTEL_PMC_IDX_FIXED_BTS (INTEL_PMC_IDX_FIXED + 16)
/* /*
* IBS cpuid feature detection * IBS cpuid feature detection
@ -234,6 +232,7 @@ struct perf_guest_switch_msr {
extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr);
extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap);
extern void perf_check_microcode(void);
#else #else
static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr) static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
{ {
@ -247,6 +246,7 @@ static inline void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
} }
static inline void perf_events_lapic_init(void) { } static inline void perf_events_lapic_init(void) { }
static inline void perf_check_microcode(void) { }
#endif #endif
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)

View File

@ -48,7 +48,7 @@ struct arch_uprobe_task {
#endif #endif
}; };
extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);

View File

@ -32,7 +32,9 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o
ifdef CONFIG_PERF_EVENTS ifdef CONFIG_PERF_EVENTS
obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd.o obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd.o
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_p4.o perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_p4.o
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o
endif endif
obj-$(CONFIG_X86_MCE) += mcheck/ obj-$(CONFIG_X86_MCE) += mcheck/

View File

@ -19,6 +19,39 @@
#include "cpu.h" #include "cpu.h"
static inline int rdmsrl_amd_safe(unsigned msr, unsigned long long *p)
{
struct cpuinfo_x86 *c = &cpu_data(smp_processor_id());
u32 gprs[8] = { 0 };
int err;
WARN_ONCE((c->x86 != 0xf), "%s should only be used on K8!\n", __func__);
gprs[1] = msr;
gprs[7] = 0x9c5a203a;
err = rdmsr_safe_regs(gprs);
*p = gprs[0] | ((u64)gprs[2] << 32);
return err;
}
static inline int wrmsrl_amd_safe(unsigned msr, unsigned long long val)
{
struct cpuinfo_x86 *c = &cpu_data(smp_processor_id());
u32 gprs[8] = { 0 };
WARN_ONCE((c->x86 != 0xf), "%s should only be used on K8!\n", __func__);
gprs[0] = (u32)val;
gprs[1] = msr;
gprs[2] = val >> 32;
gprs[7] = 0x9c5a203a;
return wrmsr_safe_regs(gprs);
}
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
/* /*
* B step AMD K6 before B 9730xxxx have hardware bugs that can cause * B step AMD K6 before B 9730xxxx have hardware bugs that can cause
@ -586,9 +619,9 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
!cpu_has(c, X86_FEATURE_TOPOEXT)) { !cpu_has(c, X86_FEATURE_TOPOEXT)) {
u64 val; u64 val;
if (!rdmsrl_amd_safe(0xc0011005, &val)) { if (!rdmsrl_safe(0xc0011005, &val)) {
val |= 1ULL << 54; val |= 1ULL << 54;
wrmsrl_amd_safe(0xc0011005, val); wrmsrl_safe(0xc0011005, val);
rdmsrl(0xc0011005, val); rdmsrl(0xc0011005, val);
if (val & (1ULL << 54)) { if (val & (1ULL << 54)) {
set_cpu_cap(c, X86_FEATURE_TOPOEXT); set_cpu_cap(c, X86_FEATURE_TOPOEXT);
@ -679,7 +712,7 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
err = rdmsrl_safe(MSR_AMD64_MCx_MASK(4), &mask); err = rdmsrl_safe(MSR_AMD64_MCx_MASK(4), &mask);
if (err == 0) { if (err == 0) {
mask |= (1 << 10); mask |= (1 << 10);
checking_wrmsrl(MSR_AMD64_MCx_MASK(4), mask); wrmsrl_safe(MSR_AMD64_MCx_MASK(4), mask);
} }
} }

View File

@ -947,7 +947,7 @@ static void __cpuinit __print_cpu_msr(void)
index_max = msr_range_array[i].max; index_max = msr_range_array[i].max;
for (index = index_min; index < index_max; index++) { for (index = index_min; index < index_max; index++) {
if (rdmsrl_amd_safe(index, &val)) if (rdmsrl_safe(index, &val))
continue; continue;
printk(KERN_INFO " MSR%08x: %016llx\n", index, val); printk(KERN_INFO " MSR%08x: %016llx\n", index, val);
} }

View File

@ -35,17 +35,6 @@
#include "perf_event.h" #include "perf_event.h"
#if 0
#undef wrmsrl
#define wrmsrl(msr, val) \
do { \
trace_printk("wrmsrl(%lx, %lx)\n", (unsigned long)(msr),\
(unsigned long)(val)); \
native_write_msr((msr), (u32)((u64)(val)), \
(u32)((u64)(val) >> 32)); \
} while (0)
#endif
struct x86_pmu x86_pmu __read_mostly; struct x86_pmu x86_pmu __read_mostly;
DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
@ -74,7 +63,7 @@ u64 x86_perf_event_update(struct perf_event *event)
int idx = hwc->idx; int idx = hwc->idx;
s64 delta; s64 delta;
if (idx == X86_PMC_IDX_FIXED_BTS) if (idx == INTEL_PMC_IDX_FIXED_BTS)
return 0; return 0;
/* /*
@ -86,7 +75,7 @@ u64 x86_perf_event_update(struct perf_event *event)
*/ */
again: again:
prev_raw_count = local64_read(&hwc->prev_count); prev_raw_count = local64_read(&hwc->prev_count);
rdmsrl(hwc->event_base, new_raw_count); rdpmcl(hwc->event_base_rdpmc, new_raw_count);
if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
new_raw_count) != prev_raw_count) new_raw_count) != prev_raw_count)
@ -189,7 +178,7 @@ static void release_pmc_hardware(void) {}
static bool check_hw_exists(void) static bool check_hw_exists(void)
{ {
u64 val, val_new = 0; u64 val, val_new = ~0;
int i, reg, ret = 0; int i, reg, ret = 0;
/* /*
@ -222,8 +211,9 @@ static bool check_hw_exists(void)
* that don't trap on the MSR access and always return 0s. * that don't trap on the MSR access and always return 0s.
*/ */
val = 0xabcdUL; val = 0xabcdUL;
ret = checking_wrmsrl(x86_pmu_event_addr(0), val); reg = x86_pmu_event_addr(0);
ret |= rdmsrl_safe(x86_pmu_event_addr(0), &val_new); ret = wrmsrl_safe(reg, val);
ret |= rdmsrl_safe(reg, &val_new);
if (ret || val != val_new) if (ret || val != val_new)
goto msr_fail; goto msr_fail;
@ -240,6 +230,7 @@ static bool check_hw_exists(void)
msr_fail: msr_fail:
printk(KERN_CONT "Broken PMU hardware detected, using software events only.\n"); printk(KERN_CONT "Broken PMU hardware detected, using software events only.\n");
printk(KERN_ERR "Failed to access perfctr msr (MSR %x is %Lx)\n", reg, val_new);
return false; return false;
} }
@ -388,7 +379,7 @@ int x86_pmu_hw_config(struct perf_event *event)
int precise = 0; int precise = 0;
/* Support for constant skid */ /* Support for constant skid */
if (x86_pmu.pebs_active) { if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
precise++; precise++;
/* Support for IP fixup */ /* Support for IP fixup */
@ -637,8 +628,8 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
c = sched->constraints[sched->state.event]; c = sched->constraints[sched->state.event];
/* Prefer fixed purpose counters */ /* Prefer fixed purpose counters */
if (x86_pmu.num_counters_fixed) { if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) {
idx = X86_PMC_IDX_FIXED; idx = INTEL_PMC_IDX_FIXED;
for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) { for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) {
if (!__test_and_set_bit(idx, sched->state.used)) if (!__test_and_set_bit(idx, sched->state.used))
goto done; goto done;
@ -646,7 +637,7 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
} }
/* Grab the first unused counter starting with idx */ /* Grab the first unused counter starting with idx */
idx = sched->state.counter; idx = sched->state.counter;
for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_FIXED) { for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) {
if (!__test_and_set_bit(idx, sched->state.used)) if (!__test_and_set_bit(idx, sched->state.used))
goto done; goto done;
} }
@ -704,8 +695,8 @@ static bool perf_sched_next_event(struct perf_sched *sched)
/* /*
* Assign a counter for each event. * Assign a counter for each event.
*/ */
static int perf_assign_events(struct event_constraint **constraints, int n, int perf_assign_events(struct event_constraint **constraints, int n,
int wmin, int wmax, int *assign) int wmin, int wmax, int *assign)
{ {
struct perf_sched sched; struct perf_sched sched;
@ -824,15 +815,17 @@ static inline void x86_assign_hw_event(struct perf_event *event,
hwc->last_cpu = smp_processor_id(); hwc->last_cpu = smp_processor_id();
hwc->last_tag = ++cpuc->tags[i]; hwc->last_tag = ++cpuc->tags[i];
if (hwc->idx == X86_PMC_IDX_FIXED_BTS) { if (hwc->idx == INTEL_PMC_IDX_FIXED_BTS) {
hwc->config_base = 0; hwc->config_base = 0;
hwc->event_base = 0; hwc->event_base = 0;
} else if (hwc->idx >= X86_PMC_IDX_FIXED) { } else if (hwc->idx >= INTEL_PMC_IDX_FIXED) {
hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL; hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
hwc->event_base = MSR_ARCH_PERFMON_FIXED_CTR0 + (hwc->idx - X86_PMC_IDX_FIXED); hwc->event_base = MSR_ARCH_PERFMON_FIXED_CTR0 + (hwc->idx - INTEL_PMC_IDX_FIXED);
hwc->event_base_rdpmc = (hwc->idx - INTEL_PMC_IDX_FIXED) | 1<<30;
} else { } else {
hwc->config_base = x86_pmu_config_addr(hwc->idx); hwc->config_base = x86_pmu_config_addr(hwc->idx);
hwc->event_base = x86_pmu_event_addr(hwc->idx); hwc->event_base = x86_pmu_event_addr(hwc->idx);
hwc->event_base_rdpmc = hwc->idx;
} }
} }
@ -930,7 +923,7 @@ int x86_perf_event_set_period(struct perf_event *event)
s64 period = hwc->sample_period; s64 period = hwc->sample_period;
int ret = 0, idx = hwc->idx; int ret = 0, idx = hwc->idx;
if (idx == X86_PMC_IDX_FIXED_BTS) if (idx == INTEL_PMC_IDX_FIXED_BTS)
return 0; return 0;
/* /*
@ -1316,7 +1309,6 @@ static struct attribute_group x86_pmu_format_group = {
static int __init init_hw_perf_events(void) static int __init init_hw_perf_events(void)
{ {
struct x86_pmu_quirk *quirk; struct x86_pmu_quirk *quirk;
struct event_constraint *c;
int err; int err;
pr_info("Performance Events: "); pr_info("Performance Events: ");
@ -1347,21 +1339,8 @@ static int __init init_hw_perf_events(void)
for (quirk = x86_pmu.quirks; quirk; quirk = quirk->next) for (quirk = x86_pmu.quirks; quirk; quirk = quirk->next)
quirk->func(); quirk->func();
if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) { if (!x86_pmu.intel_ctrl)
WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!", x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1;
x86_pmu.num_counters, X86_PMC_MAX_GENERIC);
x86_pmu.num_counters = X86_PMC_MAX_GENERIC;
}
x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1;
if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) {
WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!",
x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED);
x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED;
}
x86_pmu.intel_ctrl |=
((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED;
perf_events_lapic_init(); perf_events_lapic_init();
register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI");
@ -1370,22 +1349,6 @@ static int __init init_hw_perf_events(void)
__EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1,
0, x86_pmu.num_counters, 0); 0, x86_pmu.num_counters, 0);
if (x86_pmu.event_constraints) {
/*
* event on fixed counter2 (REF_CYCLES) only works on this
* counter, so do not extend mask to generic counters
*/
for_each_event_constraint(c, x86_pmu.event_constraints) {
if (c->cmask != X86_RAW_EVENT_MASK
|| c->idxmsk64 == X86_PMC_MSK_FIXED_REF_CYCLES) {
continue;
}
c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
c->weight += x86_pmu.num_counters;
}
}
x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */
x86_pmu_format_group.attrs = x86_pmu.format_attrs; x86_pmu_format_group.attrs = x86_pmu.format_attrs;
@ -1620,8 +1583,8 @@ static int x86_pmu_event_idx(struct perf_event *event)
if (!x86_pmu.attr_rdpmc) if (!x86_pmu.attr_rdpmc)
return 0; return 0;
if (x86_pmu.num_counters_fixed && idx >= X86_PMC_IDX_FIXED) { if (x86_pmu.num_counters_fixed && idx >= INTEL_PMC_IDX_FIXED) {
idx -= X86_PMC_IDX_FIXED; idx -= INTEL_PMC_IDX_FIXED;
idx |= 1 << 30; idx |= 1 << 30;
} }
@ -1649,7 +1612,12 @@ static ssize_t set_attr_rdpmc(struct device *cdev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned long val = simple_strtoul(buf, NULL, 0); unsigned long val;
ssize_t ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
if (!!val != !!x86_pmu.attr_rdpmc) { if (!!val != !!x86_pmu.attr_rdpmc) {
x86_pmu.attr_rdpmc = !!val; x86_pmu.attr_rdpmc = !!val;
@ -1682,13 +1650,20 @@ static void x86_pmu_flush_branch_stack(void)
x86_pmu.flush_branch_stack(); x86_pmu.flush_branch_stack();
} }
void perf_check_microcode(void)
{
if (x86_pmu.check_microcode)
x86_pmu.check_microcode();
}
EXPORT_SYMBOL_GPL(perf_check_microcode);
static struct pmu pmu = { static struct pmu pmu = {
.pmu_enable = x86_pmu_enable, .pmu_enable = x86_pmu_enable,
.pmu_disable = x86_pmu_disable, .pmu_disable = x86_pmu_disable,
.attr_groups = x86_pmu_attr_groups, .attr_groups = x86_pmu_attr_groups,
.event_init = x86_pmu_event_init, .event_init = x86_pmu_event_init,
.add = x86_pmu_add, .add = x86_pmu_add,
.del = x86_pmu_del, .del = x86_pmu_del,
@ -1696,11 +1671,11 @@ static struct pmu pmu = {
.stop = x86_pmu_stop, .stop = x86_pmu_stop,
.read = x86_pmu_read, .read = x86_pmu_read,
.start_txn = x86_pmu_start_txn, .start_txn = x86_pmu_start_txn,
.cancel_txn = x86_pmu_cancel_txn, .cancel_txn = x86_pmu_cancel_txn,
.commit_txn = x86_pmu_commit_txn, .commit_txn = x86_pmu_commit_txn,
.event_idx = x86_pmu_event_idx, .event_idx = x86_pmu_event_idx,
.flush_branch_stack = x86_pmu_flush_branch_stack, .flush_branch_stack = x86_pmu_flush_branch_stack,
}; };
@ -1863,7 +1838,7 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
else else
misc |= PERF_RECORD_MISC_GUEST_KERNEL; misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else { } else {
if (user_mode(regs)) if (!kernel_ip(regs->ip))
misc |= PERF_RECORD_MISC_USER; misc |= PERF_RECORD_MISC_USER;
else else
misc |= PERF_RECORD_MISC_KERNEL; misc |= PERF_RECORD_MISC_KERNEL;

View File

@ -14,6 +14,18 @@
#include <linux/perf_event.h> #include <linux/perf_event.h>
#if 0
#undef wrmsrl
#define wrmsrl(msr, val) \
do { \
unsigned int _msr = (msr); \
u64 _val = (val); \
trace_printk("wrmsrl(%x, %Lx)\n", (unsigned int)(_msr), \
(unsigned long long)(_val)); \
native_write_msr((_msr), (u32)(_val), (u32)(_val >> 32)); \
} while (0)
#endif
/* /*
* | NHM/WSM | SNB | * | NHM/WSM | SNB |
* register ------------------------------- * register -------------------------------
@ -57,7 +69,7 @@ struct amd_nb {
}; };
/* The maximal number of PEBS events: */ /* The maximal number of PEBS events: */
#define MAX_PEBS_EVENTS 4 #define MAX_PEBS_EVENTS 8
/* /*
* A debug store configuration. * A debug store configuration.
@ -349,6 +361,8 @@ struct x86_pmu {
void (*cpu_starting)(int cpu); void (*cpu_starting)(int cpu);
void (*cpu_dying)(int cpu); void (*cpu_dying)(int cpu);
void (*cpu_dead)(int cpu); void (*cpu_dead)(int cpu);
void (*check_microcode)(void);
void (*flush_branch_stack)(void); void (*flush_branch_stack)(void);
/* /*
@ -360,12 +374,16 @@ struct x86_pmu {
/* /*
* Intel DebugStore bits * Intel DebugStore bits
*/ */
int bts, pebs; int bts :1,
int bts_active, pebs_active; bts_active :1,
pebs :1,
pebs_active :1,
pebs_broken :1;
int pebs_record_size; int pebs_record_size;
void (*drain_pebs)(struct pt_regs *regs); void (*drain_pebs)(struct pt_regs *regs);
struct event_constraint *pebs_constraints; struct event_constraint *pebs_constraints;
void (*pebs_aliases)(struct perf_event *event); void (*pebs_aliases)(struct perf_event *event);
int max_pebs_events;
/* /*
* Intel LBR * Intel LBR
@ -468,6 +486,8 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc,
void x86_pmu_enable_all(int added); void x86_pmu_enable_all(int added);
int perf_assign_events(struct event_constraint **constraints, int n,
int wmin, int wmax, int *assign);
int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign); int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign);
void x86_pmu_stop(struct perf_event *event, int flags); void x86_pmu_stop(struct perf_event *event, int flags);

View File

@ -366,7 +366,7 @@ static void amd_pmu_cpu_starting(int cpu)
cpuc->perf_ctr_virt_mask = AMD_PERFMON_EVENTSEL_HOSTONLY; cpuc->perf_ctr_virt_mask = AMD_PERFMON_EVENTSEL_HOSTONLY;
if (boot_cpu_data.x86_max_cores < 2 || boot_cpu_data.x86 == 0x15) if (boot_cpu_data.x86_max_cores < 2)
return; return;
nb_id = amd_get_nb_id(cpu); nb_id = amd_get_nb_id(cpu);
@ -422,35 +422,6 @@ static struct attribute *amd_format_attr[] = {
NULL, NULL,
}; };
static __initconst const struct x86_pmu amd_pmu = {
.name = "AMD",
.handle_irq = x86_pmu_handle_irq,
.disable_all = x86_pmu_disable_all,
.enable_all = x86_pmu_enable_all,
.enable = x86_pmu_enable_event,
.disable = x86_pmu_disable_event,
.hw_config = amd_pmu_hw_config,
.schedule_events = x86_schedule_events,
.eventsel = MSR_K7_EVNTSEL0,
.perfctr = MSR_K7_PERFCTR0,
.event_map = amd_pmu_event_map,
.max_events = ARRAY_SIZE(amd_perfmon_event_map),
.num_counters = AMD64_NUM_COUNTERS,
.cntval_bits = 48,
.cntval_mask = (1ULL << 48) - 1,
.apic = 1,
/* use highest bit to detect overflow */
.max_period = (1ULL << 47) - 1,
.get_event_constraints = amd_get_event_constraints,
.put_event_constraints = amd_put_event_constraints,
.format_attrs = amd_format_attr,
.cpu_prepare = amd_pmu_cpu_prepare,
.cpu_starting = amd_pmu_cpu_starting,
.cpu_dead = amd_pmu_cpu_dead,
};
/* AMD Family 15h */ /* AMD Family 15h */
#define AMD_EVENT_TYPE_MASK 0x000000F0ULL #define AMD_EVENT_TYPE_MASK 0x000000F0ULL
@ -597,8 +568,8 @@ amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *ev
} }
} }
static __initconst const struct x86_pmu amd_pmu_f15h = { static __initconst const struct x86_pmu amd_pmu = {
.name = "AMD Family 15h", .name = "AMD",
.handle_irq = x86_pmu_handle_irq, .handle_irq = x86_pmu_handle_irq,
.disable_all = x86_pmu_disable_all, .disable_all = x86_pmu_disable_all,
.enable_all = x86_pmu_enable_all, .enable_all = x86_pmu_enable_all,
@ -606,50 +577,68 @@ static __initconst const struct x86_pmu amd_pmu_f15h = {
.disable = x86_pmu_disable_event, .disable = x86_pmu_disable_event,
.hw_config = amd_pmu_hw_config, .hw_config = amd_pmu_hw_config,
.schedule_events = x86_schedule_events, .schedule_events = x86_schedule_events,
.eventsel = MSR_F15H_PERF_CTL, .eventsel = MSR_K7_EVNTSEL0,
.perfctr = MSR_F15H_PERF_CTR, .perfctr = MSR_K7_PERFCTR0,
.event_map = amd_pmu_event_map, .event_map = amd_pmu_event_map,
.max_events = ARRAY_SIZE(amd_perfmon_event_map), .max_events = ARRAY_SIZE(amd_perfmon_event_map),
.num_counters = AMD64_NUM_COUNTERS_F15H, .num_counters = AMD64_NUM_COUNTERS,
.cntval_bits = 48, .cntval_bits = 48,
.cntval_mask = (1ULL << 48) - 1, .cntval_mask = (1ULL << 48) - 1,
.apic = 1, .apic = 1,
/* use highest bit to detect overflow */ /* use highest bit to detect overflow */
.max_period = (1ULL << 47) - 1, .max_period = (1ULL << 47) - 1,
.get_event_constraints = amd_get_event_constraints_f15h, .get_event_constraints = amd_get_event_constraints,
/* nortbridge counters not yet implemented: */
#if 0
.put_event_constraints = amd_put_event_constraints, .put_event_constraints = amd_put_event_constraints,
.cpu_prepare = amd_pmu_cpu_prepare,
.cpu_dead = amd_pmu_cpu_dead,
#endif
.cpu_starting = amd_pmu_cpu_starting,
.format_attrs = amd_format_attr, .format_attrs = amd_format_attr,
.cpu_prepare = amd_pmu_cpu_prepare,
.cpu_starting = amd_pmu_cpu_starting,
.cpu_dead = amd_pmu_cpu_dead,
}; };
static int setup_event_constraints(void)
{
if (boot_cpu_data.x86 >= 0x15)
x86_pmu.get_event_constraints = amd_get_event_constraints_f15h;
return 0;
}
static int setup_perfctr_core(void)
{
if (!cpu_has_perfctr_core) {
WARN(x86_pmu.get_event_constraints == amd_get_event_constraints_f15h,
KERN_ERR "Odd, counter constraints enabled but no core perfctrs detected!");
return -ENODEV;
}
WARN(x86_pmu.get_event_constraints == amd_get_event_constraints,
KERN_ERR "hw perf events core counters need constraints handler!");
/*
* If core performance counter extensions exists, we must use
* MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also
* x86_pmu_addr_offset().
*/
x86_pmu.eventsel = MSR_F15H_PERF_CTL;
x86_pmu.perfctr = MSR_F15H_PERF_CTR;
x86_pmu.num_counters = AMD64_NUM_COUNTERS_CORE;
printk(KERN_INFO "perf: AMD core performance counters detected\n");
return 0;
}
__init int amd_pmu_init(void) __init int amd_pmu_init(void)
{ {
/* Performance-monitoring supported from K7 and later: */ /* Performance-monitoring supported from K7 and later: */
if (boot_cpu_data.x86 < 6) if (boot_cpu_data.x86 < 6)
return -ENODEV; return -ENODEV;
/* x86_pmu = amd_pmu;
* If core performance counter extensions exists, it must be
* family 15h, otherwise fail. See x86_pmu_addr_offset(). setup_event_constraints();
*/ setup_perfctr_core();
switch (boot_cpu_data.x86) {
case 0x15:
if (!cpu_has_perfctr_core)
return -ENODEV;
x86_pmu = amd_pmu_f15h;
break;
default:
if (cpu_has_perfctr_core)
return -ENODEV;
x86_pmu = amd_pmu;
break;
}
/* Events are common for all AMDs */ /* Events are common for all AMDs */
memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, memcpy(hw_cache_event_ids, amd_hw_cache_event_ids,

View File

@ -21,14 +21,14 @@
*/ */
static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly = static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly =
{ {
[PERF_COUNT_HW_CPU_CYCLES] = 0x003c, [PERF_COUNT_HW_CPU_CYCLES] = 0x003c,
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e, [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e,
[PERF_COUNT_HW_CACHE_MISSES] = 0x412e, [PERF_COUNT_HW_CACHE_MISSES] = 0x412e,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5,
[PERF_COUNT_HW_BUS_CYCLES] = 0x013c, [PERF_COUNT_HW_BUS_CYCLES] = 0x013c,
[PERF_COUNT_HW_REF_CPU_CYCLES] = 0x0300, /* pseudo-encoding */ [PERF_COUNT_HW_REF_CPU_CYCLES] = 0x0300, /* pseudo-encoding */
}; };
static struct event_constraint intel_core_event_constraints[] __read_mostly = static struct event_constraint intel_core_event_constraints[] __read_mostly =
@ -747,7 +747,7 @@ static void intel_pmu_disable_all(void)
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask))
intel_pmu_disable_bts(); intel_pmu_disable_bts();
intel_pmu_pebs_disable_all(); intel_pmu_pebs_disable_all();
@ -763,9 +763,9 @@ static void intel_pmu_enable_all(int added)
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL,
x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask); x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask);
if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) {
struct perf_event *event = struct perf_event *event =
cpuc->events[X86_PMC_IDX_FIXED_BTS]; cpuc->events[INTEL_PMC_IDX_FIXED_BTS];
if (WARN_ON_ONCE(!event)) if (WARN_ON_ONCE(!event))
return; return;
@ -871,7 +871,7 @@ static inline void intel_pmu_ack_status(u64 ack)
static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) static void intel_pmu_disable_fixed(struct hw_perf_event *hwc)
{ {
int idx = hwc->idx - X86_PMC_IDX_FIXED; int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
u64 ctrl_val, mask; u64 ctrl_val, mask;
mask = 0xfULL << (idx * 4); mask = 0xfULL << (idx * 4);
@ -886,7 +886,7 @@ static void intel_pmu_disable_event(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { if (unlikely(hwc->idx == INTEL_PMC_IDX_FIXED_BTS)) {
intel_pmu_disable_bts(); intel_pmu_disable_bts();
intel_pmu_drain_bts_buffer(); intel_pmu_drain_bts_buffer();
return; return;
@ -915,7 +915,7 @@ static void intel_pmu_disable_event(struct perf_event *event)
static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) static void intel_pmu_enable_fixed(struct hw_perf_event *hwc)
{ {
int idx = hwc->idx - X86_PMC_IDX_FIXED; int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
u64 ctrl_val, bits, mask; u64 ctrl_val, bits, mask;
/* /*
@ -949,7 +949,7 @@ static void intel_pmu_enable_event(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { if (unlikely(hwc->idx == INTEL_PMC_IDX_FIXED_BTS)) {
if (!__this_cpu_read(cpu_hw_events.enabled)) if (!__this_cpu_read(cpu_hw_events.enabled))
return; return;
@ -1003,11 +1003,11 @@ static void intel_pmu_reset(void)
printk("clearing PMU state on CPU#%d\n", smp_processor_id()); printk("clearing PMU state on CPU#%d\n", smp_processor_id());
for (idx = 0; idx < x86_pmu.num_counters; idx++) { for (idx = 0; idx < x86_pmu.num_counters; idx++) {
checking_wrmsrl(x86_pmu_config_addr(idx), 0ull); wrmsrl_safe(x86_pmu_config_addr(idx), 0ull);
checking_wrmsrl(x86_pmu_event_addr(idx), 0ull); wrmsrl_safe(x86_pmu_event_addr(idx), 0ull);
} }
for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++)
checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); wrmsrl_safe(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull);
if (ds) if (ds)
ds->bts_index = ds->bts_buffer_base; ds->bts_index = ds->bts_buffer_base;
@ -1712,11 +1712,56 @@ static __init void intel_clovertown_quirk(void)
x86_pmu.pebs_constraints = NULL; x86_pmu.pebs_constraints = NULL;
} }
static int intel_snb_pebs_broken(int cpu)
{
u32 rev = UINT_MAX; /* default to broken for unknown models */
switch (cpu_data(cpu).x86_model) {
case 42: /* SNB */
rev = 0x28;
break;
case 45: /* SNB-EP */
switch (cpu_data(cpu).x86_mask) {
case 6: rev = 0x618; break;
case 7: rev = 0x70c; break;
}
}
return (cpu_data(cpu).microcode < rev);
}
static void intel_snb_check_microcode(void)
{
int pebs_broken = 0;
int cpu;
get_online_cpus();
for_each_online_cpu(cpu) {
if ((pebs_broken = intel_snb_pebs_broken(cpu)))
break;
}
put_online_cpus();
if (pebs_broken == x86_pmu.pebs_broken)
return;
/*
* Serialized by the microcode lock..
*/
if (x86_pmu.pebs_broken) {
pr_info("PEBS enabled due to microcode update\n");
x86_pmu.pebs_broken = 0;
} else {
pr_info("PEBS disabled due to CPU errata, please upgrade microcode\n");
x86_pmu.pebs_broken = 1;
}
}
static __init void intel_sandybridge_quirk(void) static __init void intel_sandybridge_quirk(void)
{ {
printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); x86_pmu.check_microcode = intel_snb_check_microcode;
x86_pmu.pebs = 0; intel_snb_check_microcode();
x86_pmu.pebs_constraints = NULL;
} }
static const struct { int id; char *name; } intel_arch_events_map[] __initconst = { static const struct { int id; char *name; } intel_arch_events_map[] __initconst = {
@ -1765,6 +1810,7 @@ __init int intel_pmu_init(void)
union cpuid10_edx edx; union cpuid10_edx edx;
union cpuid10_eax eax; union cpuid10_eax eax;
union cpuid10_ebx ebx; union cpuid10_ebx ebx;
struct event_constraint *c;
unsigned int unused; unsigned int unused;
int version; int version;
@ -1800,6 +1846,8 @@ __init int intel_pmu_init(void)
x86_pmu.events_maskl = ebx.full; x86_pmu.events_maskl = ebx.full;
x86_pmu.events_mask_len = eax.split.mask_length; x86_pmu.events_mask_len = eax.split.mask_length;
x86_pmu.max_pebs_events = min_t(unsigned, MAX_PEBS_EVENTS, x86_pmu.num_counters);
/* /*
* Quirk: v2 perfmon does not report fixed-purpose events, so * Quirk: v2 perfmon does not report fixed-purpose events, so
* assume at least 3 events: * assume at least 3 events:
@ -1951,5 +1999,37 @@ __init int intel_pmu_init(void)
} }
} }
if (x86_pmu.num_counters > INTEL_PMC_MAX_GENERIC) {
WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!",
x86_pmu.num_counters, INTEL_PMC_MAX_GENERIC);
x86_pmu.num_counters = INTEL_PMC_MAX_GENERIC;
}
x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1;
if (x86_pmu.num_counters_fixed > INTEL_PMC_MAX_FIXED) {
WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!",
x86_pmu.num_counters_fixed, INTEL_PMC_MAX_FIXED);
x86_pmu.num_counters_fixed = INTEL_PMC_MAX_FIXED;
}
x86_pmu.intel_ctrl |=
((1LL << x86_pmu.num_counters_fixed)-1) << INTEL_PMC_IDX_FIXED;
if (x86_pmu.event_constraints) {
/*
* event on fixed counter2 (REF_CYCLES) only works on this
* counter, so do not extend mask to generic counters
*/
for_each_event_constraint(c, x86_pmu.event_constraints) {
if (c->cmask != X86_RAW_EVENT_MASK
|| c->idxmsk64 == INTEL_PMC_MSK_FIXED_REF_CYCLES) {
continue;
}
c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
c->weight += x86_pmu.num_counters;
}
}
return 0; return 0;
} }

View File

@ -248,7 +248,7 @@ void reserve_ds_buffers(void)
*/ */
struct event_constraint bts_constraint = struct event_constraint bts_constraint =
EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); EVENT_CONSTRAINT(0, 1ULL << INTEL_PMC_IDX_FIXED_BTS, 0);
void intel_pmu_enable_bts(u64 config) void intel_pmu_enable_bts(u64 config)
{ {
@ -295,7 +295,7 @@ int intel_pmu_drain_bts_buffer(void)
u64 to; u64 to;
u64 flags; u64 flags;
}; };
struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; struct perf_event *event = cpuc->events[INTEL_PMC_IDX_FIXED_BTS];
struct bts_record *at, *top; struct bts_record *at, *top;
struct perf_output_handle handle; struct perf_output_handle handle;
struct perf_event_header header; struct perf_event_header header;
@ -620,7 +620,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
* Should not happen, we program the threshold at 1 and do not * Should not happen, we program the threshold at 1 and do not
* set a reset value. * set a reset value.
*/ */
WARN_ON_ONCE(n > 1); WARN_ONCE(n > 1, "bad leftover pebs %d\n", n);
at += n - 1; at += n - 1;
__intel_pmu_pebs_event(event, iregs, at); __intel_pmu_pebs_event(event, iregs, at);
@ -651,10 +651,10 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
* Should not happen, we program the threshold at 1 and do not * Should not happen, we program the threshold at 1 and do not
* set a reset value. * set a reset value.
*/ */
WARN_ON_ONCE(n > MAX_PEBS_EVENTS); WARN_ONCE(n > x86_pmu.max_pebs_events, "Unexpected number of pebs records %d\n", n);
for ( ; at < top; at++) { for ( ; at < top; at++) {
for_each_set_bit(bit, (unsigned long *)&at->status, MAX_PEBS_EVENTS) { for_each_set_bit(bit, (unsigned long *)&at->status, x86_pmu.max_pebs_events) {
event = cpuc->events[bit]; event = cpuc->events[bit];
if (!test_bit(bit, cpuc->active_mask)) if (!test_bit(bit, cpuc->active_mask))
continue; continue;
@ -670,7 +670,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
break; break;
} }
if (!event || bit >= MAX_PEBS_EVENTS) if (!event || bit >= x86_pmu.max_pebs_events)
continue; continue;
__intel_pmu_pebs_event(event, iregs, at); __intel_pmu_pebs_event(event, iregs, at);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,424 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/perf_event.h>
#include "perf_event.h"
#define UNCORE_PMU_NAME_LEN 32
#define UNCORE_BOX_HASH_SIZE 8
#define UNCORE_PMU_HRTIMER_INTERVAL (60 * NSEC_PER_SEC)
#define UNCORE_FIXED_EVENT 0xff
#define UNCORE_PMC_IDX_MAX_GENERIC 8
#define UNCORE_PMC_IDX_FIXED UNCORE_PMC_IDX_MAX_GENERIC
#define UNCORE_PMC_IDX_MAX (UNCORE_PMC_IDX_FIXED + 1)
#define UNCORE_EVENT_CONSTRAINT(c, n) EVENT_CONSTRAINT(c, n, 0xff)
/* SNB event control */
#define SNB_UNC_CTL_EV_SEL_MASK 0x000000ff
#define SNB_UNC_CTL_UMASK_MASK 0x0000ff00
#define SNB_UNC_CTL_EDGE_DET (1 << 18)
#define SNB_UNC_CTL_EN (1 << 22)
#define SNB_UNC_CTL_INVERT (1 << 23)
#define SNB_UNC_CTL_CMASK_MASK 0x1f000000
#define NHM_UNC_CTL_CMASK_MASK 0xff000000
#define NHM_UNC_FIXED_CTR_CTL_EN (1 << 0)
#define SNB_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
SNB_UNC_CTL_UMASK_MASK | \
SNB_UNC_CTL_EDGE_DET | \
SNB_UNC_CTL_INVERT | \
SNB_UNC_CTL_CMASK_MASK)
#define NHM_UNC_RAW_EVENT_MASK (SNB_UNC_CTL_EV_SEL_MASK | \
SNB_UNC_CTL_UMASK_MASK | \
SNB_UNC_CTL_EDGE_DET | \
SNB_UNC_CTL_INVERT | \
NHM_UNC_CTL_CMASK_MASK)
/* SNB global control register */
#define SNB_UNC_PERF_GLOBAL_CTL 0x391
#define SNB_UNC_FIXED_CTR_CTRL 0x394
#define SNB_UNC_FIXED_CTR 0x395
/* SNB uncore global control */
#define SNB_UNC_GLOBAL_CTL_CORE_ALL ((1 << 4) - 1)
#define SNB_UNC_GLOBAL_CTL_EN (1 << 29)
/* SNB Cbo register */
#define SNB_UNC_CBO_0_PERFEVTSEL0 0x700
#define SNB_UNC_CBO_0_PER_CTR0 0x706
#define SNB_UNC_CBO_MSR_OFFSET 0x10
/* NHM global control register */
#define NHM_UNC_PERF_GLOBAL_CTL 0x391
#define NHM_UNC_FIXED_CTR 0x394
#define NHM_UNC_FIXED_CTR_CTRL 0x395
/* NHM uncore global control */
#define NHM_UNC_GLOBAL_CTL_EN_PC_ALL ((1ULL << 8) - 1)
#define NHM_UNC_GLOBAL_CTL_EN_FC (1ULL << 32)
/* NHM uncore register */
#define NHM_UNC_PERFEVTSEL0 0x3c0
#define NHM_UNC_UNCORE_PMC0 0x3b0
/* SNB-EP Box level control */
#define SNBEP_PMON_BOX_CTL_RST_CTRL (1 << 0)
#define SNBEP_PMON_BOX_CTL_RST_CTRS (1 << 1)
#define SNBEP_PMON_BOX_CTL_FRZ (1 << 8)
#define SNBEP_PMON_BOX_CTL_FRZ_EN (1 << 16)
#define SNBEP_PMON_BOX_CTL_INT (SNBEP_PMON_BOX_CTL_RST_CTRL | \
SNBEP_PMON_BOX_CTL_RST_CTRS | \
SNBEP_PMON_BOX_CTL_FRZ_EN)
/* SNB-EP event control */
#define SNBEP_PMON_CTL_EV_SEL_MASK 0x000000ff
#define SNBEP_PMON_CTL_UMASK_MASK 0x0000ff00
#define SNBEP_PMON_CTL_RST (1 << 17)
#define SNBEP_PMON_CTL_EDGE_DET (1 << 18)
#define SNBEP_PMON_CTL_EV_SEL_EXT (1 << 21) /* only for QPI */
#define SNBEP_PMON_CTL_EN (1 << 22)
#define SNBEP_PMON_CTL_INVERT (1 << 23)
#define SNBEP_PMON_CTL_TRESH_MASK 0xff000000
#define SNBEP_PMON_RAW_EVENT_MASK (SNBEP_PMON_CTL_EV_SEL_MASK | \
SNBEP_PMON_CTL_UMASK_MASK | \
SNBEP_PMON_CTL_EDGE_DET | \
SNBEP_PMON_CTL_INVERT | \
SNBEP_PMON_CTL_TRESH_MASK)
/* SNB-EP Ubox event control */
#define SNBEP_U_MSR_PMON_CTL_TRESH_MASK 0x1f000000
#define SNBEP_U_MSR_PMON_RAW_EVENT_MASK \
(SNBEP_PMON_CTL_EV_SEL_MASK | \
SNBEP_PMON_CTL_UMASK_MASK | \
SNBEP_PMON_CTL_EDGE_DET | \
SNBEP_PMON_CTL_INVERT | \
SNBEP_U_MSR_PMON_CTL_TRESH_MASK)
#define SNBEP_CBO_PMON_CTL_TID_EN (1 << 19)
#define SNBEP_CBO_MSR_PMON_RAW_EVENT_MASK (SNBEP_PMON_RAW_EVENT_MASK | \
SNBEP_CBO_PMON_CTL_TID_EN)
/* SNB-EP PCU event control */
#define SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK 0x0000c000
#define SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK 0x1f000000
#define SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT (1 << 30)
#define SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET (1 << 31)
#define SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK \
(SNBEP_PMON_CTL_EV_SEL_MASK | \
SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \
SNBEP_PMON_CTL_EDGE_DET | \
SNBEP_PMON_CTL_INVERT | \
SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK | \
SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \
SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET)
/* SNB-EP pci control register */
#define SNBEP_PCI_PMON_BOX_CTL 0xf4
#define SNBEP_PCI_PMON_CTL0 0xd8
/* SNB-EP pci counter register */
#define SNBEP_PCI_PMON_CTR0 0xa0
/* SNB-EP home agent register */
#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH0 0x40
#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH1 0x44
#define SNBEP_HA_PCI_PMON_BOX_OPCODEMATCH 0x48
/* SNB-EP memory controller register */
#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTL 0xf0
#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTR 0xd0
/* SNB-EP QPI register */
#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH0 0x228
#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH1 0x22c
#define SNBEP_Q_Py_PCI_PMON_PKT_MASK0 0x238
#define SNBEP_Q_Py_PCI_PMON_PKT_MASK1 0x23c
/* SNB-EP Ubox register */
#define SNBEP_U_MSR_PMON_CTR0 0xc16
#define SNBEP_U_MSR_PMON_CTL0 0xc10
#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTL 0xc08
#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTR 0xc09
/* SNB-EP Cbo register */
#define SNBEP_C0_MSR_PMON_CTR0 0xd16
#define SNBEP_C0_MSR_PMON_CTL0 0xd10
#define SNBEP_C0_MSR_PMON_BOX_CTL 0xd04
#define SNBEP_C0_MSR_PMON_BOX_FILTER 0xd14
#define SNBEP_CB0_MSR_PMON_BOX_FILTER_MASK 0xfffffc1f
#define SNBEP_CBO_MSR_OFFSET 0x20
/* SNB-EP PCU register */
#define SNBEP_PCU_MSR_PMON_CTR0 0xc36
#define SNBEP_PCU_MSR_PMON_CTL0 0xc30
#define SNBEP_PCU_MSR_PMON_BOX_CTL 0xc24
#define SNBEP_PCU_MSR_PMON_BOX_FILTER 0xc34
#define SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK 0xffffffff
#define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc
#define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd
struct intel_uncore_ops;
struct intel_uncore_pmu;
struct intel_uncore_box;
struct uncore_event_desc;
struct intel_uncore_type {
const char *name;
int num_counters;
int num_boxes;
int perf_ctr_bits;
int fixed_ctr_bits;
unsigned perf_ctr;
unsigned event_ctl;
unsigned event_mask;
unsigned fixed_ctr;
unsigned fixed_ctl;
unsigned box_ctl;
unsigned msr_offset;
unsigned num_shared_regs:8;
unsigned single_fixed:1;
struct event_constraint unconstrainted;
struct event_constraint *constraints;
struct intel_uncore_pmu *pmus;
struct intel_uncore_ops *ops;
struct uncore_event_desc *event_descs;
const struct attribute_group *attr_groups[3];
};
#define format_group attr_groups[0]
struct intel_uncore_ops {
void (*init_box)(struct intel_uncore_box *);
void (*disable_box)(struct intel_uncore_box *);
void (*enable_box)(struct intel_uncore_box *);
void (*disable_event)(struct intel_uncore_box *, struct perf_event *);
void (*enable_event)(struct intel_uncore_box *, struct perf_event *);
u64 (*read_counter)(struct intel_uncore_box *, struct perf_event *);
int (*hw_config)(struct intel_uncore_box *, struct perf_event *);
struct event_constraint *(*get_constraint)(struct intel_uncore_box *,
struct perf_event *);
void (*put_constraint)(struct intel_uncore_box *, struct perf_event *);
};
struct intel_uncore_pmu {
struct pmu pmu;
char name[UNCORE_PMU_NAME_LEN];
int pmu_idx;
int func_id;
struct intel_uncore_type *type;
struct intel_uncore_box ** __percpu box;
struct list_head box_list;
};
struct intel_uncore_extra_reg {
raw_spinlock_t lock;
u64 config1;
atomic_t ref;
};
struct intel_uncore_box {
int phys_id;
int n_active; /* number of active events */
int n_events;
int cpu; /* cpu to collect events */
unsigned long flags;
atomic_t refcnt;
struct perf_event *events[UNCORE_PMC_IDX_MAX];
struct perf_event *event_list[UNCORE_PMC_IDX_MAX];
unsigned long active_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)];
u64 tags[UNCORE_PMC_IDX_MAX];
struct pci_dev *pci_dev;
struct intel_uncore_pmu *pmu;
struct hrtimer hrtimer;
struct list_head list;
struct intel_uncore_extra_reg shared_regs[0];
};
#define UNCORE_BOX_FLAG_INITIATED 0
struct uncore_event_desc {
struct kobj_attribute attr;
const char *config;
};
#define INTEL_UNCORE_EVENT_DESC(_name, _config) \
{ \
.attr = __ATTR(_name, 0444, uncore_event_show, NULL), \
.config = _config, \
}
#define DEFINE_UNCORE_FORMAT_ATTR(_var, _name, _format) \
static ssize_t __uncore_##_var##_show(struct kobject *kobj, \
struct kobj_attribute *attr, \
char *page) \
{ \
BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
return sprintf(page, _format "\n"); \
} \
static struct kobj_attribute format_attr_##_var = \
__ATTR(_name, 0444, __uncore_##_var##_show, NULL)
static ssize_t uncore_event_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct uncore_event_desc *event =
container_of(attr, struct uncore_event_desc, attr);
return sprintf(buf, "%s", event->config);
}
static inline unsigned uncore_pci_box_ctl(struct intel_uncore_box *box)
{
return box->pmu->type->box_ctl;
}
static inline unsigned uncore_pci_fixed_ctl(struct intel_uncore_box *box)
{
return box->pmu->type->fixed_ctl;
}
static inline unsigned uncore_pci_fixed_ctr(struct intel_uncore_box *box)
{
return box->pmu->type->fixed_ctr;
}
static inline
unsigned uncore_pci_event_ctl(struct intel_uncore_box *box, int idx)
{
return idx * 4 + box->pmu->type->event_ctl;
}
static inline
unsigned uncore_pci_perf_ctr(struct intel_uncore_box *box, int idx)
{
return idx * 8 + box->pmu->type->perf_ctr;
}
static inline
unsigned uncore_msr_box_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->box_ctl)
return 0;
return box->pmu->type->box_ctl +
box->pmu->type->msr_offset * box->pmu->pmu_idx;
}
static inline
unsigned uncore_msr_fixed_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->fixed_ctl)
return 0;
return box->pmu->type->fixed_ctl +
box->pmu->type->msr_offset * box->pmu->pmu_idx;
}
static inline
unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
{
return box->pmu->type->fixed_ctr +
box->pmu->type->msr_offset * box->pmu->pmu_idx;
}
static inline
unsigned uncore_msr_event_ctl(struct intel_uncore_box *box, int idx)
{
return idx + box->pmu->type->event_ctl +
box->pmu->type->msr_offset * box->pmu->pmu_idx;
}
static inline
unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx)
{
return idx + box->pmu->type->perf_ctr +
box->pmu->type->msr_offset * box->pmu->pmu_idx;
}
static inline
unsigned uncore_fixed_ctl(struct intel_uncore_box *box)
{
if (box->pci_dev)
return uncore_pci_fixed_ctl(box);
else
return uncore_msr_fixed_ctl(box);
}
static inline
unsigned uncore_fixed_ctr(struct intel_uncore_box *box)
{
if (box->pci_dev)
return uncore_pci_fixed_ctr(box);
else
return uncore_msr_fixed_ctr(box);
}
static inline
unsigned uncore_event_ctl(struct intel_uncore_box *box, int idx)
{
if (box->pci_dev)
return uncore_pci_event_ctl(box, idx);
else
return uncore_msr_event_ctl(box, idx);
}
static inline
unsigned uncore_perf_ctr(struct intel_uncore_box *box, int idx)
{
if (box->pci_dev)
return uncore_pci_perf_ctr(box, idx);
else
return uncore_msr_perf_ctr(box, idx);
}
static inline int uncore_perf_ctr_bits(struct intel_uncore_box *box)
{
return box->pmu->type->perf_ctr_bits;
}
static inline int uncore_fixed_ctr_bits(struct intel_uncore_box *box)
{
return box->pmu->type->fixed_ctr_bits;
}
static inline int uncore_num_counters(struct intel_uncore_box *box)
{
return box->pmu->type->num_counters;
}
static inline void uncore_disable_box(struct intel_uncore_box *box)
{
if (box->pmu->type->ops->disable_box)
box->pmu->type->ops->disable_box(box);
}
static inline void uncore_enable_box(struct intel_uncore_box *box)
{
if (box->pmu->type->ops->enable_box)
box->pmu->type->ops->enable_box(box);
}
static inline void uncore_disable_event(struct intel_uncore_box *box,
struct perf_event *event)
{
box->pmu->type->ops->disable_event(box, event);
}
static inline void uncore_enable_event(struct intel_uncore_box *box,
struct perf_event *event)
{
box->pmu->type->ops->enable_event(box, event);
}
static inline u64 uncore_read_counter(struct intel_uncore_box *box,
struct perf_event *event)
{
return box->pmu->type->ops->read_counter(box, event);
}
static inline void uncore_box_init(struct intel_uncore_box *box)
{
if (!test_and_set_bit(UNCORE_BOX_FLAG_INITIATED, &box->flags)) {
if (box->pmu->type->ops->init_box)
box->pmu->type->ops->init_box(box);
}
}

View File

@ -895,8 +895,8 @@ static void p4_pmu_disable_pebs(void)
* So at moment let leave metrics turned on forever -- it's * So at moment let leave metrics turned on forever -- it's
* ok for now but need to be revisited! * ok for now but need to be revisited!
* *
* (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)0); * (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (u64)0);
* (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)0); * (void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)0);
*/ */
} }
@ -909,7 +909,7 @@ static inline void p4_pmu_disable_event(struct perf_event *event)
* state we need to clear P4_CCCR_OVF, otherwise interrupt get * state we need to clear P4_CCCR_OVF, otherwise interrupt get
* asserted again and again * asserted again and again
*/ */
(void)checking_wrmsrl(hwc->config_base, (void)wrmsrl_safe(hwc->config_base,
(u64)(p4_config_unpack_cccr(hwc->config)) & (u64)(p4_config_unpack_cccr(hwc->config)) &
~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED); ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED);
} }
@ -943,8 +943,8 @@ static void p4_pmu_enable_pebs(u64 config)
bind = &p4_pebs_bind_map[idx]; bind = &p4_pebs_bind_map[idx];
(void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs); (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs);
(void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert); (void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert);
} }
static void p4_pmu_enable_event(struct perf_event *event) static void p4_pmu_enable_event(struct perf_event *event)
@ -978,8 +978,8 @@ static void p4_pmu_enable_event(struct perf_event *event)
*/ */
p4_pmu_enable_pebs(hwc->config); p4_pmu_enable_pebs(hwc->config);
(void)checking_wrmsrl(escr_addr, escr_conf); (void)wrmsrl_safe(escr_addr, escr_conf);
(void)checking_wrmsrl(hwc->config_base, (void)wrmsrl_safe(hwc->config_base,
(cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE); (cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE);
} }
@ -1325,7 +1325,7 @@ __init int p4_pmu_init(void)
unsigned int low, high; unsigned int low, high;
/* If we get stripped -- indexing fails */ /* If we get stripped -- indexing fails */
BUILD_BUG_ON(ARCH_P4_MAX_CCCR > X86_PMC_MAX_GENERIC); BUILD_BUG_ON(ARCH_P4_MAX_CCCR > INTEL_PMC_MAX_GENERIC);
rdmsr(MSR_IA32_MISC_ENABLE, low, high); rdmsr(MSR_IA32_MISC_ENABLE, low, high);
if (!(low & (1 << 7))) { if (!(low & (1 << 7))) {

View File

@ -71,7 +71,7 @@ p6_pmu_disable_event(struct perf_event *event)
if (cpuc->enabled) if (cpuc->enabled)
val |= ARCH_PERFMON_EVENTSEL_ENABLE; val |= ARCH_PERFMON_EVENTSEL_ENABLE;
(void)checking_wrmsrl(hwc->config_base, val); (void)wrmsrl_safe(hwc->config_base, val);
} }
static void p6_pmu_enable_event(struct perf_event *event) static void p6_pmu_enable_event(struct perf_event *event)
@ -84,7 +84,7 @@ static void p6_pmu_enable_event(struct perf_event *event)
if (cpuc->enabled) if (cpuc->enabled)
val |= ARCH_PERFMON_EVENTSEL_ENABLE; val |= ARCH_PERFMON_EVENTSEL_ENABLE;
(void)checking_wrmsrl(hwc->config_base, val); (void)wrmsrl_safe(hwc->config_base, val);
} }
PMU_FORMAT_ATTR(event, "config:0-7" ); PMU_FORMAT_ATTR(event, "config:0-7" );

View File

@ -87,6 +87,7 @@
#include <asm/microcode.h> #include <asm/microcode.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/perf_event.h>
MODULE_DESCRIPTION("Microcode Update Driver"); MODULE_DESCRIPTION("Microcode Update Driver");
MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
@ -277,7 +278,6 @@ static int reload_for_cpu(int cpu)
struct ucode_cpu_info *uci = ucode_cpu_info + cpu; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
int err = 0; int err = 0;
mutex_lock(&microcode_mutex);
if (uci->valid) { if (uci->valid) {
enum ucode_state ustate; enum ucode_state ustate;
@ -288,7 +288,6 @@ static int reload_for_cpu(int cpu)
if (ustate == UCODE_ERROR) if (ustate == UCODE_ERROR)
err = -EINVAL; err = -EINVAL;
} }
mutex_unlock(&microcode_mutex);
return err; return err;
} }
@ -298,19 +297,31 @@ static ssize_t reload_store(struct device *dev,
const char *buf, size_t size) const char *buf, size_t size)
{ {
unsigned long val; unsigned long val;
int cpu = dev->id; int cpu;
ssize_t ret = 0; ssize_t ret = 0, tmp_ret;
ret = kstrtoul(buf, 0, &val); ret = kstrtoul(buf, 0, &val);
if (ret) if (ret)
return ret; return ret;
if (val == 1) { if (val != 1)
get_online_cpus(); return size;
if (cpu_online(cpu))
ret = reload_for_cpu(cpu); get_online_cpus();
put_online_cpus(); mutex_lock(&microcode_mutex);
for_each_online_cpu(cpu) {
tmp_ret = reload_for_cpu(cpu);
if (tmp_ret != 0)
pr_warn("Error reloading microcode on CPU %d\n", cpu);
/* save retval of the first encountered reload error */
if (!ret)
ret = tmp_ret;
} }
if (!ret)
perf_check_microcode();
mutex_unlock(&microcode_mutex);
put_online_cpus();
if (!ret) if (!ret)
ret = size; ret = size;
@ -339,7 +350,6 @@ static DEVICE_ATTR(version, 0400, version_show, NULL);
static DEVICE_ATTR(processor_flags, 0400, pf_show, NULL); static DEVICE_ATTR(processor_flags, 0400, pf_show, NULL);
static struct attribute *mc_default_attrs[] = { static struct attribute *mc_default_attrs[] = {
&dev_attr_reload.attr,
&dev_attr_version.attr, &dev_attr_version.attr,
&dev_attr_processor_flags.attr, &dev_attr_processor_flags.attr,
NULL NULL
@ -516,6 +526,16 @@ static const struct x86_cpu_id microcode_id[] = {
MODULE_DEVICE_TABLE(x86cpu, microcode_id); MODULE_DEVICE_TABLE(x86cpu, microcode_id);
#endif #endif
static struct attribute *cpu_root_microcode_attrs[] = {
&dev_attr_reload.attr,
NULL
};
static struct attribute_group cpu_root_microcode_group = {
.name = "microcode",
.attrs = cpu_root_microcode_attrs,
};
static int __init microcode_init(void) static int __init microcode_init(void)
{ {
struct cpuinfo_x86 *c = &cpu_data(0); struct cpuinfo_x86 *c = &cpu_data(0);
@ -540,16 +560,25 @@ static int __init microcode_init(void)
mutex_lock(&microcode_mutex); mutex_lock(&microcode_mutex);
error = subsys_interface_register(&mc_cpu_interface); error = subsys_interface_register(&mc_cpu_interface);
if (!error)
perf_check_microcode();
mutex_unlock(&microcode_mutex); mutex_unlock(&microcode_mutex);
put_online_cpus(); put_online_cpus();
if (error) if (error)
goto out_pdev; goto out_pdev;
error = sysfs_create_group(&cpu_subsys.dev_root->kobj,
&cpu_root_microcode_group);
if (error) {
pr_err("Error creating microcode group!\n");
goto out_driver;
}
error = microcode_dev_init(); error = microcode_dev_init();
if (error) if (error)
goto out_driver; goto out_ucode_group;
register_syscore_ops(&mc_syscore_ops); register_syscore_ops(&mc_syscore_ops);
register_hotcpu_notifier(&mc_cpu_notifier); register_hotcpu_notifier(&mc_cpu_notifier);
@ -559,7 +588,11 @@ static int __init microcode_init(void)
return 0; return 0;
out_driver: out_ucode_group:
sysfs_remove_group(&cpu_subsys.dev_root->kobj,
&cpu_root_microcode_group);
out_driver:
get_online_cpus(); get_online_cpus();
mutex_lock(&microcode_mutex); mutex_lock(&microcode_mutex);
@ -568,7 +601,7 @@ static int __init microcode_init(void)
mutex_unlock(&microcode_mutex); mutex_unlock(&microcode_mutex);
put_online_cpus(); put_online_cpus();
out_pdev: out_pdev:
platform_device_unregister(microcode_pdev); platform_device_unregister(microcode_pdev);
return error; return error;
@ -584,6 +617,9 @@ static void __exit microcode_exit(void)
unregister_hotcpu_notifier(&mc_cpu_notifier); unregister_hotcpu_notifier(&mc_cpu_notifier);
unregister_syscore_ops(&mc_syscore_ops); unregister_syscore_ops(&mc_syscore_ops);
sysfs_remove_group(&cpu_subsys.dev_root->kobj,
&cpu_root_microcode_group);
get_online_cpus(); get_online_cpus();
mutex_lock(&microcode_mutex); mutex_lock(&microcode_mutex);

View File

@ -352,9 +352,7 @@ struct pv_cpu_ops pv_cpu_ops = {
#endif #endif
.wbinvd = native_wbinvd, .wbinvd = native_wbinvd,
.read_msr = native_read_msr_safe, .read_msr = native_read_msr_safe,
.rdmsr_regs = native_rdmsr_safe_regs,
.write_msr = native_write_msr_safe, .write_msr = native_write_msr_safe,
.wrmsr_regs = native_wrmsr_safe_regs,
.read_tsc = native_read_tsc, .read_tsc = native_read_tsc,
.read_pmc = native_read_pmc, .read_pmc = native_read_pmc,
.read_tscp = native_read_tscp, .read_tscp = native_read_tscp,

View File

@ -466,7 +466,7 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
task->thread.gs = addr; task->thread.gs = addr;
if (doit) { if (doit) {
load_gs_index(0); load_gs_index(0);
ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr); ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, addr);
} }
} }
put_cpu(); put_cpu();
@ -494,7 +494,7 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
/* set the selector to 0 to not confuse /* set the selector to 0 to not confuse
__switch_to */ __switch_to */
loadsegment(fs, 0); loadsegment(fs, 0);
ret = checking_wrmsrl(MSR_FS_BASE, addr); ret = wrmsrl_safe(MSR_FS_BASE, addr);
} }
} }
put_cpu(); put_cpu();

View File

@ -409,9 +409,10 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,
* arch_uprobe_analyze_insn - instruction analysis including validity and fixups. * arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
* @mm: the probed address space. * @mm: the probed address space.
* @arch_uprobe: the probepoint information. * @arch_uprobe: the probepoint information.
* @addr: virtual address at which to install the probepoint
* Return 0 on success or a -ve number on error. * Return 0 on success or a -ve number on error.
*/ */
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
{ {
int ret; int ret;
struct insn insn; struct insn insn;

View File

@ -80,10 +80,10 @@ static inline struct kvm_pmc *get_fixed_pmc_idx(struct kvm_pmu *pmu, int idx)
static struct kvm_pmc *global_idx_to_pmc(struct kvm_pmu *pmu, int idx) static struct kvm_pmc *global_idx_to_pmc(struct kvm_pmu *pmu, int idx)
{ {
if (idx < X86_PMC_IDX_FIXED) if (idx < INTEL_PMC_IDX_FIXED)
return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + idx, MSR_P6_EVNTSEL0); return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + idx, MSR_P6_EVNTSEL0);
else else
return get_fixed_pmc_idx(pmu, idx - X86_PMC_IDX_FIXED); return get_fixed_pmc_idx(pmu, idx - INTEL_PMC_IDX_FIXED);
} }
void kvm_deliver_pmi(struct kvm_vcpu *vcpu) void kvm_deliver_pmi(struct kvm_vcpu *vcpu)
@ -291,7 +291,7 @@ static void reprogram_idx(struct kvm_pmu *pmu, int idx)
if (pmc_is_gp(pmc)) if (pmc_is_gp(pmc))
reprogram_gp_counter(pmc, pmc->eventsel); reprogram_gp_counter(pmc, pmc->eventsel);
else { else {
int fidx = idx - X86_PMC_IDX_FIXED; int fidx = idx - INTEL_PMC_IDX_FIXED;
reprogram_fixed_counter(pmc, reprogram_fixed_counter(pmc,
fixed_en_pmi(pmu->fixed_ctr_ctrl, fidx), fidx); fixed_en_pmi(pmu->fixed_ctr_ctrl, fidx), fidx);
} }
@ -452,7 +452,7 @@ void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu)
return; return;
pmu->nr_arch_gp_counters = min((int)(entry->eax >> 8) & 0xff, pmu->nr_arch_gp_counters = min((int)(entry->eax >> 8) & 0xff,
X86_PMC_MAX_GENERIC); INTEL_PMC_MAX_GENERIC);
pmu->counter_bitmask[KVM_PMC_GP] = pmu->counter_bitmask[KVM_PMC_GP] =
((u64)1 << ((entry->eax >> 16) & 0xff)) - 1; ((u64)1 << ((entry->eax >> 16) & 0xff)) - 1;
bitmap_len = (entry->eax >> 24) & 0xff; bitmap_len = (entry->eax >> 24) & 0xff;
@ -462,13 +462,13 @@ void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu)
pmu->nr_arch_fixed_counters = 0; pmu->nr_arch_fixed_counters = 0;
} else { } else {
pmu->nr_arch_fixed_counters = min((int)(entry->edx & 0x1f), pmu->nr_arch_fixed_counters = min((int)(entry->edx & 0x1f),
X86_PMC_MAX_FIXED); INTEL_PMC_MAX_FIXED);
pmu->counter_bitmask[KVM_PMC_FIXED] = pmu->counter_bitmask[KVM_PMC_FIXED] =
((u64)1 << ((entry->edx >> 5) & 0xff)) - 1; ((u64)1 << ((entry->edx >> 5) & 0xff)) - 1;
} }
pmu->global_ctrl = ((1 << pmu->nr_arch_gp_counters) - 1) | pmu->global_ctrl = ((1 << pmu->nr_arch_gp_counters) - 1) |
(((1ull << pmu->nr_arch_fixed_counters) - 1) << X86_PMC_IDX_FIXED); (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED);
pmu->global_ctrl_mask = ~pmu->global_ctrl; pmu->global_ctrl_mask = ~pmu->global_ctrl;
} }
@ -478,15 +478,15 @@ void kvm_pmu_init(struct kvm_vcpu *vcpu)
struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_pmu *pmu = &vcpu->arch.pmu;
memset(pmu, 0, sizeof(*pmu)); memset(pmu, 0, sizeof(*pmu));
for (i = 0; i < X86_PMC_MAX_GENERIC; i++) { for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
pmu->gp_counters[i].type = KVM_PMC_GP; pmu->gp_counters[i].type = KVM_PMC_GP;
pmu->gp_counters[i].vcpu = vcpu; pmu->gp_counters[i].vcpu = vcpu;
pmu->gp_counters[i].idx = i; pmu->gp_counters[i].idx = i;
} }
for (i = 0; i < X86_PMC_MAX_FIXED; i++) { for (i = 0; i < INTEL_PMC_MAX_FIXED; i++) {
pmu->fixed_counters[i].type = KVM_PMC_FIXED; pmu->fixed_counters[i].type = KVM_PMC_FIXED;
pmu->fixed_counters[i].vcpu = vcpu; pmu->fixed_counters[i].vcpu = vcpu;
pmu->fixed_counters[i].idx = i + X86_PMC_IDX_FIXED; pmu->fixed_counters[i].idx = i + INTEL_PMC_IDX_FIXED;
} }
init_irq_work(&pmu->irq_work, trigger_pmi); init_irq_work(&pmu->irq_work, trigger_pmi);
kvm_pmu_cpuid_update(vcpu); kvm_pmu_cpuid_update(vcpu);
@ -498,13 +498,13 @@ void kvm_pmu_reset(struct kvm_vcpu *vcpu)
int i; int i;
irq_work_sync(&pmu->irq_work); irq_work_sync(&pmu->irq_work);
for (i = 0; i < X86_PMC_MAX_GENERIC; i++) { for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
struct kvm_pmc *pmc = &pmu->gp_counters[i]; struct kvm_pmc *pmc = &pmu->gp_counters[i];
stop_counter(pmc); stop_counter(pmc);
pmc->counter = pmc->eventsel = 0; pmc->counter = pmc->eventsel = 0;
} }
for (i = 0; i < X86_PMC_MAX_FIXED; i++) for (i = 0; i < INTEL_PMC_MAX_FIXED; i++)
stop_counter(&pmu->fixed_counters[i]); stop_counter(&pmu->fixed_counters[i]);
pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status = pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status =

View File

@ -710,16 +710,6 @@ TRACE_EVENT(kvm_skinit,
__entry->rip, __entry->slb) __entry->rip, __entry->slb)
); );
#define __print_insn(insn, ilen) ({ \
int i; \
const char *ret = p->buffer + p->len; \
\
for (i = 0; i < ilen; ++i) \
trace_seq_printf(p, " %02x", insn[i]); \
trace_seq_printf(p, "%c", 0); \
ret; \
})
#define KVM_EMUL_INSN_F_CR0_PE (1 << 0) #define KVM_EMUL_INSN_F_CR0_PE (1 << 0)
#define KVM_EMUL_INSN_F_EFL_VM (1 << 1) #define KVM_EMUL_INSN_F_EFL_VM (1 << 1)
#define KVM_EMUL_INSN_F_CS_D (1 << 2) #define KVM_EMUL_INSN_F_CS_D (1 << 2)
@ -786,7 +776,7 @@ TRACE_EVENT(kvm_emulate_insn,
TP_printk("%x:%llx:%s (%s)%s", TP_printk("%x:%llx:%s (%s)%s",
__entry->csbase, __entry->rip, __entry->csbase, __entry->rip,
__print_insn(__entry->insn, __entry->len), __print_hex(__entry->insn, __entry->len),
__print_symbolic(__entry->flags, __print_symbolic(__entry->flags,
kvm_trace_symbol_emul_flags), kvm_trace_symbol_emul_flags),
__entry->failed ? " failed" : "" __entry->failed ? " failed" : ""

View File

@ -1,5 +1,5 @@
#include <linux/module.h> #include <linux/module.h>
#include <asm/msr.h> #include <asm/msr.h>
EXPORT_SYMBOL(native_rdmsr_safe_regs); EXPORT_SYMBOL(rdmsr_safe_regs);
EXPORT_SYMBOL(native_wrmsr_safe_regs); EXPORT_SYMBOL(wrmsr_safe_regs);

View File

@ -6,13 +6,13 @@
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
/* /*
* int native_{rdmsr,wrmsr}_safe_regs(u32 gprs[8]); * int {rdmsr,wrmsr}_safe_regs(u32 gprs[8]);
* *
* reg layout: u32 gprs[eax, ecx, edx, ebx, esp, ebp, esi, edi] * reg layout: u32 gprs[eax, ecx, edx, ebx, esp, ebp, esi, edi]
* *
*/ */
.macro op_safe_regs op .macro op_safe_regs op
ENTRY(native_\op\()_safe_regs) ENTRY(\op\()_safe_regs)
CFI_STARTPROC CFI_STARTPROC
pushq_cfi %rbx pushq_cfi %rbx
pushq_cfi %rbp pushq_cfi %rbp
@ -45,13 +45,13 @@ ENTRY(native_\op\()_safe_regs)
_ASM_EXTABLE(1b, 3b) _ASM_EXTABLE(1b, 3b)
CFI_ENDPROC CFI_ENDPROC
ENDPROC(native_\op\()_safe_regs) ENDPROC(\op\()_safe_regs)
.endm .endm
#else /* X86_32 */ #else /* X86_32 */
.macro op_safe_regs op .macro op_safe_regs op
ENTRY(native_\op\()_safe_regs) ENTRY(\op\()_safe_regs)
CFI_STARTPROC CFI_STARTPROC
pushl_cfi %ebx pushl_cfi %ebx
pushl_cfi %ebp pushl_cfi %ebp
@ -92,7 +92,7 @@ ENTRY(native_\op\()_safe_regs)
_ASM_EXTABLE(1b, 3b) _ASM_EXTABLE(1b, 3b)
CFI_ENDPROC CFI_ENDPROC
ENDPROC(native_\op\()_safe_regs) ENDPROC(\op\()_safe_regs)
.endm .endm
#endif #endif

View File

@ -312,7 +312,7 @@ static int op_amd_fill_in_addresses(struct op_msrs * const msrs)
goto fail; goto fail;
} }
/* both registers must be reserved */ /* both registers must be reserved */
if (num_counters == AMD64_NUM_COUNTERS_F15H) { if (num_counters == AMD64_NUM_COUNTERS_CORE) {
msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1); msrs->counters[i].addr = MSR_F15H_PERF_CTR + (i << 1);
msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1); msrs->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1);
} else { } else {
@ -514,7 +514,7 @@ static int op_amd_init(struct oprofile_operations *ops)
ops->create_files = setup_ibs_files; ops->create_files = setup_ibs_files;
if (boot_cpu_data.x86 == 0x15) { if (boot_cpu_data.x86 == 0x15) {
num_counters = AMD64_NUM_COUNTERS_F15H; num_counters = AMD64_NUM_COUNTERS_CORE;
} else { } else {
num_counters = AMD64_NUM_COUNTERS; num_counters = AMD64_NUM_COUNTERS;
} }

View File

@ -205,9 +205,9 @@ void syscall32_cpu_init(void)
{ {
/* Load these always in case some future AMD CPU supports /* Load these always in case some future AMD CPU supports
SYSENTER from compat mode too. */ SYSENTER from compat mode too. */
checking_wrmsrl(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS); wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS);
checking_wrmsrl(MSR_IA32_SYSENTER_ESP, 0ULL); wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL);
checking_wrmsrl(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target); wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target);
wrmsrl(MSR_CSTAR, ia32_cstar_target); wrmsrl(MSR_CSTAR, ia32_cstar_target);
} }

View File

@ -1124,9 +1124,7 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
.wbinvd = native_wbinvd, .wbinvd = native_wbinvd,
.read_msr = native_read_msr_safe, .read_msr = native_read_msr_safe,
.rdmsr_regs = native_rdmsr_safe_regs,
.write_msr = xen_write_msr_safe, .write_msr = xen_write_msr_safe,
.wrmsr_regs = native_wrmsr_safe_regs,
.read_tsc = native_read_tsc, .read_tsc = native_read_tsc,
.read_pmc = native_read_pmc, .read_pmc = native_read_pmc,

View File

@ -1,5 +1,6 @@
/* /*
* Copyright 2010 ARM Ltd. * Copyright 2010 ARM Ltd.
* Copyright 2012 Advanced Micro Devices, Inc., Robert Richter
* *
* Perf-events backend for OProfile. * Perf-events backend for OProfile.
*/ */
@ -25,7 +26,7 @@ static int oprofile_perf_enabled;
static DEFINE_MUTEX(oprofile_perf_mutex); static DEFINE_MUTEX(oprofile_perf_mutex);
static struct op_counter_config *counter_config; static struct op_counter_config *counter_config;
static struct perf_event **perf_events[NR_CPUS]; static DEFINE_PER_CPU(struct perf_event **, perf_events);
static int num_counters; static int num_counters;
/* /*
@ -38,7 +39,7 @@ static void op_overflow_handler(struct perf_event *event,
u32 cpu = smp_processor_id(); u32 cpu = smp_processor_id();
for (id = 0; id < num_counters; ++id) for (id = 0; id < num_counters; ++id)
if (perf_events[cpu][id] == event) if (per_cpu(perf_events, cpu)[id] == event)
break; break;
if (id != num_counters) if (id != num_counters)
@ -74,7 +75,7 @@ static int op_create_counter(int cpu, int event)
{ {
struct perf_event *pevent; struct perf_event *pevent;
if (!counter_config[event].enabled || perf_events[cpu][event]) if (!counter_config[event].enabled || per_cpu(perf_events, cpu)[event])
return 0; return 0;
pevent = perf_event_create_kernel_counter(&counter_config[event].attr, pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
@ -91,18 +92,18 @@ static int op_create_counter(int cpu, int event)
return -EBUSY; return -EBUSY;
} }
perf_events[cpu][event] = pevent; per_cpu(perf_events, cpu)[event] = pevent;
return 0; return 0;
} }
static void op_destroy_counter(int cpu, int event) static void op_destroy_counter(int cpu, int event)
{ {
struct perf_event *pevent = perf_events[cpu][event]; struct perf_event *pevent = per_cpu(perf_events, cpu)[event];
if (pevent) { if (pevent) {
perf_event_release_kernel(pevent); perf_event_release_kernel(pevent);
perf_events[cpu][event] = NULL; per_cpu(perf_events, cpu)[event] = NULL;
} }
} }
@ -257,12 +258,12 @@ void oprofile_perf_exit(void)
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
for (id = 0; id < num_counters; ++id) { for (id = 0; id < num_counters; ++id) {
event = perf_events[cpu][id]; event = per_cpu(perf_events, cpu)[id];
if (event) if (event)
perf_event_release_kernel(event); perf_event_release_kernel(event);
} }
kfree(perf_events[cpu]); kfree(per_cpu(perf_events, cpu));
} }
kfree(counter_config); kfree(counter_config);
@ -277,8 +278,6 @@ int __init oprofile_perf_init(struct oprofile_operations *ops)
if (ret) if (ret)
return ret; return ret;
memset(&perf_events, 0, sizeof(perf_events));
num_counters = perf_num_counters(); num_counters = perf_num_counters();
if (num_counters <= 0) { if (num_counters <= 0) {
pr_info("oprofile: no performance counters\n"); pr_info("oprofile: no performance counters\n");
@ -298,9 +297,9 @@ int __init oprofile_perf_init(struct oprofile_operations *ops)
} }
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
perf_events[cpu] = kcalloc(num_counters, per_cpu(perf_events, cpu) = kcalloc(num_counters,
sizeof(struct perf_event *), GFP_KERNEL); sizeof(struct perf_event *), GFP_KERNEL);
if (!perf_events[cpu]) { if (!per_cpu(perf_events, cpu)) {
pr_info("oprofile: failed to allocate %d perf events " pr_info("oprofile: failed to allocate %d perf events "
"for cpu %d\n", num_counters, cpu); "for cpu %d\n", num_counters, cpu);
ret = -ENOMEM; ret = -ENOMEM;

View File

@ -65,7 +65,7 @@ struct trace_iterator {
void *private; void *private;
int cpu_file; int cpu_file;
struct mutex mutex; struct mutex mutex;
struct ring_buffer_iter *buffer_iter[NR_CPUS]; struct ring_buffer_iter **buffer_iter;
unsigned long iter_flags; unsigned long iter_flags;
/* trace_seq for __print_flags() and __print_symbolic() etc. */ /* trace_seq for __print_flags() and __print_symbolic() etc. */
@ -207,6 +207,9 @@ struct ftrace_event_call {
* bit 1: enabled * bit 1: enabled
* bit 2: filter_active * bit 2: filter_active
* bit 3: enabled cmd record * bit 3: enabled cmd record
* bit 4: allow trace by non root (cap any)
* bit 5: failed to apply filter
* bit 6: ftrace internal event (do not enable)
* *
* Changes to flags must hold the event_mutex. * Changes to flags must hold the event_mutex.
* *

View File

@ -42,8 +42,7 @@
* allowed. * allowed.
* *
* Not initializing the key (static data is initialized to 0s anyway) is the * Not initializing the key (static data is initialized to 0s anyway) is the
* same as using STATIC_KEY_INIT_FALSE and static_key_false() is * same as using STATIC_KEY_INIT_FALSE.
* equivalent with static_branch().
* *
*/ */
@ -107,12 +106,6 @@ static __always_inline bool static_key_true(struct static_key *key)
return !static_key_false(key); return !static_key_false(key);
} }
/* Deprecated. Please use 'static_key_false() instead. */
static __always_inline bool static_branch(struct static_key *key)
{
return arch_static_branch(key);
}
extern struct jump_entry __start___jump_table[]; extern struct jump_entry __start___jump_table[];
extern struct jump_entry __stop___jump_table[]; extern struct jump_entry __stop___jump_table[];
@ -166,14 +159,6 @@ static __always_inline bool static_key_true(struct static_key *key)
return false; return false;
} }
/* Deprecated. Please use 'static_key_false() instead. */
static __always_inline bool static_branch(struct static_key *key)
{
if (unlikely(atomic_read(&key->enabled)) > 0)
return true;
return false;
}
static inline void static_key_slow_inc(struct static_key *key) static inline void static_key_slow_inc(struct static_key *key)
{ {
atomic_inc(&key->enabled); atomic_inc(&key->enabled);

View File

@ -2755,6 +2755,17 @@
#define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27 #define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27
#define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e #define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e
#define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f #define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f
#define PCI_DEVICE_ID_INTEL_UNC_HA 0x3c46
#define PCI_DEVICE_ID_INTEL_UNC_IMC0 0x3cb0
#define PCI_DEVICE_ID_INTEL_UNC_IMC1 0x3cb1
#define PCI_DEVICE_ID_INTEL_UNC_IMC2 0x3cb4
#define PCI_DEVICE_ID_INTEL_UNC_IMC3 0x3cb5
#define PCI_DEVICE_ID_INTEL_UNC_QPI0 0x3c41
#define PCI_DEVICE_ID_INTEL_UNC_QPI1 0x3c42
#define PCI_DEVICE_ID_INTEL_UNC_R2PCIE 0x3c43
#define PCI_DEVICE_ID_INTEL_UNC_R3QPI0 0x3c44
#define PCI_DEVICE_ID_INTEL_UNC_R3QPI1 0x3c45
#define PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX 0x3ce0
#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f
#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0
#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5

View File

@ -677,6 +677,7 @@ struct hw_perf_event {
u64 last_tag; u64 last_tag;
unsigned long config_base; unsigned long config_base;
unsigned long event_base; unsigned long event_base;
int event_base_rdpmc;
int idx; int idx;
int last_cpu; int last_cpu;
@ -1106,6 +1107,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr,
struct task_struct *task, struct task_struct *task,
perf_overflow_handler_t callback, perf_overflow_handler_t callback,
void *context); void *context);
extern void perf_pmu_migrate_context(struct pmu *pmu,
int src_cpu, int dst_cpu);
extern u64 perf_event_read_value(struct perf_event *event, extern u64 perf_event_read_value(struct perf_event *event,
u64 *enabled, u64 *running); u64 *enabled, u64 *running);

View File

@ -1581,7 +1581,6 @@ struct task_struct {
#endif #endif
#ifdef CONFIG_UPROBES #ifdef CONFIG_UPROBES
struct uprobe_task *utask; struct uprobe_task *utask;
int uprobe_srcu_id;
#endif #endif
}; };

View File

@ -153,7 +153,7 @@ static inline void tracepoint_synchronize_unregister(void)
} \ } \
static inline void trace_##name##_rcuidle(proto) \ static inline void trace_##name##_rcuidle(proto) \
{ \ { \
if (static_branch(&__tracepoint_##name.key)) \ if (static_key_false(&__tracepoint_##name.key)) \
__DO_TRACE(&__tracepoint_##name, \ __DO_TRACE(&__tracepoint_##name, \
TP_PROTO(data_proto), \ TP_PROTO(data_proto), \
TP_ARGS(data_args), \ TP_ARGS(data_args), \

View File

@ -571,6 +571,7 @@ static inline void ftrace_test_probe_##call(void) \
#undef __print_flags #undef __print_flags
#undef __print_symbolic #undef __print_symbolic
#undef __print_hex
#undef __get_dynamic_array #undef __get_dynamic_array
#undef __get_str #undef __get_str

View File

@ -1645,6 +1645,8 @@ perf_install_in_context(struct perf_event_context *ctx,
lockdep_assert_held(&ctx->mutex); lockdep_assert_held(&ctx->mutex);
event->ctx = ctx; event->ctx = ctx;
if (event->cpu != -1)
event->cpu = cpu;
if (!task) { if (!task) {
/* /*
@ -6252,6 +6254,8 @@ SYSCALL_DEFINE5(perf_event_open,
} }
} }
get_online_cpus();
event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, event = perf_event_alloc(&attr, cpu, task, group_leader, NULL,
NULL, NULL); NULL, NULL);
if (IS_ERR(event)) { if (IS_ERR(event)) {
@ -6304,7 +6308,7 @@ SYSCALL_DEFINE5(perf_event_open,
/* /*
* Get the target context (task or percpu): * Get the target context (task or percpu):
*/ */
ctx = find_get_context(pmu, task, cpu); ctx = find_get_context(pmu, task, event->cpu);
if (IS_ERR(ctx)) { if (IS_ERR(ctx)) {
err = PTR_ERR(ctx); err = PTR_ERR(ctx);
goto err_alloc; goto err_alloc;
@ -6377,20 +6381,23 @@ SYSCALL_DEFINE5(perf_event_open,
mutex_lock(&ctx->mutex); mutex_lock(&ctx->mutex);
if (move_group) { if (move_group) {
perf_install_in_context(ctx, group_leader, cpu); synchronize_rcu();
perf_install_in_context(ctx, group_leader, event->cpu);
get_ctx(ctx); get_ctx(ctx);
list_for_each_entry(sibling, &group_leader->sibling_list, list_for_each_entry(sibling, &group_leader->sibling_list,
group_entry) { group_entry) {
perf_install_in_context(ctx, sibling, cpu); perf_install_in_context(ctx, sibling, event->cpu);
get_ctx(ctx); get_ctx(ctx);
} }
} }
perf_install_in_context(ctx, event, cpu); perf_install_in_context(ctx, event, event->cpu);
++ctx->generation; ++ctx->generation;
perf_unpin_context(ctx); perf_unpin_context(ctx);
mutex_unlock(&ctx->mutex); mutex_unlock(&ctx->mutex);
put_online_cpus();
event->owner = current; event->owner = current;
mutex_lock(&current->perf_event_mutex); mutex_lock(&current->perf_event_mutex);
@ -6419,6 +6426,7 @@ SYSCALL_DEFINE5(perf_event_open,
err_alloc: err_alloc:
free_event(event); free_event(event);
err_task: err_task:
put_online_cpus();
if (task) if (task)
put_task_struct(task); put_task_struct(task);
err_group_fd: err_group_fd:
@ -6479,6 +6487,39 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
} }
EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);
void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
{
struct perf_event_context *src_ctx;
struct perf_event_context *dst_ctx;
struct perf_event *event, *tmp;
LIST_HEAD(events);
src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx;
dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx;
mutex_lock(&src_ctx->mutex);
list_for_each_entry_safe(event, tmp, &src_ctx->event_list,
event_entry) {
perf_remove_from_context(event);
put_ctx(src_ctx);
list_add(&event->event_entry, &events);
}
mutex_unlock(&src_ctx->mutex);
synchronize_rcu();
mutex_lock(&dst_ctx->mutex);
list_for_each_entry_safe(event, tmp, &events, event_entry) {
list_del(&event->event_entry);
if (event->state >= PERF_EVENT_STATE_OFF)
event->state = PERF_EVENT_STATE_INACTIVE;
perf_install_in_context(dst_ctx, event, dst_cpu);
get_ctx(dst_ctx);
}
mutex_unlock(&dst_ctx->mutex);
}
EXPORT_SYMBOL_GPL(perf_pmu_migrate_context);
static void sync_child_event(struct perf_event *child_event, static void sync_child_event(struct perf_event *child_event,
struct task_struct *child) struct task_struct *child)
{ {

View File

@ -38,13 +38,29 @@
#define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES) #define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES)
#define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE #define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE
static struct srcu_struct uprobes_srcu;
static struct rb_root uprobes_tree = RB_ROOT; static struct rb_root uprobes_tree = RB_ROOT;
static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */
#define UPROBES_HASH_SZ 13 #define UPROBES_HASH_SZ 13
/*
* We need separate register/unregister and mmap/munmap lock hashes because
* of mmap_sem nesting.
*
* uprobe_register() needs to install probes on (potentially) all processes
* and thus needs to acquire multiple mmap_sems (consequtively, not
* concurrently), whereas uprobe_mmap() is called while holding mmap_sem
* for the particular process doing the mmap.
*
* uprobe_register()->register_for_each_vma() needs to drop/acquire mmap_sem
* because of lock order against i_mmap_mutex. This means there's a hole in
* the register vma iteration where a mmap() can happen.
*
* Thus uprobe_register() can race with uprobe_mmap() and we can try and
* install a probe where one is already installed.
*/
/* serialize (un)register */ /* serialize (un)register */
static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
@ -61,17 +77,6 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
*/ */
static atomic_t uprobe_events = ATOMIC_INIT(0); static atomic_t uprobe_events = ATOMIC_INIT(0);
/*
* Maintain a temporary per vma info that can be used to search if a vma
* has already been handled. This structure is introduced since extending
* vm_area_struct wasnt recommended.
*/
struct vma_info {
struct list_head probe_list;
struct mm_struct *mm;
loff_t vaddr;
};
struct uprobe { struct uprobe {
struct rb_node rb_node; /* node in the rb tree */ struct rb_node rb_node; /* node in the rb tree */
atomic_t ref; atomic_t ref;
@ -100,7 +105,8 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register)
if (!is_register) if (!is_register)
return true; return true;
if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) if ((vma->vm_flags & (VM_HUGETLB|VM_READ|VM_WRITE|VM_EXEC|VM_SHARED))
== (VM_READ|VM_EXEC))
return true; return true;
return false; return false;
@ -129,33 +135,17 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset)
static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage)
{ {
struct mm_struct *mm = vma->vm_mm; struct mm_struct *mm = vma->vm_mm;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep;
spinlock_t *ptl;
unsigned long addr; unsigned long addr;
int err = -EFAULT; spinlock_t *ptl;
pte_t *ptep;
addr = page_address_in_vma(page, vma); addr = page_address_in_vma(page, vma);
if (addr == -EFAULT) if (addr == -EFAULT)
goto out; return -EFAULT;
pgd = pgd_offset(mm, addr); ptep = page_check_address(page, mm, addr, &ptl, 0);
if (!pgd_present(*pgd))
goto out;
pud = pud_offset(pgd, addr);
if (!pud_present(*pud))
goto out;
pmd = pmd_offset(pud, addr);
if (!pmd_present(*pmd))
goto out;
ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
if (!ptep) if (!ptep)
goto out; return -EAGAIN;
get_page(kpage); get_page(kpage);
page_add_new_anon_rmap(kpage, vma, addr); page_add_new_anon_rmap(kpage, vma, addr);
@ -174,10 +164,8 @@ static int __replace_page(struct vm_area_struct *vma, struct page *page, struct
try_to_free_swap(page); try_to_free_swap(page);
put_page(page); put_page(page);
pte_unmap_unlock(ptep, ptl); pte_unmap_unlock(ptep, ptl);
err = 0;
out: return 0;
return err;
} }
/** /**
@ -222,9 +210,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
void *vaddr_old, *vaddr_new; void *vaddr_old, *vaddr_new;
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct uprobe *uprobe; struct uprobe *uprobe;
loff_t addr;
int ret; int ret;
retry:
/* Read the page with vaddr into memory */ /* Read the page with vaddr into memory */
ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma);
if (ret <= 0) if (ret <= 0)
@ -246,10 +233,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
if (mapping != vma->vm_file->f_mapping) if (mapping != vma->vm_file->f_mapping)
goto put_out; goto put_out;
addr = vma_address(vma, uprobe->offset);
if (vaddr != (unsigned long)addr)
goto put_out;
ret = -ENOMEM; ret = -ENOMEM;
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
if (!new_page) if (!new_page)
@ -267,11 +250,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
vaddr_new = kmap_atomic(new_page); vaddr_new = kmap_atomic(new_page);
memcpy(vaddr_new, vaddr_old, PAGE_SIZE); memcpy(vaddr_new, vaddr_old, PAGE_SIZE);
memcpy(vaddr_new + (vaddr & ~PAGE_MASK), &opcode, UPROBE_SWBP_INSN_SIZE);
/* poke the new insn in, ASSUMES we don't cross page boundary */
vaddr &= ~PAGE_MASK;
BUG_ON(vaddr + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
memcpy(vaddr_new + vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_new);
kunmap_atomic(vaddr_old); kunmap_atomic(vaddr_old);
@ -291,6 +270,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
put_out: put_out:
put_page(old_page); put_page(old_page);
if (unlikely(ret == -EAGAIN))
goto retry;
return ret; return ret;
} }
@ -312,7 +293,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_
void *vaddr_new; void *vaddr_new;
int ret; int ret;
ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &page, NULL); ret = get_user_pages(NULL, mm, vaddr, 1, 0, 1, &page, NULL);
if (ret <= 0) if (ret <= 0)
return ret; return ret;
@ -333,10 +314,20 @@ static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr)
uprobe_opcode_t opcode; uprobe_opcode_t opcode;
int result; int result;
if (current->mm == mm) {
pagefault_disable();
result = __copy_from_user_inatomic(&opcode, (void __user*)vaddr,
sizeof(opcode));
pagefault_enable();
if (likely(result == 0))
goto out;
}
result = read_opcode(mm, vaddr, &opcode); result = read_opcode(mm, vaddr, &opcode);
if (result) if (result)
return result; return result;
out:
if (is_swbp_insn(&opcode)) if (is_swbp_insn(&opcode))
return 1; return 1;
@ -355,7 +346,9 @@ static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr)
int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
{ {
int result; int result;
/*
* See the comment near uprobes_hash().
*/
result = is_swbp_at_addr(mm, vaddr); result = is_swbp_at_addr(mm, vaddr);
if (result == 1) if (result == 1)
return -EEXIST; return -EEXIST;
@ -520,7 +513,6 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset)
uprobe->inode = igrab(inode); uprobe->inode = igrab(inode);
uprobe->offset = offset; uprobe->offset = offset;
init_rwsem(&uprobe->consumer_rwsem); init_rwsem(&uprobe->consumer_rwsem);
INIT_LIST_HEAD(&uprobe->pending_list);
/* add to uprobes_tree, sorted on inode:offset */ /* add to uprobes_tree, sorted on inode:offset */
cur_uprobe = insert_uprobe(uprobe); cur_uprobe = insert_uprobe(uprobe);
@ -588,20 +580,22 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)
} }
static int static int
__copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insn, __copy_insn(struct address_space *mapping, struct file *filp, char *insn,
unsigned long nbytes, unsigned long offset) unsigned long nbytes, loff_t offset)
{ {
struct file *filp = vma->vm_file;
struct page *page; struct page *page;
void *vaddr; void *vaddr;
unsigned long off1; unsigned long off;
unsigned long idx; pgoff_t idx;
if (!filp) if (!filp)
return -EINVAL; return -EINVAL;
idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT); if (!mapping->a_ops->readpage)
off1 = offset &= ~PAGE_MASK; return -EIO;
idx = offset >> PAGE_CACHE_SHIFT;
off = offset & ~PAGE_MASK;
/* /*
* Ensure that the page that has the original instruction is * Ensure that the page that has the original instruction is
@ -612,22 +606,20 @@ __copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *ins
return PTR_ERR(page); return PTR_ERR(page);
vaddr = kmap_atomic(page); vaddr = kmap_atomic(page);
memcpy(insn, vaddr + off1, nbytes); memcpy(insn, vaddr + off, nbytes);
kunmap_atomic(vaddr); kunmap_atomic(vaddr);
page_cache_release(page); page_cache_release(page);
return 0; return 0;
} }
static int static int copy_insn(struct uprobe *uprobe, struct file *filp)
copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)
{ {
struct address_space *mapping; struct address_space *mapping;
unsigned long nbytes; unsigned long nbytes;
int bytes; int bytes;
addr &= ~PAGE_MASK; nbytes = PAGE_SIZE - (uprobe->offset & ~PAGE_MASK);
nbytes = PAGE_SIZE - addr;
mapping = uprobe->inode->i_mapping; mapping = uprobe->inode->i_mapping;
/* Instruction at end of binary; copy only available bytes */ /* Instruction at end of binary; copy only available bytes */
@ -638,13 +630,13 @@ copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)
/* Instruction at the page-boundary; copy bytes in second page */ /* Instruction at the page-boundary; copy bytes in second page */
if (nbytes < bytes) { if (nbytes < bytes) {
if (__copy_insn(mapping, vma, uprobe->arch.insn + nbytes, int err = __copy_insn(mapping, filp, uprobe->arch.insn + nbytes,
bytes - nbytes, uprobe->offset + nbytes)) bytes - nbytes, uprobe->offset + nbytes);
return -ENOMEM; if (err)
return err;
bytes = nbytes; bytes = nbytes;
} }
return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); return __copy_insn(mapping, filp, uprobe->arch.insn, bytes, uprobe->offset);
} }
/* /*
@ -672,9 +664,8 @@ copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)
*/ */
static int static int
install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
struct vm_area_struct *vma, loff_t vaddr) struct vm_area_struct *vma, unsigned long vaddr)
{ {
unsigned long addr;
int ret; int ret;
/* /*
@ -687,20 +678,22 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
if (!uprobe->consumers) if (!uprobe->consumers)
return -EEXIST; return -EEXIST;
addr = (unsigned long)vaddr;
if (!(uprobe->flags & UPROBE_COPY_INSN)) { if (!(uprobe->flags & UPROBE_COPY_INSN)) {
ret = copy_insn(uprobe, vma, addr); ret = copy_insn(uprobe, vma->vm_file);
if (ret) if (ret)
return ret; return ret;
if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn))
return -EEXIST; return -ENOTSUPP;
ret = arch_uprobe_analyze_insn(&uprobe->arch, mm); ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr);
if (ret) if (ret)
return ret; return ret;
/* write_opcode() assumes we don't cross page boundary */
BUG_ON((uprobe->offset & ~PAGE_MASK) +
UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
uprobe->flags |= UPROBE_COPY_INSN; uprobe->flags |= UPROBE_COPY_INSN;
} }
@ -713,7 +706,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
* Hence increment before and decrement on failure. * Hence increment before and decrement on failure.
*/ */
atomic_inc(&mm->uprobes_state.count); atomic_inc(&mm->uprobes_state.count);
ret = set_swbp(&uprobe->arch, mm, addr); ret = set_swbp(&uprobe->arch, mm, vaddr);
if (ret) if (ret)
atomic_dec(&mm->uprobes_state.count); atomic_dec(&mm->uprobes_state.count);
@ -721,27 +714,21 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
} }
static void static void
remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr)
{ {
if (!set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true)) if (!set_orig_insn(&uprobe->arch, mm, vaddr, true))
atomic_dec(&mm->uprobes_state.count); atomic_dec(&mm->uprobes_state.count);
} }
/* /*
* There could be threads that have hit the breakpoint and are entering the * There could be threads that have already hit the breakpoint. They
* notifier code and trying to acquire the uprobes_treelock. The thread * will recheck the current insn and restart if find_uprobe() fails.
* calling delete_uprobe() that is removing the uprobe from the rb_tree can * See find_active_uprobe().
* race with these threads and might acquire the uprobes_treelock compared
* to some of the breakpoint hit threads. In such a case, the breakpoint
* hit threads will not find the uprobe. The current unregistering thread
* waits till all other threads have hit a breakpoint, to acquire the
* uprobes_treelock before the uprobe is removed from the rbtree.
*/ */
static void delete_uprobe(struct uprobe *uprobe) static void delete_uprobe(struct uprobe *uprobe)
{ {
unsigned long flags; unsigned long flags;
synchronize_srcu(&uprobes_srcu);
spin_lock_irqsave(&uprobes_treelock, flags); spin_lock_irqsave(&uprobes_treelock, flags);
rb_erase(&uprobe->rb_node, &uprobes_tree); rb_erase(&uprobe->rb_node, &uprobes_tree);
spin_unlock_irqrestore(&uprobes_treelock, flags); spin_unlock_irqrestore(&uprobes_treelock, flags);
@ -750,139 +737,135 @@ static void delete_uprobe(struct uprobe *uprobe)
atomic_dec(&uprobe_events); atomic_dec(&uprobe_events);
} }
static struct vma_info * struct map_info {
__find_next_vma_info(struct address_space *mapping, struct list_head *head, struct map_info *next;
struct vma_info *vi, loff_t offset, bool is_register) struct mm_struct *mm;
unsigned long vaddr;
};
static inline struct map_info *free_map_info(struct map_info *info)
{ {
struct map_info *next = info->next;
kfree(info);
return next;
}
static struct map_info *
build_map_info(struct address_space *mapping, loff_t offset, bool is_register)
{
unsigned long pgoff = offset >> PAGE_SHIFT;
struct prio_tree_iter iter; struct prio_tree_iter iter;
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct vma_info *tmpvi; struct map_info *curr = NULL;
unsigned long pgoff; struct map_info *prev = NULL;
int existing_vma; struct map_info *info;
loff_t vaddr; int more = 0;
pgoff = offset >> PAGE_SHIFT;
again:
mutex_lock(&mapping->i_mmap_mutex);
vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
if (!valid_vma(vma, is_register)) if (!valid_vma(vma, is_register))
continue; continue;
existing_vma = 0; if (!prev && !more) {
vaddr = vma_address(vma, offset); /*
* Needs GFP_NOWAIT to avoid i_mmap_mutex recursion through
list_for_each_entry(tmpvi, head, probe_list) { * reclaim. This is optimistic, no harm done if it fails.
if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { */
existing_vma = 1; prev = kmalloc(sizeof(struct map_info),
break; GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN);
} if (prev)
prev->next = NULL;
}
if (!prev) {
more++;
continue;
} }
/* if (!atomic_inc_not_zero(&vma->vm_mm->mm_users))
* Another vma needs a probe to be installed. However skip continue;
* installing the probe if the vma is about to be unlinked.
*/
if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) {
vi->mm = vma->vm_mm;
vi->vaddr = vaddr;
list_add(&vi->probe_list, head);
return vi; info = prev;
} prev = prev->next;
info->next = curr;
curr = info;
info->mm = vma->vm_mm;
info->vaddr = vma_address(vma, offset);
} }
return NULL;
}
/*
* Iterate in the rmap prio tree and find a vma where a probe has not
* yet been inserted.
*/
static struct vma_info *
find_next_vma_info(struct address_space *mapping, struct list_head *head,
loff_t offset, bool is_register)
{
struct vma_info *vi, *retvi;
vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL);
if (!vi)
return ERR_PTR(-ENOMEM);
mutex_lock(&mapping->i_mmap_mutex);
retvi = __find_next_vma_info(mapping, head, vi, offset, is_register);
mutex_unlock(&mapping->i_mmap_mutex); mutex_unlock(&mapping->i_mmap_mutex);
if (!retvi) if (!more)
kfree(vi); goto out;
return retvi; prev = curr;
while (curr) {
mmput(curr->mm);
curr = curr->next;
}
do {
info = kmalloc(sizeof(struct map_info), GFP_KERNEL);
if (!info) {
curr = ERR_PTR(-ENOMEM);
goto out;
}
info->next = prev;
prev = info;
} while (--more);
goto again;
out:
while (prev)
prev = free_map_info(prev);
return curr;
} }
static int register_for_each_vma(struct uprobe *uprobe, bool is_register) static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
{ {
struct list_head try_list; struct map_info *info;
struct vm_area_struct *vma; int err = 0;
struct address_space *mapping;
struct vma_info *vi, *tmpvi;
struct mm_struct *mm;
loff_t vaddr;
int ret;
mapping = uprobe->inode->i_mapping; info = build_map_info(uprobe->inode->i_mapping,
INIT_LIST_HEAD(&try_list); uprobe->offset, is_register);
if (IS_ERR(info))
return PTR_ERR(info);
ret = 0; while (info) {
struct mm_struct *mm = info->mm;
struct vm_area_struct *vma;
for (;;) { if (err)
vi = find_next_vma_info(mapping, &try_list, uprobe->offset, is_register); goto free;
if (!vi)
break;
if (IS_ERR(vi)) { down_write(&mm->mmap_sem);
ret = PTR_ERR(vi); vma = find_vma(mm, (unsigned long)info->vaddr);
break; if (!vma || !valid_vma(vma, is_register))
} goto unlock;
mm = vi->mm;
down_read(&mm->mmap_sem);
vma = find_vma(mm, (unsigned long)vi->vaddr);
if (!vma || !valid_vma(vma, is_register)) {
list_del(&vi->probe_list);
kfree(vi);
up_read(&mm->mmap_sem);
mmput(mm);
continue;
}
vaddr = vma_address(vma, uprobe->offset);
if (vma->vm_file->f_mapping->host != uprobe->inode || if (vma->vm_file->f_mapping->host != uprobe->inode ||
vaddr != vi->vaddr) { vma_address(vma, uprobe->offset) != info->vaddr)
list_del(&vi->probe_list); goto unlock;
kfree(vi);
up_read(&mm->mmap_sem);
mmput(mm);
continue;
}
if (is_register)
ret = install_breakpoint(uprobe, mm, vma, vi->vaddr);
else
remove_breakpoint(uprobe, mm, vi->vaddr);
up_read(&mm->mmap_sem);
mmput(mm);
if (is_register) { if (is_register) {
if (ret && ret == -EEXIST) err = install_breakpoint(uprobe, mm, vma, info->vaddr);
ret = 0; /*
if (ret) * We can race against uprobe_mmap(), see the
break; * comment near uprobe_hash().
*/
if (err == -EEXIST)
err = 0;
} else {
remove_breakpoint(uprobe, mm, info->vaddr);
} }
unlock:
up_write(&mm->mmap_sem);
free:
mmput(mm);
info = free_map_info(info);
} }
list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { return err;
list_del(&vi->probe_list);
kfree(vi);
}
return ret;
} }
static int __uprobe_register(struct uprobe *uprobe) static int __uprobe_register(struct uprobe *uprobe)
@ -1048,7 +1031,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head)
int uprobe_mmap(struct vm_area_struct *vma) int uprobe_mmap(struct vm_area_struct *vma)
{ {
struct list_head tmp_list; struct list_head tmp_list;
struct uprobe *uprobe, *u; struct uprobe *uprobe;
struct inode *inode; struct inode *inode;
int ret, count; int ret, count;
@ -1066,12 +1049,9 @@ int uprobe_mmap(struct vm_area_struct *vma)
ret = 0; ret = 0;
count = 0; count = 0;
list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { list_for_each_entry(uprobe, &tmp_list, pending_list) {
loff_t vaddr;
list_del(&uprobe->pending_list);
if (!ret) { if (!ret) {
vaddr = vma_address(vma, uprobe->offset); loff_t vaddr = vma_address(vma, uprobe->offset);
if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { if (vaddr < vma->vm_start || vaddr >= vma->vm_end) {
put_uprobe(uprobe); put_uprobe(uprobe);
@ -1079,8 +1059,10 @@ int uprobe_mmap(struct vm_area_struct *vma)
} }
ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr);
/*
/* Ignore double add: */ * We can race against uprobe_register(), see the
* comment near uprobe_hash().
*/
if (ret == -EEXIST) { if (ret == -EEXIST) {
ret = 0; ret = 0;
@ -1115,7 +1097,7 @@ int uprobe_mmap(struct vm_area_struct *vma)
void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{ {
struct list_head tmp_list; struct list_head tmp_list;
struct uprobe *uprobe, *u; struct uprobe *uprobe;
struct inode *inode; struct inode *inode;
if (!atomic_read(&uprobe_events) || !valid_vma(vma, false)) if (!atomic_read(&uprobe_events) || !valid_vma(vma, false))
@ -1132,11 +1114,8 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
mutex_lock(uprobes_mmap_hash(inode)); mutex_lock(uprobes_mmap_hash(inode));
build_probe_list(inode, &tmp_list); build_probe_list(inode, &tmp_list);
list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { list_for_each_entry(uprobe, &tmp_list, pending_list) {
loff_t vaddr; loff_t vaddr = vma_address(vma, uprobe->offset);
list_del(&uprobe->pending_list);
vaddr = vma_address(vma, uprobe->offset);
if (vaddr >= start && vaddr < end) { if (vaddr >= start && vaddr < end) {
/* /*
@ -1378,9 +1357,6 @@ void uprobe_free_utask(struct task_struct *t)
{ {
struct uprobe_task *utask = t->utask; struct uprobe_task *utask = t->utask;
if (t->uprobe_srcu_id != -1)
srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id);
if (!utask) if (!utask)
return; return;
@ -1398,7 +1374,6 @@ void uprobe_free_utask(struct task_struct *t)
void uprobe_copy_process(struct task_struct *t) void uprobe_copy_process(struct task_struct *t)
{ {
t->utask = NULL; t->utask = NULL;
t->uprobe_srcu_id = -1;
} }
/* /*
@ -1417,7 +1392,6 @@ static struct uprobe_task *add_utask(void)
if (unlikely(!utask)) if (unlikely(!utask))
return NULL; return NULL;
utask->active_uprobe = NULL;
current->utask = utask; current->utask = utask;
return utask; return utask;
} }
@ -1479,41 +1453,64 @@ static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs)
return false; return false;
} }
static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
{
struct mm_struct *mm = current->mm;
struct uprobe *uprobe = NULL;
struct vm_area_struct *vma;
down_read(&mm->mmap_sem);
vma = find_vma(mm, bp_vaddr);
if (vma && vma->vm_start <= bp_vaddr) {
if (valid_vma(vma, false)) {
struct inode *inode;
loff_t offset;
inode = vma->vm_file->f_mapping->host;
offset = bp_vaddr - vma->vm_start;
offset += (vma->vm_pgoff << PAGE_SHIFT);
uprobe = find_uprobe(inode, offset);
}
if (!uprobe)
*is_swbp = is_swbp_at_addr(mm, bp_vaddr);
} else {
*is_swbp = -EFAULT;
}
up_read(&mm->mmap_sem);
return uprobe;
}
/* /*
* Run handler and ask thread to singlestep. * Run handler and ask thread to singlestep.
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps. * Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
*/ */
static void handle_swbp(struct pt_regs *regs) static void handle_swbp(struct pt_regs *regs)
{ {
struct vm_area_struct *vma;
struct uprobe_task *utask; struct uprobe_task *utask;
struct uprobe *uprobe; struct uprobe *uprobe;
struct mm_struct *mm;
unsigned long bp_vaddr; unsigned long bp_vaddr;
int uninitialized_var(is_swbp);
uprobe = NULL;
bp_vaddr = uprobe_get_swbp_addr(regs); bp_vaddr = uprobe_get_swbp_addr(regs);
mm = current->mm; uprobe = find_active_uprobe(bp_vaddr, &is_swbp);
down_read(&mm->mmap_sem);
vma = find_vma(mm, bp_vaddr);
if (vma && vma->vm_start <= bp_vaddr && valid_vma(vma, false)) {
struct inode *inode;
loff_t offset;
inode = vma->vm_file->f_mapping->host;
offset = bp_vaddr - vma->vm_start;
offset += (vma->vm_pgoff << PAGE_SHIFT);
uprobe = find_uprobe(inode, offset);
}
srcu_read_unlock_raw(&uprobes_srcu, current->uprobe_srcu_id);
current->uprobe_srcu_id = -1;
up_read(&mm->mmap_sem);
if (!uprobe) { if (!uprobe) {
/* No matching uprobe; signal SIGTRAP. */ if (is_swbp > 0) {
send_sig(SIGTRAP, current, 0); /* No matching uprobe; signal SIGTRAP. */
send_sig(SIGTRAP, current, 0);
} else {
/*
* Either we raced with uprobe_unregister() or we can't
* access this memory. The latter is only possible if
* another thread plays with our ->mm. In both cases
* we can simply restart. If this vma was unmapped we
* can pretend this insn was not executed yet and get
* the (correct) SIGSEGV after restart.
*/
instruction_pointer_set(regs, bp_vaddr);
}
return; return;
} }
@ -1620,7 +1617,6 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs)
utask->state = UTASK_BP_HIT; utask->state = UTASK_BP_HIT;
set_thread_flag(TIF_UPROBE); set_thread_flag(TIF_UPROBE);
current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu);
return 1; return 1;
} }
@ -1655,7 +1651,6 @@ static int __init init_uprobes(void)
mutex_init(&uprobes_mutex[i]); mutex_init(&uprobes_mutex[i]);
mutex_init(&uprobes_mmap_mutex[i]); mutex_init(&uprobes_mmap_mutex[i]);
} }
init_srcu_struct(&uprobes_srcu);
return register_die_notifier(&uprobe_exception_nb); return register_die_notifier(&uprobe_exception_nb);
} }

View File

@ -312,7 +312,7 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,
static int __register_ftrace_function(struct ftrace_ops *ops) static int __register_ftrace_function(struct ftrace_ops *ops)
{ {
if (ftrace_disabled) if (unlikely(ftrace_disabled))
return -ENODEV; return -ENODEV;
if (FTRACE_WARN_ON(ops == &global_ops)) if (FTRACE_WARN_ON(ops == &global_ops))
@ -4299,16 +4299,12 @@ int register_ftrace_function(struct ftrace_ops *ops)
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
if (unlikely(ftrace_disabled))
goto out_unlock;
ret = __register_ftrace_function(ops); ret = __register_ftrace_function(ops);
if (!ret) if (!ret)
ret = ftrace_startup(ops, 0); ret = ftrace_startup(ops, 0);
out_unlock:
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(register_ftrace_function); EXPORT_SYMBOL_GPL(register_ftrace_function);

View File

@ -3239,6 +3239,10 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
if (cpu_buffer->commit_page == cpu_buffer->reader_page) if (cpu_buffer->commit_page == cpu_buffer->reader_page)
goto out; goto out;
/* Don't bother swapping if the ring buffer is empty */
if (rb_num_of_entries(cpu_buffer) == 0)
goto out;
/* /*
* Reset the reader page to size zero. * Reset the reader page to size zero.
*/ */

View File

@ -830,6 +830,8 @@ int register_tracer(struct tracer *type)
current_trace = saved_tracer; current_trace = saved_tracer;
if (ret) { if (ret) {
printk(KERN_CONT "FAILED!\n"); printk(KERN_CONT "FAILED!\n");
/* Add the warning after printing 'FAILED' */
WARN_ON(1);
goto out; goto out;
} }
/* Only reset on passing, to avoid touching corrupted buffers */ /* Only reset on passing, to avoid touching corrupted buffers */
@ -1708,9 +1710,11 @@ EXPORT_SYMBOL_GPL(trace_vprintk);
static void trace_iterator_increment(struct trace_iterator *iter) static void trace_iterator_increment(struct trace_iterator *iter)
{ {
struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, iter->cpu);
iter->idx++; iter->idx++;
if (iter->buffer_iter[iter->cpu]) if (buf_iter)
ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); ring_buffer_read(buf_iter, NULL);
} }
static struct trace_entry * static struct trace_entry *
@ -1718,7 +1722,7 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
unsigned long *lost_events) unsigned long *lost_events)
{ {
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct ring_buffer_iter *buf_iter = iter->buffer_iter[cpu]; struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, cpu);
if (buf_iter) if (buf_iter)
event = ring_buffer_iter_peek(buf_iter, ts); event = ring_buffer_iter_peek(buf_iter, ts);
@ -1856,10 +1860,10 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu)
tr->data[cpu]->skipped_entries = 0; tr->data[cpu]->skipped_entries = 0;
if (!iter->buffer_iter[cpu]) buf_iter = trace_buffer_iter(iter, cpu);
if (!buf_iter)
return; return;
buf_iter = iter->buffer_iter[cpu];
ring_buffer_iter_reset(buf_iter); ring_buffer_iter_reset(buf_iter);
/* /*
@ -2205,13 +2209,15 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
int trace_empty(struct trace_iterator *iter) int trace_empty(struct trace_iterator *iter)
{ {
struct ring_buffer_iter *buf_iter;
int cpu; int cpu;
/* If we are looking at one CPU buffer, only check that one */ /* If we are looking at one CPU buffer, only check that one */
if (iter->cpu_file != TRACE_PIPE_ALL_CPU) { if (iter->cpu_file != TRACE_PIPE_ALL_CPU) {
cpu = iter->cpu_file; cpu = iter->cpu_file;
if (iter->buffer_iter[cpu]) { buf_iter = trace_buffer_iter(iter, cpu);
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu])) if (buf_iter) {
if (!ring_buffer_iter_empty(buf_iter))
return 0; return 0;
} else { } else {
if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu))
@ -2221,8 +2227,9 @@ int trace_empty(struct trace_iterator *iter)
} }
for_each_tracing_cpu(cpu) { for_each_tracing_cpu(cpu) {
if (iter->buffer_iter[cpu]) { buf_iter = trace_buffer_iter(iter, cpu);
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu])) if (buf_iter) {
if (!ring_buffer_iter_empty(buf_iter))
return 0; return 0;
} else { } else {
if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu))
@ -2381,6 +2388,11 @@ __tracing_open(struct inode *inode, struct file *file)
if (!iter) if (!iter)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
iter->buffer_iter = kzalloc(sizeof(*iter->buffer_iter) * num_possible_cpus(),
GFP_KERNEL);
if (!iter->buffer_iter)
goto release;
/* /*
* We make a copy of the current tracer to avoid concurrent * We make a copy of the current tracer to avoid concurrent
* changes on it while we are reading. * changes on it while we are reading.
@ -2441,6 +2453,8 @@ __tracing_open(struct inode *inode, struct file *file)
fail: fail:
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
kfree(iter->trace); kfree(iter->trace);
kfree(iter->buffer_iter);
release:
seq_release_private(inode, file); seq_release_private(inode, file);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
@ -2481,6 +2495,7 @@ static int tracing_release(struct inode *inode, struct file *file)
mutex_destroy(&iter->mutex); mutex_destroy(&iter->mutex);
free_cpumask_var(iter->started); free_cpumask_var(iter->started);
kfree(iter->trace); kfree(iter->trace);
kfree(iter->buffer_iter);
seq_release_private(inode, file); seq_release_private(inode, file);
return 0; return 0;
} }

View File

@ -317,6 +317,14 @@ struct tracer {
#define TRACE_PIPE_ALL_CPU -1 #define TRACE_PIPE_ALL_CPU -1
static inline struct ring_buffer_iter *
trace_buffer_iter(struct trace_iterator *iter, int cpu)
{
if (iter->buffer_iter && iter->buffer_iter[cpu])
return iter->buffer_iter[cpu];
return NULL;
}
int tracer_init(struct tracer *t, struct trace_array *tr); int tracer_init(struct tracer *t, struct trace_array *tr);
int tracing_is_enabled(void); int tracing_is_enabled(void);
void trace_wake_up(void); void trace_wake_up(void);

View File

@ -538,7 +538,7 @@ get_return_for_leaf(struct trace_iterator *iter,
next = &data->ret; next = &data->ret;
} else { } else {
ring_iter = iter->buffer_iter[iter->cpu]; ring_iter = trace_buffer_iter(iter, iter->cpu);
/* First peek to compare current entry and the next one */ /* First peek to compare current entry and the next one */
if (ring_iter) if (ring_iter)

View File

@ -1325,4 +1325,4 @@ __init static int init_events(void)
return 0; return 0;
} }
device_initcall(init_events); early_initcall(init_events);

View File

@ -250,8 +250,12 @@ endef
all_objs := $(sort $(ALL_OBJS)) all_objs := $(sort $(ALL_OBJS))
all_deps := $(all_objs:%.o=.%.d) all_deps := $(all_objs:%.o=.%.d)
# let .d file also depends on the source and header files
define check_deps define check_deps
$(CC) -M $(CFLAGS) $< > $@; @set -e; $(RM) $@; \
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
$(RM) $@.$$$$
endef endef
$(gui_deps): ks_version.h $(gui_deps): ks_version.h
@ -270,11 +274,13 @@ endif
tags: force tags: force
$(RM) tags $(RM) tags
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
TAGS: force TAGS: force
$(RM) TAGS $(RM) TAGS
find . -name '*.[ch]' | xargs etags find . -name '*.[ch]' | xargs etags \
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
define do_install define do_install
$(print_install) \ $(print_install) \
@ -290,7 +296,7 @@ install_lib: all_cmd install_plugins install_python
install: install_lib install: install_lib
clean: clean:
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
$(RM) tags TAGS $(RM) tags TAGS
endif # skip-makefile endif # skip-makefile

View File

@ -467,8 +467,10 @@ int pevent_register_function(struct pevent *pevent, char *func,
item->mod = NULL; item->mod = NULL;
item->addr = addr; item->addr = addr;
pevent->funclist = item; if (!item->func || (mod && !item->mod))
die("malloc func");
pevent->funclist = item;
pevent->func_count++; pevent->func_count++;
return 0; return 0;
@ -511,12 +513,12 @@ struct printk_list {
static int printk_cmp(const void *a, const void *b) static int printk_cmp(const void *a, const void *b)
{ {
const struct func_map *fa = a; const struct printk_map *pa = a;
const struct func_map *fb = b; const struct printk_map *pb = b;
if (fa->addr < fb->addr) if (pa->addr < pb->addr)
return -1; return -1;
if (fa->addr > fb->addr) if (pa->addr > pb->addr)
return 1; return 1;
return 0; return 0;
@ -583,10 +585,13 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt,
item = malloc_or_die(sizeof(*item)); item = malloc_or_die(sizeof(*item));
item->next = pevent->printklist; item->next = pevent->printklist;
pevent->printklist = item;
item->printk = strdup(fmt); item->printk = strdup(fmt);
item->addr = addr; item->addr = addr;
if (!item->printk)
die("malloc fmt");
pevent->printklist = item;
pevent->printk_count++; pevent->printk_count++;
return 0; return 0;
@ -616,7 +621,9 @@ static struct event_format *alloc_event(void)
{ {
struct event_format *event; struct event_format *event;
event = malloc_or_die(sizeof(*event)); event = malloc(sizeof(*event));
if (!event)
return NULL;
memset(event, 0, sizeof(*event)); memset(event, 0, sizeof(*event));
return event; return event;
@ -626,12 +633,8 @@ static void add_event(struct pevent *pevent, struct event_format *event)
{ {
int i; int i;
if (!pevent->events) pevent->events = realloc(pevent->events, sizeof(event) *
pevent->events = malloc_or_die(sizeof(event)); (pevent->nr_events + 1));
else
pevent->events =
realloc(pevent->events, sizeof(event) *
(pevent->nr_events + 1));
if (!pevent->events) if (!pevent->events)
die("Can not allocate events"); die("Can not allocate events");
@ -697,6 +700,10 @@ static void free_arg(struct print_arg *arg)
free_arg(arg->symbol.field); free_arg(arg->symbol.field);
free_flag_sym(arg->symbol.symbols); free_flag_sym(arg->symbol.symbols);
break; break;
case PRINT_HEX:
free_arg(arg->hex.field);
free_arg(arg->hex.size);
break;
case PRINT_TYPE: case PRINT_TYPE:
free(arg->typecast.type); free(arg->typecast.type);
free_arg(arg->typecast.item); free_arg(arg->typecast.item);
@ -775,6 +782,25 @@ int pevent_peek_char(void)
return __peek_char(); return __peek_char();
} }
static int extend_token(char **tok, char *buf, int size)
{
char *newtok = realloc(*tok, size);
if (!newtok) {
free(*tok);
*tok = NULL;
return -1;
}
if (!*tok)
strcpy(newtok, buf);
else
strcat(newtok, buf);
*tok = newtok;
return 0;
}
static enum event_type force_token(const char *str, char **tok); static enum event_type force_token(const char *str, char **tok);
static enum event_type __read_token(char **tok) static enum event_type __read_token(char **tok)
@ -859,17 +885,10 @@ static enum event_type __read_token(char **tok)
do { do {
if (i == (BUFSIZ - 1)) { if (i == (BUFSIZ - 1)) {
buf[i] = 0; buf[i] = 0;
if (*tok) {
*tok = realloc(*tok, tok_size + BUFSIZ);
if (!*tok)
return EVENT_NONE;
strcat(*tok, buf);
} else
*tok = strdup(buf);
if (!*tok)
return EVENT_NONE;
tok_size += BUFSIZ; tok_size += BUFSIZ;
if (extend_token(tok, buf, tok_size) < 0)
return EVENT_NONE;
i = 0; i = 0;
} }
last_ch = ch; last_ch = ch;
@ -908,17 +927,10 @@ static enum event_type __read_token(char **tok)
while (get_type(__peek_char()) == type) { while (get_type(__peek_char()) == type) {
if (i == (BUFSIZ - 1)) { if (i == (BUFSIZ - 1)) {
buf[i] = 0; buf[i] = 0;
if (*tok) {
*tok = realloc(*tok, tok_size + BUFSIZ);
if (!*tok)
return EVENT_NONE;
strcat(*tok, buf);
} else
*tok = strdup(buf);
if (!*tok)
return EVENT_NONE;
tok_size += BUFSIZ; tok_size += BUFSIZ;
if (extend_token(tok, buf, tok_size) < 0)
return EVENT_NONE;
i = 0; i = 0;
} }
ch = __read_char(); ch = __read_char();
@ -927,14 +939,7 @@ static enum event_type __read_token(char **tok)
out: out:
buf[i] = 0; buf[i] = 0;
if (*tok) { if (extend_token(tok, buf, tok_size + i + 1) < 0)
*tok = realloc(*tok, tok_size + i);
if (!*tok)
return EVENT_NONE;
strcat(*tok, buf);
} else
*tok = strdup(buf);
if (!*tok)
return EVENT_NONE; return EVENT_NONE;
if (type == EVENT_ITEM) { if (type == EVENT_ITEM) {
@ -1255,9 +1260,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f
field->flags |= FIELD_IS_POINTER; field->flags |= FIELD_IS_POINTER;
if (field->type) { if (field->type) {
field->type = realloc(field->type, char *new_type;
strlen(field->type) + new_type = realloc(field->type,
strlen(last_token) + 2); strlen(field->type) +
strlen(last_token) + 2);
if (!new_type) {
free(last_token);
goto fail;
}
field->type = new_type;
strcat(field->type, " "); strcat(field->type, " ");
strcat(field->type, last_token); strcat(field->type, last_token);
free(last_token); free(last_token);
@ -1282,6 +1293,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
if (strcmp(token, "[") == 0) { if (strcmp(token, "[") == 0) {
enum event_type last_type = type; enum event_type last_type = type;
char *brackets = token; char *brackets = token;
char *new_brackets;
int len; int len;
field->flags |= FIELD_IS_ARRAY; field->flags |= FIELD_IS_ARRAY;
@ -1301,9 +1313,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f
len = 1; len = 1;
last_type = type; last_type = type;
brackets = realloc(brackets, new_brackets = realloc(brackets,
strlen(brackets) + strlen(brackets) +
strlen(token) + len); strlen(token) + len);
if (!new_brackets) {
free(brackets);
goto fail;
}
brackets = new_brackets;
if (len == 2) if (len == 2)
strcat(brackets, " "); strcat(brackets, " ");
strcat(brackets, token); strcat(brackets, token);
@ -1319,7 +1336,12 @@ static int event_read_fields(struct event_format *event, struct format_field **f
free_token(token); free_token(token);
brackets = realloc(brackets, strlen(brackets) + 2); new_brackets = realloc(brackets, strlen(brackets) + 2);
if (!new_brackets) {
free(brackets);
goto fail;
}
brackets = new_brackets;
strcat(brackets, "]"); strcat(brackets, "]");
/* add brackets to type */ /* add brackets to type */
@ -1330,10 +1352,16 @@ static int event_read_fields(struct event_format *event, struct format_field **f
* the format: type [] item; * the format: type [] item;
*/ */
if (type == EVENT_ITEM) { if (type == EVENT_ITEM) {
field->type = realloc(field->type, char *new_type;
strlen(field->type) + new_type = realloc(field->type,
strlen(field->name) + strlen(field->type) +
strlen(brackets) + 2); strlen(field->name) +
strlen(brackets) + 2);
if (!new_type) {
free(brackets);
goto fail;
}
field->type = new_type;
strcat(field->type, " "); strcat(field->type, " ");
strcat(field->type, field->name); strcat(field->type, field->name);
free_token(field->name); free_token(field->name);
@ -1341,9 +1369,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f
field->name = token; field->name = token;
type = read_token(&token); type = read_token(&token);
} else { } else {
field->type = realloc(field->type, char *new_type;
strlen(field->type) + new_type = realloc(field->type,
strlen(brackets) + 1); strlen(field->type) +
strlen(brackets) + 1);
if (!new_type) {
free(brackets);
goto fail;
}
field->type = new_type;
strcat(field->type, brackets); strcat(field->type, brackets);
} }
free(brackets); free(brackets);
@ -1726,10 +1760,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
/* could just be a type pointer */ /* could just be a type pointer */
if ((strcmp(arg->op.op, "*") == 0) && if ((strcmp(arg->op.op, "*") == 0) &&
type == EVENT_DELIM && (strcmp(token, ")") == 0)) { type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
char *new_atom;
if (left->type != PRINT_ATOM) if (left->type != PRINT_ATOM)
die("bad pointer type"); die("bad pointer type");
left->atom.atom = realloc(left->atom.atom, new_atom = realloc(left->atom.atom,
strlen(left->atom.atom) + 3); strlen(left->atom.atom) + 3);
if (!new_atom)
goto out_free;
left->atom.atom = new_atom;
strcat(left->atom.atom, " *"); strcat(left->atom.atom, " *");
free(arg->op.op); free(arg->op.op);
*arg = *left; *arg = *left;
@ -2146,6 +2186,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
if (value == NULL) if (value == NULL)
goto out_free; goto out_free;
field->value = strdup(value); field->value = strdup(value);
if (field->value == NULL)
goto out_free;
free_arg(arg); free_arg(arg);
arg = alloc_arg(); arg = alloc_arg();
@ -2159,6 +2201,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
if (value == NULL) if (value == NULL)
goto out_free; goto out_free;
field->str = strdup(value); field->str = strdup(value);
if (field->str == NULL)
goto out_free;
free_arg(arg); free_arg(arg);
arg = NULL; arg = NULL;
@ -2259,6 +2303,45 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
return EVENT_ERROR; return EVENT_ERROR;
} }
static enum event_type
process_hex(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
char *token;
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_HEX;
field = alloc_arg();
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
arg->hex.field = field;
free_token(token);
field = alloc_arg();
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ")"))
goto out_free;
arg->hex.size = field;
free_token(token);
type = read_token_item(tok);
return type;
out_free:
free_arg(field);
free_token(token);
*tok = NULL;
return EVENT_ERROR;
}
static enum event_type static enum event_type
process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok)
{ {
@ -2488,6 +2571,10 @@ process_function(struct event_format *event, struct print_arg *arg,
is_symbolic_field = 1; is_symbolic_field = 1;
return process_symbols(event, arg, tok); return process_symbols(event, arg, tok);
} }
if (strcmp(token, "__print_hex") == 0) {
free_token(token);
return process_hex(event, arg, tok);
}
if (strcmp(token, "__get_str") == 0) { if (strcmp(token, "__get_str") == 0) {
free_token(token); free_token(token);
return process_str(event, arg, tok); return process_str(event, arg, tok);
@ -2541,7 +2628,16 @@ process_arg_token(struct event_format *event, struct print_arg *arg,
} }
/* atoms can be more than one token long */ /* atoms can be more than one token long */
while (type == EVENT_ITEM) { while (type == EVENT_ITEM) {
atom = realloc(atom, strlen(atom) + strlen(token) + 2); char *new_atom;
new_atom = realloc(atom,
strlen(atom) + strlen(token) + 2);
if (!new_atom) {
free(atom);
*tok = NULL;
free_token(token);
return EVENT_ERROR;
}
atom = new_atom;
strcat(atom, " "); strcat(atom, " ");
strcat(atom, token); strcat(atom, token);
free_token(token); free_token(token);
@ -2835,7 +2931,7 @@ static int get_common_info(struct pevent *pevent,
event = pevent->events[0]; event = pevent->events[0];
field = pevent_find_common_field(event, type); field = pevent_find_common_field(event, type);
if (!field) if (!field)
die("field '%s' not found", type); return -1;
*offset = field->offset; *offset = field->offset;
*size = field->size; *size = field->size;
@ -2886,15 +2982,16 @@ static int parse_common_flags(struct pevent *pevent, void *data)
static int parse_common_lock_depth(struct pevent *pevent, void *data) static int parse_common_lock_depth(struct pevent *pevent, void *data)
{ {
int ret; return __parse_common(pevent, data,
&pevent->ld_size, &pevent->ld_offset,
"common_lock_depth");
}
ret = __parse_common(pevent, data, static int parse_common_migrate_disable(struct pevent *pevent, void *data)
&pevent->ld_size, &pevent->ld_offset, {
"common_lock_depth"); return __parse_common(pevent, data,
if (ret < 0) &pevent->ld_size, &pevent->ld_offset,
return -1; "common_migrate_disable");
return ret;
} }
static int events_id_cmp(const void *a, const void *b); static int events_id_cmp(const void *a, const void *b);
@ -2995,6 +3092,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
break; break;
case PRINT_FLAGS: case PRINT_FLAGS:
case PRINT_SYMBOL: case PRINT_SYMBOL:
case PRINT_HEX:
break; break;
case PRINT_TYPE: case PRINT_TYPE:
val = eval_num_arg(data, size, event, arg->typecast.item); val = eval_num_arg(data, size, event, arg->typecast.item);
@ -3214,11 +3312,13 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
{ {
struct pevent *pevent = event->pevent; struct pevent *pevent = event->pevent;
struct print_flag_sym *flag; struct print_flag_sym *flag;
struct format_field *field;
unsigned long long val, fval; unsigned long long val, fval;
unsigned long addr; unsigned long addr;
char *str; char *str;
unsigned char *hex;
int print; int print;
int len; int i, len;
switch (arg->type) { switch (arg->type) {
case PRINT_NULL: case PRINT_NULL:
@ -3228,27 +3328,29 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
print_str_to_seq(s, format, len_arg, arg->atom.atom); print_str_to_seq(s, format, len_arg, arg->atom.atom);
return; return;
case PRINT_FIELD: case PRINT_FIELD:
if (!arg->field.field) { field = arg->field.field;
arg->field.field = pevent_find_any_field(event, arg->field.name); if (!field) {
if (!arg->field.field) field = pevent_find_any_field(event, arg->field.name);
if (!field)
die("field %s not found", arg->field.name); die("field %s not found", arg->field.name);
arg->field.field = field;
} }
/* Zero sized fields, mean the rest of the data */ /* Zero sized fields, mean the rest of the data */
len = arg->field.field->size ? : size - arg->field.field->offset; len = field->size ? : size - field->offset;
/* /*
* Some events pass in pointers. If this is not an array * Some events pass in pointers. If this is not an array
* and the size is the same as long_size, assume that it * and the size is the same as long_size, assume that it
* is a pointer. * is a pointer.
*/ */
if (!(arg->field.field->flags & FIELD_IS_ARRAY) && if (!(field->flags & FIELD_IS_ARRAY) &&
arg->field.field->size == pevent->long_size) { field->size == pevent->long_size) {
addr = *(unsigned long *)(data + arg->field.field->offset); addr = *(unsigned long *)(data + field->offset);
trace_seq_printf(s, "%lx", addr); trace_seq_printf(s, "%lx", addr);
break; break;
} }
str = malloc_or_die(len + 1); str = malloc_or_die(len + 1);
memcpy(str, data + arg->field.field->offset, len); memcpy(str, data + field->offset, len);
str[len] = 0; str[len] = 0;
print_str_to_seq(s, format, len_arg, str); print_str_to_seq(s, format, len_arg, str);
free(str); free(str);
@ -3281,6 +3383,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
} }
} }
break; break;
case PRINT_HEX:
field = arg->hex.field->field.field;
if (!field) {
str = arg->hex.field->field.name;
field = pevent_find_any_field(event, str);
if (!field)
die("field %s not found", str);
arg->hex.field->field.field = field;
}
hex = data + field->offset;
len = eval_num_arg(data, size, event, arg->hex.size);
for (i = 0; i < len; i++) {
if (i)
trace_seq_putc(s, ' ');
trace_seq_printf(s, "%02x", hex[i]);
}
break;
case PRINT_TYPE: case PRINT_TYPE:
break; break;
@ -3299,7 +3418,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
break; break;
} }
case PRINT_BSTRING: case PRINT_BSTRING:
trace_seq_printf(s, format, arg->string.string); print_str_to_seq(s, format, len_arg, arg->string.string);
break; break;
case PRINT_OP: case PRINT_OP:
/* /*
@ -3363,6 +3482,10 @@ process_defined_func(struct trace_seq *s, void *data, int size,
string = malloc_or_die(sizeof(*string)); string = malloc_or_die(sizeof(*string));
string->next = strings; string->next = strings;
string->str = strdup(str.buffer); string->str = strdup(str.buffer);
if (!string->str)
die("malloc str");
args[i] = (unsigned long long)string->str;
strings = string; strings = string;
trace_seq_destroy(&str); trace_seq_destroy(&str);
break; break;
@ -3400,6 +3523,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
unsigned long long ip, val; unsigned long long ip, val;
char *ptr; char *ptr;
void *bptr; void *bptr;
int vsize;
field = pevent->bprint_buf_field; field = pevent->bprint_buf_field;
ip_field = pevent->bprint_ip_field; ip_field = pevent->bprint_ip_field;
@ -3448,6 +3572,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
goto process_again; goto process_again;
case '0' ... '9': case '0' ... '9':
goto process_again; goto process_again;
case '.':
goto process_again;
case 'p': case 'p':
ls = 1; ls = 1;
/* fall through */ /* fall through */
@ -3455,23 +3581,30 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
case 'u': case 'u':
case 'x': case 'x':
case 'i': case 'i':
switch (ls) {
case 0:
vsize = 4;
break;
case 1:
vsize = pevent->long_size;
break;
case 2:
vsize = 8;
break;
default:
vsize = ls; /* ? */
break;
}
/* fall through */
case '*':
if (*ptr == '*')
vsize = 4;
/* the pointers are always 4 bytes aligned */ /* the pointers are always 4 bytes aligned */
bptr = (void *)(((unsigned long)bptr + 3) & bptr = (void *)(((unsigned long)bptr + 3) &
~3); ~3);
switch (ls) { val = pevent_read_number(pevent, bptr, vsize);
case 0: bptr += vsize;
ls = 4;
break;
case 1:
ls = pevent->long_size;
break;
case 2:
ls = 8;
default:
break;
}
val = pevent_read_number(pevent, bptr, ls);
bptr += ls;
arg = alloc_arg(); arg = alloc_arg();
arg->next = NULL; arg->next = NULL;
arg->type = PRINT_ATOM; arg->type = PRINT_ATOM;
@ -3479,12 +3612,21 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
sprintf(arg->atom.atom, "%lld", val); sprintf(arg->atom.atom, "%lld", val);
*next = arg; *next = arg;
next = &arg->next; next = &arg->next;
/*
* The '*' case means that an arg is used as the length.
* We need to continue to figure out for what.
*/
if (*ptr == '*')
goto process_again;
break; break;
case 's': case 's':
arg = alloc_arg(); arg = alloc_arg();
arg->next = NULL; arg->next = NULL;
arg->type = PRINT_BSTRING; arg->type = PRINT_BSTRING;
arg->string.string = strdup(bptr); arg->string.string = strdup(bptr);
if (!arg->string.string)
break;
bptr += strlen(bptr) + 1; bptr += strlen(bptr) + 1;
*next = arg; *next = arg;
next = &arg->next; next = &arg->next;
@ -3589,6 +3731,16 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
} }
static int is_printable_array(char *p, unsigned int len)
{
unsigned int i;
for (i = 0; i < len && p[i]; i++)
if (!isprint(p[i]))
return 0;
return 1;
}
static void print_event_fields(struct trace_seq *s, void *data, int size, static void print_event_fields(struct trace_seq *s, void *data, int size,
struct event_format *event) struct event_format *event)
{ {
@ -3608,7 +3760,8 @@ static void print_event_fields(struct trace_seq *s, void *data, int size,
len = offset >> 16; len = offset >> 16;
offset &= 0xffff; offset &= 0xffff;
} }
if (field->flags & FIELD_IS_STRING) { if (field->flags & FIELD_IS_STRING &&
is_printable_array(data + offset, len)) {
trace_seq_printf(s, "%s", (char *)data + offset); trace_seq_printf(s, "%s", (char *)data + offset);
} else { } else {
trace_seq_puts(s, "ARRAY["); trace_seq_puts(s, "ARRAY[");
@ -3619,6 +3772,7 @@ static void print_event_fields(struct trace_seq *s, void *data, int size,
*((unsigned char *)data + offset + i)); *((unsigned char *)data + offset + i));
} }
trace_seq_putc(s, ']'); trace_seq_putc(s, ']');
field->flags &= ~FIELD_IS_STRING;
} }
} else { } else {
val = pevent_read_number(event->pevent, data + field->offset, val = pevent_read_number(event->pevent, data + field->offset,
@ -3758,6 +3912,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
} else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') {
print_mac_arg(s, *(ptr+1), data, size, event, arg); print_mac_arg(s, *(ptr+1), data, size, event, arg);
ptr++; ptr++;
arg = arg->next;
break; break;
} }
@ -3794,14 +3949,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
break; break;
} }
} }
if (pevent->long_size == 8 && ls) { if (pevent->long_size == 8 && ls &&
sizeof(long) != 8) {
char *p; char *p;
ls = 2; ls = 2;
/* make %l into %ll */ /* make %l into %ll */
p = strchr(format, 'l'); p = strchr(format, 'l');
if (p) if (p)
memmove(p, p+1, strlen(p)+1); memmove(p+1, p, strlen(p)+1);
else if (strcmp(format, "%p") == 0) else if (strcmp(format, "%p") == 0)
strcpy(format, "0x%llx"); strcpy(format, "0x%llx");
} }
@ -3878,8 +4034,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
* pevent_data_lat_fmt - parse the data for the latency format * pevent_data_lat_fmt - parse the data for the latency format
* @pevent: a handle to the pevent * @pevent: a handle to the pevent
* @s: the trace_seq to write to * @s: the trace_seq to write to
* @data: the raw data to read from * @record: the record to read from
* @size: currently unused.
* *
* This parses out the Latency format (interrupts disabled, * This parses out the Latency format (interrupts disabled,
* need rescheduling, in hard/soft interrupt, preempt count * need rescheduling, in hard/soft interrupt, preempt count
@ -3889,10 +4044,13 @@ void pevent_data_lat_fmt(struct pevent *pevent,
struct trace_seq *s, struct pevent_record *record) struct trace_seq *s, struct pevent_record *record)
{ {
static int check_lock_depth = 1; static int check_lock_depth = 1;
static int check_migrate_disable = 1;
static int lock_depth_exists; static int lock_depth_exists;
static int migrate_disable_exists;
unsigned int lat_flags; unsigned int lat_flags;
unsigned int pc; unsigned int pc;
int lock_depth; int lock_depth;
int migrate_disable;
int hardirq; int hardirq;
int softirq; int softirq;
void *data = record->data; void *data = record->data;
@ -3900,18 +4058,26 @@ void pevent_data_lat_fmt(struct pevent *pevent,
lat_flags = parse_common_flags(pevent, data); lat_flags = parse_common_flags(pevent, data);
pc = parse_common_pc(pevent, data); pc = parse_common_pc(pevent, data);
/* lock_depth may not always exist */ /* lock_depth may not always exist */
if (check_lock_depth) {
struct format_field *field;
struct event_format *event;
check_lock_depth = 0;
event = pevent->events[0];
field = pevent_find_common_field(event, "common_lock_depth");
if (field)
lock_depth_exists = 1;
}
if (lock_depth_exists) if (lock_depth_exists)
lock_depth = parse_common_lock_depth(pevent, data); lock_depth = parse_common_lock_depth(pevent, data);
else if (check_lock_depth) {
lock_depth = parse_common_lock_depth(pevent, data);
if (lock_depth < 0)
check_lock_depth = 0;
else
lock_depth_exists = 1;
}
/* migrate_disable may not always exist */
if (migrate_disable_exists)
migrate_disable = parse_common_migrate_disable(pevent, data);
else if (check_migrate_disable) {
migrate_disable = parse_common_migrate_disable(pevent, data);
if (migrate_disable < 0)
check_migrate_disable = 0;
else
migrate_disable_exists = 1;
}
hardirq = lat_flags & TRACE_FLAG_HARDIRQ; hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
softirq = lat_flags & TRACE_FLAG_SOFTIRQ; softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
@ -3930,6 +4096,13 @@ void pevent_data_lat_fmt(struct pevent *pevent,
else else
trace_seq_putc(s, '.'); trace_seq_putc(s, '.');
if (migrate_disable_exists) {
if (migrate_disable < 0)
trace_seq_putc(s, '.');
else
trace_seq_printf(s, "%d", migrate_disable);
}
if (lock_depth_exists) { if (lock_depth_exists) {
if (lock_depth < 0) if (lock_depth < 0)
trace_seq_putc(s, '.'); trace_seq_putc(s, '.');
@ -3996,10 +4169,7 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
* pevent_data_comm_from_pid - parse the data into the print format * pevent_data_comm_from_pid - parse the data into the print format
* @s: the trace_seq to write to * @s: the trace_seq to write to
* @event: the handle to the event * @event: the handle to the event
* @cpu: the cpu the event was recorded on * @record: the record to read from
* @data: the raw data
* @size: the size of the raw data
* @nsecs: the timestamp of the event
* *
* This parses the raw @data using the given @event information and * This parses the raw @data using the given @event information and
* writes the print format into the trace_seq. * writes the print format into the trace_seq.
@ -4279,6 +4449,13 @@ static void print_args(struct print_arg *args)
trace_seq_destroy(&s); trace_seq_destroy(&s);
printf(")"); printf(")");
break; break;
case PRINT_HEX:
printf("__print_hex(");
print_args(args->hex.field);
printf(", ");
print_args(args->hex.size);
printf(")");
break;
case PRINT_STRING: case PRINT_STRING:
case PRINT_BSTRING: case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string); printf("__get_str(%s)", args->string.string);
@ -4541,6 +4718,8 @@ int pevent_parse_event(struct pevent *pevent,
die("failed to read event id"); die("failed to read event id");
event->system = strdup(sys); event->system = strdup(sys);
if (!event->system)
die("failed to allocate system");
/* Add pevent to event so that it can be referenced */ /* Add pevent to event so that it can be referenced */
event->pevent = pevent; event->pevent = pevent;
@ -4582,6 +4761,11 @@ int pevent_parse_event(struct pevent *pevent,
list = &arg->next; list = &arg->next;
arg->type = PRINT_FIELD; arg->type = PRINT_FIELD;
arg->field.name = strdup(field->name); arg->field.name = strdup(field->name);
if (!arg->field.name) {
do_warning("failed to allocate field name");
event->flags |= EVENT_FL_FAILED;
return -1;
}
arg->field.field = field; arg->field.field = field;
} }
return 0; return 0;
@ -4753,7 +4937,7 @@ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
* @record: The record with the field name. * @record: The record with the field name.
* @err: print default error if failed. * @err: print default error if failed.
* *
* Returns: 0 on success, -1 field not fould, or 1 if buffer is full. * Returns: 0 on success, -1 field not found, or 1 if buffer is full.
*/ */
int pevent_print_num_field(struct trace_seq *s, const char *fmt, int pevent_print_num_field(struct trace_seq *s, const char *fmt,
struct event_format *event, const char *name, struct event_format *event, const char *name,
@ -4795,11 +4979,12 @@ static void free_func_handle(struct pevent_function_handler *func)
* pevent_register_print_function - register a helper function * pevent_register_print_function - register a helper function
* @pevent: the handle to the pevent * @pevent: the handle to the pevent
* @func: the function to process the helper function * @func: the function to process the helper function
* @ret_type: the return type of the helper function
* @name: the name of the helper function * @name: the name of the helper function
* @parameters: A list of enum pevent_func_arg_type * @parameters: A list of enum pevent_func_arg_type
* *
* Some events may have helper functions in the print format arguments. * Some events may have helper functions in the print format arguments.
* This allows a plugin to dynmically create a way to process one * This allows a plugin to dynamically create a way to process one
* of these functions. * of these functions.
* *
* The @parameters is a variable list of pevent_func_arg_type enums that * The @parameters is a variable list of pevent_func_arg_type enums that
@ -4870,12 +5055,13 @@ int pevent_register_print_function(struct pevent *pevent,
} }
/** /**
* pevent_register_event_handle - register a way to parse an event * pevent_register_event_handler - register a way to parse an event
* @pevent: the handle to the pevent * @pevent: the handle to the pevent
* @id: the id of the event to register * @id: the id of the event to register
* @sys_name: the system name the event belongs to * @sys_name: the system name the event belongs to
* @event_name: the name of the event * @event_name: the name of the event
* @func: the function to call to parse the event information * @func: the function to call to parse the event information
* @context: the data to be passed to @func
* *
* This function allows a developer to override the parsing of * This function allows a developer to override the parsing of
* a given event. If for some reason the default print format * a given event. If for some reason the default print format
@ -4925,6 +5111,11 @@ int pevent_register_event_handler(struct pevent *pevent,
if (sys_name) if (sys_name)
handle->sys_name = strdup(sys_name); handle->sys_name = strdup(sys_name);
if ((event_name && !handle->event_name) ||
(sys_name && !handle->sys_name)) {
die("Failed to allocate event/sys name");
}
handle->func = func; handle->func = func;
handle->next = pevent->handlers; handle->next = pevent->handlers;
pevent->handlers = handle; pevent->handlers = handle;

View File

@ -226,6 +226,11 @@ struct print_arg_symbol {
struct print_flag_sym *symbols; struct print_flag_sym *symbols;
}; };
struct print_arg_hex {
struct print_arg *field;
struct print_arg *size;
};
struct print_arg_dynarray { struct print_arg_dynarray {
struct format_field *field; struct format_field *field;
struct print_arg *index; struct print_arg *index;
@ -253,6 +258,7 @@ enum print_arg_type {
PRINT_FIELD, PRINT_FIELD,
PRINT_FLAGS, PRINT_FLAGS,
PRINT_SYMBOL, PRINT_SYMBOL,
PRINT_HEX,
PRINT_TYPE, PRINT_TYPE,
PRINT_STRING, PRINT_STRING,
PRINT_BSTRING, PRINT_BSTRING,
@ -270,6 +276,7 @@ struct print_arg {
struct print_arg_typecast typecast; struct print_arg_typecast typecast;
struct print_arg_flags flags; struct print_arg_flags flags;
struct print_arg_symbol symbol; struct print_arg_symbol symbol;
struct print_arg_hex hex;
struct print_arg_func func; struct print_arg_func func;
struct print_arg_string string; struct print_arg_string string;
struct print_arg_op op; struct print_arg_op op;

View File

@ -96,7 +96,7 @@ static enum event_type read_token(char **tok)
(strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) &&
pevent_peek_char() == '~') { pevent_peek_char() == '~') {
/* append it */ /* append it */
*tok = malloc(3); *tok = malloc_or_die(3);
sprintf(*tok, "%c%c", *token, '~'); sprintf(*tok, "%c%c", *token, '~');
free_token(token); free_token(token);
/* Now remove the '~' from the buffer */ /* Now remove the '~' from the buffer */
@ -148,17 +148,11 @@ add_filter_type(struct event_filter *filter, int id)
if (filter_type) if (filter_type)
return filter_type; return filter_type;
if (!filter->filters) filter->event_filters = realloc(filter->event_filters,
filter->event_filters = sizeof(*filter->event_filters) *
malloc_or_die(sizeof(*filter->event_filters)); (filter->filters + 1));
else { if (!filter->event_filters)
filter->event_filters = die("Could not allocate filter");
realloc(filter->event_filters,
sizeof(*filter->event_filters) *
(filter->filters + 1));
if (!filter->event_filters)
die("Could not allocate filter");
}
for (i = 0; i < filter->filters; i++) { for (i = 0; i < filter->filters; i++) {
if (filter->event_filters[i].event_id > id) if (filter->event_filters[i].event_id > id)
@ -1480,7 +1474,7 @@ void pevent_filter_clear_trivial(struct event_filter *filter,
{ {
struct filter_type *filter_type; struct filter_type *filter_type;
int count = 0; int count = 0;
int *ids; int *ids = NULL;
int i; int i;
if (!filter->filters) if (!filter->filters)
@ -1504,10 +1498,8 @@ void pevent_filter_clear_trivial(struct event_filter *filter,
default: default:
break; break;
} }
if (count)
ids = realloc(ids, sizeof(*ids) * (count + 1)); ids = realloc(ids, sizeof(*ids) * (count + 1));
else
ids = malloc(sizeof(*ids));
if (!ids) if (!ids)
die("Can't allocate ids"); die("Can't allocate ids");
ids[count++] = filter_type->event_id; ids[count++] = filter_type->event_id;
@ -1710,18 +1702,43 @@ static int test_num(struct event_format *event,
static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record)
{ {
const char *val = record->data + arg->str.field->offset; struct event_format *event;
struct pevent *pevent;
unsigned long long addr;
const char *val = NULL;
char hex[64];
/* /* If the field is not a string convert it */
* We need to copy the data since we can't be sure the field if (arg->str.field->flags & FIELD_IS_STRING) {
* is null terminated. val = record->data + arg->str.field->offset;
*/
if (*(val + arg->str.field->size - 1)) { /*
/* copy it */ * We need to copy the data since we can't be sure the field
memcpy(arg->str.buffer, val, arg->str.field->size); * is null terminated.
/* the buffer is already NULL terminated */ */
val = arg->str.buffer; if (*(val + arg->str.field->size - 1)) {
/* copy it */
memcpy(arg->str.buffer, val, arg->str.field->size);
/* the buffer is already NULL terminated */
val = arg->str.buffer;
}
} else {
event = arg->str.field->event;
pevent = event->pevent;
addr = get_value(event, arg->str.field, record);
if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG))
/* convert to a kernel symbol */
val = pevent_find_function(pevent, addr);
if (val == NULL) {
/* just use the hex of the string name */
snprintf(hex, 64, "0x%llx", addr);
val = hex;
}
} }
return val; return val;
} }
@ -2001,11 +2018,13 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
char *lstr; char *lstr;
char *rstr; char *rstr;
char *op; char *op;
char *str; char *str = NULL;
int len; int len;
lstr = arg_to_str(filter, arg->exp.left); lstr = arg_to_str(filter, arg->exp.left);
rstr = arg_to_str(filter, arg->exp.right); rstr = arg_to_str(filter, arg->exp.right);
if (!lstr || !rstr)
goto out;
switch (arg->exp.type) { switch (arg->exp.type) {
case FILTER_EXP_ADD: case FILTER_EXP_ADD:
@ -2045,6 +2064,7 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; len = strlen(op) + strlen(lstr) + strlen(rstr) + 4;
str = malloc_or_die(len); str = malloc_or_die(len);
snprintf(str, len, "%s %s %s", lstr, op, rstr); snprintf(str, len, "%s %s %s", lstr, op, rstr);
out:
free(lstr); free(lstr);
free(rstr); free(rstr);
@ -2061,6 +2081,8 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
lstr = arg_to_str(filter, arg->num.left); lstr = arg_to_str(filter, arg->num.left);
rstr = arg_to_str(filter, arg->num.right); rstr = arg_to_str(filter, arg->num.right);
if (!lstr || !rstr)
goto out;
switch (arg->num.type) { switch (arg->num.type) {
case FILTER_CMP_EQ: case FILTER_CMP_EQ:
@ -2097,6 +2119,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
break; break;
} }
out:
free(lstr); free(lstr);
free(rstr); free(rstr);
return str; return str;
@ -2247,7 +2270,12 @@ int pevent_filter_compare(struct event_filter *filter1, struct event_filter *fil
/* The best way to compare complex filters is with strings */ /* The best way to compare complex filters is with strings */
str1 = arg_to_str(filter1, filter_type1->filter); str1 = arg_to_str(filter1, filter_type1->filter);
str2 = arg_to_str(filter2, filter_type2->filter); str2 = arg_to_str(filter2, filter_type2->filter);
result = strcmp(str1, str2) != 0; if (str1 && str2)
result = strcmp(str1, str2) != 0;
else
/* bail out if allocation fails */
result = 1;
free(str1); free(str1);
free(str2); free(str2);
if (result) if (result)

View File

@ -12,7 +12,7 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
This 'perf bench' command is general framework for benchmark suites. This 'perf bench' command is a general framework for benchmark suites.
COMMON OPTIONS COMMON OPTIONS
-------------- --------------
@ -45,14 +45,20 @@ SUBSYSTEM
'sched':: 'sched'::
Scheduler and IPC mechanisms. Scheduler and IPC mechanisms.
'mem'::
Memory access performance.
'all'::
All benchmark subsystems.
SUITES FOR 'sched' SUITES FOR 'sched'
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
*messaging*:: *messaging*::
Suite for evaluating performance of scheduler and IPC mechanisms. Suite for evaluating performance of scheduler and IPC mechanisms.
Based on hackbench by Rusty Russell. Based on hackbench by Rusty Russell.
Options of *pipe* Options of *messaging*
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
-p:: -p::
--pipe:: --pipe::
Use pipe() instead of socketpair() Use pipe() instead of socketpair()
@ -115,6 +121,72 @@ Example of *pipe*
59004 ops/sec 59004 ops/sec
--------------------- ---------------------
SUITES FOR 'mem'
~~~~~~~~~~~~~~~~
*memcpy*::
Suite for evaluating performance of simple memory copy in various ways.
Options of *memcpy*
^^^^^^^^^^^^^^^^^^^
-l::
--length::
Specify length of memory to copy (default: 1MB).
Available units are B, KB, MB, GB and TB (case insensitive).
-r::
--routine::
Specify routine to copy (default: default).
Available routines are depend on the architecture.
On x86-64, x86-64-unrolled, x86-64-movsq and x86-64-movsb are supported.
-i::
--iterations::
Repeat memcpy invocation this number of times.
-c::
--cycle::
Use perf's cpu-cycles event instead of gettimeofday syscall.
-o::
--only-prefault::
Show only the result with page faults before memcpy.
-n::
--no-prefault::
Show only the result without page faults before memcpy.
*memset*::
Suite for evaluating performance of simple memory set in various ways.
Options of *memset*
^^^^^^^^^^^^^^^^^^^
-l::
--length::
Specify length of memory to set (default: 1MB).
Available units are B, KB, MB, GB and TB (case insensitive).
-r::
--routine::
Specify routine to set (default: default).
Available routines are depend on the architecture.
On x86-64, x86-64-unrolled, x86-64-stosq and x86-64-stosb are supported.
-i::
--iterations::
Repeat memset invocation this number of times.
-c::
--cycle::
Use perf's cpu-cycles event instead of gettimeofday syscall.
-o::
--only-prefault::
Show only the result with page faults before memset.
-n::
--no-prefault::
Show only the result without page faults before memset.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf[1] linkperf:perf[1]

View File

@ -57,7 +57,7 @@ OPTIONS
-s:: -s::
--sort=:: --sort=::
Sort by key(s): pid, comm, dso, symbol, parent. Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-p:: -p::
--parent=<regex>:: --parent=<regex>::

View File

@ -112,7 +112,7 @@ Default is to monitor all CPUS.
-s:: -s::
--sort:: --sort::
Sort by key(s): pid, comm, dso, symbol, parent Sort by key(s): pid, comm, dso, symbol, parent, srcline.
-n:: -n::
--show-nr-samples:: --show-nr-samples::

View File

@ -155,7 +155,7 @@ endif
### --- END CONFIGURATION SECTION --- ### --- END CONFIGURATION SECTION ---
BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
BASIC_LDFLAGS = BASIC_LDFLAGS =
# Guard against environment variables # Guard against environment variables
@ -503,6 +503,7 @@ else
LIB_OBJS += $(OUTPUT)ui/progress.o LIB_OBJS += $(OUTPUT)ui/progress.o
LIB_OBJS += $(OUTPUT)ui/util.o LIB_OBJS += $(OUTPUT)ui/util.o
LIB_OBJS += $(OUTPUT)ui/tui/setup.o LIB_OBJS += $(OUTPUT)ui/tui/setup.o
LIB_OBJS += $(OUTPUT)ui/tui/util.o
LIB_H += ui/browser.h LIB_H += ui/browser.h
LIB_H += ui/browsers/map.h LIB_H += ui/browsers/map.h
LIB_H += ui/helpline.h LIB_H += ui/helpline.h
@ -522,13 +523,18 @@ else
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
BASIC_CFLAGS += -DNO_GTK2_SUPPORT BASIC_CFLAGS += -DNO_GTK2_SUPPORT
else else
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
endif
BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0) BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
EXTLIBS += $(shell pkg-config --libs gtk+-2.0) EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
# Make sure that it'd be included only once. # Make sure that it'd be included only once.
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),) ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/util.o
endif endif
endif endif
endif endif

View File

@ -24,21 +24,21 @@
static const char *length_str = "1MB"; static const char *length_str = "1MB";
static const char *routine = "default"; static const char *routine = "default";
static int iterations = 1; static int iterations = 1;
static bool use_clock; static bool use_cycle;
static int clock_fd; static int cycle_fd;
static bool only_prefault; static bool only_prefault;
static bool no_prefault; static bool no_prefault;
static const struct option options[] = { static const struct option options[] = {
OPT_STRING('l', "length", &length_str, "1MB", OPT_STRING('l', "length", &length_str, "1MB",
"Specify length of memory to copy. " "Specify length of memory to copy. "
"available unit: B, MB, GB (upper and lower)"), "Available units: B, KB, MB, GB and TB (upper and lower)"),
OPT_STRING('r', "routine", &routine, "default", OPT_STRING('r', "routine", &routine, "default",
"Specify routine to copy"), "Specify routine to copy"),
OPT_INTEGER('i', "iterations", &iterations, OPT_INTEGER('i', "iterations", &iterations,
"repeat memcpy() invocation this number of times"), "repeat memcpy() invocation this number of times"),
OPT_BOOLEAN('c', "clock", &use_clock, OPT_BOOLEAN('c', "cycle", &use_cycle,
"Use CPU clock for measuring"), "Use cycles event instead of gettimeofday() for measuring"),
OPT_BOOLEAN('o', "only-prefault", &only_prefault, OPT_BOOLEAN('o', "only-prefault", &only_prefault,
"Show only the result with page faults before memcpy()"), "Show only the result with page faults before memcpy()"),
OPT_BOOLEAN('n', "no-prefault", &no_prefault, OPT_BOOLEAN('n', "no-prefault", &no_prefault,
@ -76,27 +76,27 @@ static const char * const bench_mem_memcpy_usage[] = {
NULL NULL
}; };
static struct perf_event_attr clock_attr = { static struct perf_event_attr cycle_attr = {
.type = PERF_TYPE_HARDWARE, .type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES .config = PERF_COUNT_HW_CPU_CYCLES
}; };
static void init_clock(void) static void init_cycle(void)
{ {
clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
if (clock_fd < 0 && errno == ENOSYS) if (cycle_fd < 0 && errno == ENOSYS)
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
else else
BUG_ON(clock_fd < 0); BUG_ON(cycle_fd < 0);
} }
static u64 get_clock(void) static u64 get_cycle(void)
{ {
int ret; int ret;
u64 clk; u64 clk;
ret = read(clock_fd, &clk, sizeof(u64)); ret = read(cycle_fd, &clk, sizeof(u64));
BUG_ON(ret != sizeof(u64)); BUG_ON(ret != sizeof(u64));
return clk; return clk;
@ -119,9 +119,9 @@ static void alloc_mem(void **dst, void **src, size_t length)
die("memory allocation failed - maybe length is too large?\n"); die("memory allocation failed - maybe length is too large?\n");
} }
static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault)
{ {
u64 clock_start = 0ULL, clock_end = 0ULL; u64 cycle_start = 0ULL, cycle_end = 0ULL;
void *src = NULL, *dst = NULL; void *src = NULL, *dst = NULL;
int i; int i;
@ -130,14 +130,14 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
if (prefault) if (prefault)
fn(dst, src, len); fn(dst, src, len);
clock_start = get_clock(); cycle_start = get_cycle();
for (i = 0; i < iterations; ++i) for (i = 0; i < iterations; ++i)
fn(dst, src, len); fn(dst, src, len);
clock_end = get_clock(); cycle_end = get_cycle();
free(src); free(src);
free(dst); free(dst);
return clock_end - clock_start; return cycle_end - cycle_start;
} }
static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
@ -182,17 +182,17 @@ int bench_mem_memcpy(int argc, const char **argv,
int i; int i;
size_t len; size_t len;
double result_bps[2]; double result_bps[2];
u64 result_clock[2]; u64 result_cycle[2];
argc = parse_options(argc, argv, options, argc = parse_options(argc, argv, options,
bench_mem_memcpy_usage, 0); bench_mem_memcpy_usage, 0);
if (use_clock) if (use_cycle)
init_clock(); init_cycle();
len = (size_t)perf_atoll((char *)length_str); len = (size_t)perf_atoll((char *)length_str);
result_clock[0] = result_clock[1] = 0ULL; result_cycle[0] = result_cycle[1] = 0ULL;
result_bps[0] = result_bps[1] = 0.0; result_bps[0] = result_bps[1] = 0.0;
if ((s64)len <= 0) { if ((s64)len <= 0) {
@ -223,11 +223,11 @@ int bench_mem_memcpy(int argc, const char **argv,
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
/* show both of results */ /* show both of results */
if (use_clock) { if (use_cycle) {
result_clock[0] = result_cycle[0] =
do_memcpy_clock(routines[i].fn, len, false); do_memcpy_cycle(routines[i].fn, len, false);
result_clock[1] = result_cycle[1] =
do_memcpy_clock(routines[i].fn, len, true); do_memcpy_cycle(routines[i].fn, len, true);
} else { } else {
result_bps[0] = result_bps[0] =
do_memcpy_gettimeofday(routines[i].fn, do_memcpy_gettimeofday(routines[i].fn,
@ -237,9 +237,9 @@ int bench_mem_memcpy(int argc, const char **argv,
len, true); len, true);
} }
} else { } else {
if (use_clock) { if (use_cycle) {
result_clock[pf] = result_cycle[pf] =
do_memcpy_clock(routines[i].fn, do_memcpy_cycle(routines[i].fn,
len, only_prefault); len, only_prefault);
} else { } else {
result_bps[pf] = result_bps[pf] =
@ -251,12 +251,12 @@ int bench_mem_memcpy(int argc, const char **argv,
switch (bench_format) { switch (bench_format) {
case BENCH_FORMAT_DEFAULT: case BENCH_FORMAT_DEFAULT:
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
if (use_clock) { if (use_cycle) {
printf(" %14lf Clock/Byte\n", printf(" %14lf Cycle/Byte\n",
(double)result_clock[0] (double)result_cycle[0]
/ (double)len); / (double)len);
printf(" %14lf Clock/Byte (with prefault)\n", printf(" %14lf Cycle/Byte (with prefault)\n",
(double)result_clock[1] (double)result_cycle[1]
/ (double)len); / (double)len);
} else { } else {
print_bps(result_bps[0]); print_bps(result_bps[0]);
@ -265,9 +265,9 @@ int bench_mem_memcpy(int argc, const char **argv,
printf(" (with prefault)\n"); printf(" (with prefault)\n");
} }
} else { } else {
if (use_clock) { if (use_cycle) {
printf(" %14lf Clock/Byte", printf(" %14lf Cycle/Byte",
(double)result_clock[pf] (double)result_cycle[pf]
/ (double)len); / (double)len);
} else } else
print_bps(result_bps[pf]); print_bps(result_bps[pf]);
@ -277,17 +277,17 @@ int bench_mem_memcpy(int argc, const char **argv,
break; break;
case BENCH_FORMAT_SIMPLE: case BENCH_FORMAT_SIMPLE:
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
if (use_clock) { if (use_cycle) {
printf("%lf %lf\n", printf("%lf %lf\n",
(double)result_clock[0] / (double)len, (double)result_cycle[0] / (double)len,
(double)result_clock[1] / (double)len); (double)result_cycle[1] / (double)len);
} else { } else {
printf("%lf %lf\n", printf("%lf %lf\n",
result_bps[0], result_bps[1]); result_bps[0], result_bps[1]);
} }
} else { } else {
if (use_clock) { if (use_cycle) {
printf("%lf\n", (double)result_clock[pf] printf("%lf\n", (double)result_cycle[pf]
/ (double)len); / (double)len);
} else } else
printf("%lf\n", result_bps[pf]); printf("%lf\n", result_bps[pf]);

View File

@ -24,21 +24,21 @@
static const char *length_str = "1MB"; static const char *length_str = "1MB";
static const char *routine = "default"; static const char *routine = "default";
static int iterations = 1; static int iterations = 1;
static bool use_clock; static bool use_cycle;
static int clock_fd; static int cycle_fd;
static bool only_prefault; static bool only_prefault;
static bool no_prefault; static bool no_prefault;
static const struct option options[] = { static const struct option options[] = {
OPT_STRING('l', "length", &length_str, "1MB", OPT_STRING('l', "length", &length_str, "1MB",
"Specify length of memory to copy. " "Specify length of memory to set. "
"available unit: B, MB, GB (upper and lower)"), "Available units: B, KB, MB, GB and TB (upper and lower)"),
OPT_STRING('r', "routine", &routine, "default", OPT_STRING('r', "routine", &routine, "default",
"Specify routine to copy"), "Specify routine to set"),
OPT_INTEGER('i', "iterations", &iterations, OPT_INTEGER('i', "iterations", &iterations,
"repeat memset() invocation this number of times"), "repeat memset() invocation this number of times"),
OPT_BOOLEAN('c', "clock", &use_clock, OPT_BOOLEAN('c', "cycle", &use_cycle,
"Use CPU clock for measuring"), "Use cycles event instead of gettimeofday() for measuring"),
OPT_BOOLEAN('o', "only-prefault", &only_prefault, OPT_BOOLEAN('o', "only-prefault", &only_prefault,
"Show only the result with page faults before memset()"), "Show only the result with page faults before memset()"),
OPT_BOOLEAN('n', "no-prefault", &no_prefault, OPT_BOOLEAN('n', "no-prefault", &no_prefault,
@ -76,27 +76,27 @@ static const char * const bench_mem_memset_usage[] = {
NULL NULL
}; };
static struct perf_event_attr clock_attr = { static struct perf_event_attr cycle_attr = {
.type = PERF_TYPE_HARDWARE, .type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES .config = PERF_COUNT_HW_CPU_CYCLES
}; };
static void init_clock(void) static void init_cycle(void)
{ {
clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
if (clock_fd < 0 && errno == ENOSYS) if (cycle_fd < 0 && errno == ENOSYS)
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
else else
BUG_ON(clock_fd < 0); BUG_ON(cycle_fd < 0);
} }
static u64 get_clock(void) static u64 get_cycle(void)
{ {
int ret; int ret;
u64 clk; u64 clk;
ret = read(clock_fd, &clk, sizeof(u64)); ret = read(cycle_fd, &clk, sizeof(u64));
BUG_ON(ret != sizeof(u64)); BUG_ON(ret != sizeof(u64));
return clk; return clk;
@ -115,9 +115,9 @@ static void alloc_mem(void **dst, size_t length)
die("memory allocation failed - maybe length is too large?\n"); die("memory allocation failed - maybe length is too large?\n");
} }
static u64 do_memset_clock(memset_t fn, size_t len, bool prefault) static u64 do_memset_cycle(memset_t fn, size_t len, bool prefault)
{ {
u64 clock_start = 0ULL, clock_end = 0ULL; u64 cycle_start = 0ULL, cycle_end = 0ULL;
void *dst = NULL; void *dst = NULL;
int i; int i;
@ -126,13 +126,13 @@ static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)
if (prefault) if (prefault)
fn(dst, -1, len); fn(dst, -1, len);
clock_start = get_clock(); cycle_start = get_cycle();
for (i = 0; i < iterations; ++i) for (i = 0; i < iterations; ++i)
fn(dst, i, len); fn(dst, i, len);
clock_end = get_clock(); cycle_end = get_cycle();
free(dst); free(dst);
return clock_end - clock_start; return cycle_end - cycle_start;
} }
static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault) static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault)
@ -176,17 +176,17 @@ int bench_mem_memset(int argc, const char **argv,
int i; int i;
size_t len; size_t len;
double result_bps[2]; double result_bps[2];
u64 result_clock[2]; u64 result_cycle[2];
argc = parse_options(argc, argv, options, argc = parse_options(argc, argv, options,
bench_mem_memset_usage, 0); bench_mem_memset_usage, 0);
if (use_clock) if (use_cycle)
init_clock(); init_cycle();
len = (size_t)perf_atoll((char *)length_str); len = (size_t)perf_atoll((char *)length_str);
result_clock[0] = result_clock[1] = 0ULL; result_cycle[0] = result_cycle[1] = 0ULL;
result_bps[0] = result_bps[1] = 0.0; result_bps[0] = result_bps[1] = 0.0;
if ((s64)len <= 0) { if ((s64)len <= 0) {
@ -217,11 +217,11 @@ int bench_mem_memset(int argc, const char **argv,
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
/* show both of results */ /* show both of results */
if (use_clock) { if (use_cycle) {
result_clock[0] = result_cycle[0] =
do_memset_clock(routines[i].fn, len, false); do_memset_cycle(routines[i].fn, len, false);
result_clock[1] = result_cycle[1] =
do_memset_clock(routines[i].fn, len, true); do_memset_cycle(routines[i].fn, len, true);
} else { } else {
result_bps[0] = result_bps[0] =
do_memset_gettimeofday(routines[i].fn, do_memset_gettimeofday(routines[i].fn,
@ -231,9 +231,9 @@ int bench_mem_memset(int argc, const char **argv,
len, true); len, true);
} }
} else { } else {
if (use_clock) { if (use_cycle) {
result_clock[pf] = result_cycle[pf] =
do_memset_clock(routines[i].fn, do_memset_cycle(routines[i].fn,
len, only_prefault); len, only_prefault);
} else { } else {
result_bps[pf] = result_bps[pf] =
@ -245,12 +245,12 @@ int bench_mem_memset(int argc, const char **argv,
switch (bench_format) { switch (bench_format) {
case BENCH_FORMAT_DEFAULT: case BENCH_FORMAT_DEFAULT:
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
if (use_clock) { if (use_cycle) {
printf(" %14lf Clock/Byte\n", printf(" %14lf Cycle/Byte\n",
(double)result_clock[0] (double)result_cycle[0]
/ (double)len); / (double)len);
printf(" %14lf Clock/Byte (with prefault)\n ", printf(" %14lf Cycle/Byte (with prefault)\n ",
(double)result_clock[1] (double)result_cycle[1]
/ (double)len); / (double)len);
} else { } else {
print_bps(result_bps[0]); print_bps(result_bps[0]);
@ -259,9 +259,9 @@ int bench_mem_memset(int argc, const char **argv,
printf(" (with prefault)\n"); printf(" (with prefault)\n");
} }
} else { } else {
if (use_clock) { if (use_cycle) {
printf(" %14lf Clock/Byte", printf(" %14lf Cycle/Byte",
(double)result_clock[pf] (double)result_cycle[pf]
/ (double)len); / (double)len);
} else } else
print_bps(result_bps[pf]); print_bps(result_bps[pf]);
@ -271,17 +271,17 @@ int bench_mem_memset(int argc, const char **argv,
break; break;
case BENCH_FORMAT_SIMPLE: case BENCH_FORMAT_SIMPLE:
if (!only_prefault && !no_prefault) { if (!only_prefault && !no_prefault) {
if (use_clock) { if (use_cycle) {
printf("%lf %lf\n", printf("%lf %lf\n",
(double)result_clock[0] / (double)len, (double)result_cycle[0] / (double)len,
(double)result_clock[1] / (double)len); (double)result_cycle[1] / (double)len);
} else { } else {
printf("%lf %lf\n", printf("%lf %lf\n",
result_bps[0], result_bps[1]); result_bps[0], result_bps[1]);
} }
} else { } else {
if (use_clock) { if (use_cycle) {
printf("%lf\n", (double)result_clock[pf] printf("%lf\n", (double)result_cycle[pf]
/ (double)len); / (double)len);
} else } else
printf("%lf\n", result_bps[pf]); printf("%lf\n", result_bps[pf]);

View File

@ -33,7 +33,7 @@ struct bench_suite {
}; };
\ \
/* sentinel: easy for help */ /* sentinel: easy for help */
#define suite_all { "all", "test all suite (pseudo suite)", NULL } #define suite_all { "all", "Test all benchmark suites", NULL }
static struct bench_suite sched_suites[] = { static struct bench_suite sched_suites[] = {
{ "messaging", { "messaging",
@ -75,7 +75,7 @@ static struct bench_subsys subsystems[] = {
"memory access performance", "memory access performance",
mem_suites }, mem_suites },
{ "all", /* sentinel: easy for help */ { "all", /* sentinel: easy for help */
"test all subsystem (pseudo subsystem)", "all benchmark subsystem",
NULL }, NULL },
{ NULL, { NULL,
NULL, NULL,

View File

@ -60,7 +60,7 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail
list_for_each_entry(pos, &session->evlist->entries, node) { list_for_each_entry(pos, &session->evlist->entries, node) {
bool first = true; bool first = true;
printf("%s", event_name(pos)); printf("%s", perf_evsel__name(pos));
if (details->verbose || details->freq) { if (details->verbose || details->freq) {
comma_printf(&first, " sample_freq=%" PRIu64, comma_printf(&first, " sample_freq=%" PRIu64,

View File

@ -57,6 +57,11 @@ static unsigned long nr_allocs, nr_cross_allocs;
#define PATH_SYS_NODE "/sys/devices/system/node" #define PATH_SYS_NODE "/sys/devices/system/node"
struct perf_kmem {
struct perf_tool tool;
struct perf_session *session;
};
static void init_cpunode_map(void) static void init_cpunode_map(void)
{ {
FILE *fp; FILE *fp;
@ -278,14 +283,16 @@ static void process_free_event(void *data,
s_alloc->alloc_cpu = -1; s_alloc->alloc_cpu = -1;
} }
static void process_raw_event(union perf_event *raw_event __used, void *data, static void process_raw_event(struct perf_tool *tool,
union perf_event *raw_event __used, void *data,
int cpu, u64 timestamp, struct thread *thread) int cpu, u64 timestamp, struct thread *thread)
{ {
struct perf_kmem *kmem = container_of(tool, struct perf_kmem, tool);
struct event_format *event; struct event_format *event;
int type; int type;
type = trace_parse_common_type(data); type = trace_parse_common_type(kmem->session->pevent, data);
event = trace_find_event(type); event = pevent_find_event(kmem->session->pevent, type);
if (!strcmp(event->name, "kmalloc") || if (!strcmp(event->name, "kmalloc") ||
!strcmp(event->name, "kmem_cache_alloc")) { !strcmp(event->name, "kmem_cache_alloc")) {
@ -306,7 +313,7 @@ static void process_raw_event(union perf_event *raw_event __used, void *data,
} }
} }
static int process_sample_event(struct perf_tool *tool __used, static int process_sample_event(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel __used, struct perf_evsel *evsel __used,
@ -322,16 +329,18 @@ static int process_sample_event(struct perf_tool *tool __used,
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
process_raw_event(event, sample->raw_data, sample->cpu, process_raw_event(tool, event, sample->raw_data, sample->cpu,
sample->time, thread); sample->time, thread);
return 0; return 0;
} }
static struct perf_tool perf_kmem = { static struct perf_kmem perf_kmem = {
.sample = process_sample_event, .tool = {
.comm = perf_event__process_comm, .sample = process_sample_event,
.ordered_samples = true, .comm = perf_event__process_comm,
.ordered_samples = true,
},
}; };
static double fragmentation(unsigned long n_req, unsigned long n_alloc) static double fragmentation(unsigned long n_req, unsigned long n_alloc)
@ -486,11 +495,15 @@ static void sort_result(void)
static int __cmd_kmem(void) static int __cmd_kmem(void)
{ {
int err = -EINVAL; int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, struct perf_session *session;
0, false, &perf_kmem);
session = perf_session__new(input_name, O_RDONLY, 0, false,
&perf_kmem.tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
perf_kmem.session = session;
if (perf_session__create_kernel_maps(session) < 0) if (perf_session__create_kernel_maps(session) < 0)
goto out_delete; goto out_delete;
@ -498,7 +511,7 @@ static int __cmd_kmem(void)
goto out_delete; goto out_delete;
setup_pager(); setup_pager();
err = perf_session__process_events(session, &perf_kmem); err = perf_session__process_events(session, &perf_kmem.tool);
if (err != 0) if (err != 0)
goto out_delete; goto out_delete;
sort_result(); sort_result();

View File

@ -724,8 +724,8 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
struct event_format *event; struct event_format *event;
int type; int type;
type = trace_parse_common_type(data); type = trace_parse_common_type(session->pevent, data);
event = trace_find_event(type); event = pevent_find_event(session->pevent, type);
if (!strcmp(event->name, "lock_acquire")) if (!strcmp(event->name, "lock_acquire"))
process_lock_acquire_event(data, event, cpu, timestamp, thread); process_lock_acquire_event(data, event, cpu, timestamp, thread);

View File

@ -265,7 +265,7 @@ static void perf_record__open(struct perf_record *rec)
if (err == ENOENT) { if (err == ENOENT) {
ui__error("The %s event is not supported.\n", ui__error("The %s event is not supported.\n",
event_name(pos)); perf_evsel__name(pos));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -916,7 +916,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
usage_with_options(record_usage, record_options); usage_with_options(record_usage, record_options);
list_for_each_entry(pos, &evsel_list->entries, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_header__push_event(pos->attr.config, event_name(pos))) if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos)))
goto out_free_fd; goto out_free_fd;
} }

View File

@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) { && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, al->thread,
sample->callchain, &parent); sample->callchain, &parent);
if (err) if (err)
return err; return err;
@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct hist_entry *he; struct hist_entry *he;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, al->thread,
sample->callchain, &parent); sample->callchain, &parent);
if (err) if (err)
return err; return err;
@ -230,7 +230,7 @@ static int process_read_event(struct perf_tool *tool,
struct perf_report *rep = container_of(tool, struct perf_report, tool); struct perf_report *rep = container_of(tool, struct perf_report, tool);
if (rep->show_threads) { if (rep->show_threads) {
const char *name = evsel ? event_name(evsel) : "unknown"; const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
perf_read_values_add_value(&rep->show_threads_values, perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid, event->read.pid, event->read.tid,
event->read.id, event->read.id,
@ -239,17 +239,18 @@ static int process_read_event(struct perf_tool *tool,
} }
dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
evsel ? event_name(evsel) : "FAIL", evsel ? perf_evsel__name(evsel) : "FAIL",
event->read.value); event->read.value);
return 0; return 0;
} }
/* For pipe mode, sample_type is not currently set */
static int perf_report__setup_sample_type(struct perf_report *rep) static int perf_report__setup_sample_type(struct perf_report *rep)
{ {
struct perf_session *self = rep->session; struct perf_session *self = rep->session;
if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { if (!self->fd_pipe && !(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) { if (sort__has_parent) {
ui__error("Selected --sort parent, but no " ui__error("Selected --sort parent, but no "
"callchain data. Did you call " "callchain data. Did you call "
@ -272,7 +273,8 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
} }
if (sort__branch_mode == 1) { if (sort__branch_mode == 1) {
if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) { if (!self->fd_pipe &&
!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
ui__error("Selected -b but no branch data. " ui__error("Selected -b but no branch data. "
"Did you call perf record without -b?\n"); "Did you call perf record without -b?\n");
return -1; return -1;
@ -314,7 +316,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists; struct hists *hists = &pos->hists;
const char *evname = event_name(pos); const char *evname = perf_evsel__name(pos);
hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, NULL, false, true, 0, 0, stdout); hists__fprintf(hists, NULL, false, true, 0, 0, stdout);

View File

@ -43,6 +43,11 @@ static u64 sleep_measurement_overhead;
static unsigned long nr_tasks; static unsigned long nr_tasks;
struct perf_sched {
struct perf_tool tool;
struct perf_session *session;
};
struct sched_atom; struct sched_atom;
struct task_desc { struct task_desc {
@ -1597,11 +1602,13 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct machine *machine) struct machine *machine)
{ {
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
struct pevent *pevent = sched->session->pevent;
struct thread *thread = machine__findnew_thread(machine, sample->pid); struct thread *thread = machine__findnew_thread(machine, sample->pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %s event, skipping it.\n", pr_debug("problem processing %s event, skipping it.\n",
evsel->name); perf_evsel__name(evsel));
return -1; return -1;
} }
@ -1612,7 +1619,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
tracepoint_handler f = evsel->handler.func; tracepoint_handler f = evsel->handler.func;
if (evsel->handler.data == NULL) if (evsel->handler.data == NULL)
evsel->handler.data = trace_find_event(evsel->attr.config); evsel->handler.data = pevent_find_event(pevent,
evsel->attr.config);
f(tool, evsel->handler.data, sample, machine, thread); f(tool, evsel->handler.data, sample, machine, thread);
} }
@ -1620,12 +1628,14 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
return 0; return 0;
} }
static struct perf_tool perf_sched = { static struct perf_sched perf_sched = {
.sample = perf_sched__process_tracepoint_sample, .tool = {
.comm = perf_event__process_comm, .sample = perf_sched__process_tracepoint_sample,
.lost = perf_event__process_lost, .comm = perf_event__process_comm,
.fork = perf_event__process_task, .lost = perf_event__process_lost,
.ordered_samples = true, .fork = perf_event__process_task,
.ordered_samples = true,
},
}; };
static void read_events(bool destroy, struct perf_session **psession) static void read_events(bool destroy, struct perf_session **psession)
@ -1640,16 +1650,20 @@ static void read_events(bool destroy, struct perf_session **psession)
{ "sched:sched_process_exit", process_sched_exit_event, }, { "sched:sched_process_exit", process_sched_exit_event, },
{ "sched:sched_migrate_task", process_sched_migrate_task_event, }, { "sched:sched_migrate_task", process_sched_migrate_task_event, },
}; };
struct perf_session *session = perf_session__new(input_name, O_RDONLY, struct perf_session *session;
0, false, &perf_sched);
session = perf_session__new(input_name, O_RDONLY, 0, false,
&perf_sched.tool);
if (session == NULL) if (session == NULL)
die("No Memory"); die("No Memory");
err = perf_evlist__set_tracepoints_handlers_array(session->evlist, handlers); perf_sched.session = session;
err = perf_session__set_tracepoints_handlers(session, handlers);
assert(err == 0); assert(err == 0);
if (perf_session__has_traces(session, "record -R")) { if (perf_session__has_traces(session, "record -R")) {
err = perf_session__process_events(session, &perf_sched); err = perf_session__process_events(session, &perf_sched.tool);
if (err) if (err)
die("Failed to process events, error %d", err); die("Failed to process events, error %d", err);

View File

@ -28,6 +28,11 @@ static bool system_wide;
static const char *cpu_list; static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
struct perf_script {
struct perf_tool tool;
struct perf_session *session;
};
enum perf_output_field { enum perf_output_field {
PERF_OUTPUT_COMM = 1U << 0, PERF_OUTPUT_COMM = 1U << 0,
PERF_OUTPUT_TID = 1U << 1, PERF_OUTPUT_TID = 1U << 1,
@ -137,10 +142,11 @@ static const char *output_field2str(enum perf_output_field field)
#define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x)
static int perf_event_attr__check_stype(struct perf_event_attr *attr, static int perf_evsel__check_stype(struct perf_evsel *evsel,
u64 sample_type, const char *sample_msg, u64 sample_type, const char *sample_msg,
enum perf_output_field field) enum perf_output_field field)
{ {
struct perf_event_attr *attr = &evsel->attr;
int type = attr->type; int type = attr->type;
const char *evname; const char *evname;
@ -148,7 +154,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
return 0; return 0;
if (output[type].user_set) { if (output[type].user_set) {
evname = __event_name(attr->type, attr->config); evname = perf_evsel__name(evsel);
pr_err("Samples for '%s' event do not have %s attribute set. " pr_err("Samples for '%s' event do not have %s attribute set. "
"Cannot print '%s' field.\n", "Cannot print '%s' field.\n",
evname, sample_msg, output_field2str(field)); evname, sample_msg, output_field2str(field));
@ -157,7 +163,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
/* user did not ask for it explicitly so remove from the default list */ /* user did not ask for it explicitly so remove from the default list */
output[type].fields &= ~field; output[type].fields &= ~field;
evname = __event_name(attr->type, attr->config); evname = perf_evsel__name(evsel);
pr_debug("Samples for '%s' event do not have %s attribute set. " pr_debug("Samples for '%s' event do not have %s attribute set. "
"Skipping '%s' field.\n", "Skipping '%s' field.\n",
evname, sample_msg, output_field2str(field)); evname, sample_msg, output_field2str(field));
@ -175,8 +181,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(IP)) { if (PRINT_FIELD(IP)) {
if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
PERF_OUTPUT_IP)) PERF_OUTPUT_IP))
return -EINVAL; return -EINVAL;
if (!no_callchain && if (!no_callchain &&
@ -185,8 +191,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
} }
if (PRINT_FIELD(ADDR) && if (PRINT_FIELD(ADDR) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
PERF_OUTPUT_ADDR)) PERF_OUTPUT_ADDR))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
@ -208,18 +214,18 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
} }
if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
PERF_OUTPUT_TID|PERF_OUTPUT_PID)) PERF_OUTPUT_TID|PERF_OUTPUT_PID))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(TIME) && if (PRINT_FIELD(TIME) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
PERF_OUTPUT_TIME)) PERF_OUTPUT_TIME))
return -EINVAL; return -EINVAL;
if (PRINT_FIELD(CPU) && if (PRINT_FIELD(CPU) &&
perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
PERF_OUTPUT_CPU)) PERF_OUTPUT_CPU))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -256,11 +262,13 @@ static int perf_session__check_output_opt(struct perf_session *session)
return 0; return 0;
} }
static void print_sample_start(struct perf_sample *sample, static void print_sample_start(struct pevent *pevent,
struct perf_sample *sample,
struct thread *thread, struct thread *thread,
struct perf_event_attr *attr) struct perf_evsel *evsel)
{ {
int type; int type;
struct perf_event_attr *attr = &evsel->attr;
struct event_format *event; struct event_format *event;
const char *evname = NULL; const char *evname = NULL;
unsigned long secs; unsigned long secs;
@ -300,12 +308,18 @@ static void print_sample_start(struct perf_sample *sample,
if (PRINT_FIELD(EVNAME)) { if (PRINT_FIELD(EVNAME)) {
if (attr->type == PERF_TYPE_TRACEPOINT) { if (attr->type == PERF_TYPE_TRACEPOINT) {
type = trace_parse_common_type(sample->raw_data); /*
event = trace_find_event(type); * XXX Do we really need this here?
* perf_evlist__set_tracepoint_names should have done
* this already
*/
type = trace_parse_common_type(pevent,
sample->raw_data);
event = pevent_find_event(pevent, type);
if (event) if (event)
evname = event->name; evname = event->name;
} else } else
evname = __event_name(attr->type, attr->config); evname = perf_evsel__name(evsel);
printf("%s: ", evname ? evname : "[unknown]"); printf("%s: ", evname ? evname : "[unknown]");
} }
@ -387,7 +401,7 @@ static void print_sample_bts(union perf_event *event,
printf(" "); printf(" ");
else else
printf("\n"); printf("\n");
perf_event__print_ip(event, sample, machine, evsel, perf_event__print_ip(event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO), PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET)); PRINT_FIELD(SYMOFFSET));
} }
@ -402,6 +416,7 @@ static void print_sample_bts(union perf_event *event,
} }
static void process_event(union perf_event *event __unused, static void process_event(union perf_event *event __unused,
struct pevent *pevent,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct machine *machine, struct machine *machine,
@ -412,7 +427,7 @@ static void process_event(union perf_event *event __unused,
if (output[attr->type].fields == 0) if (output[attr->type].fields == 0)
return; return;
print_sample_start(sample, thread, attr); print_sample_start(pevent, sample, thread, evsel);
if (is_bts_event(attr)) { if (is_bts_event(attr)) {
print_sample_bts(event, sample, evsel, machine, thread); print_sample_bts(event, sample, evsel, machine, thread);
@ -420,7 +435,7 @@ static void process_event(union perf_event *event __unused,
} }
if (PRINT_FIELD(TRACE)) if (PRINT_FIELD(TRACE))
print_trace_event(sample->cpu, sample->raw_data, print_trace_event(pevent, sample->cpu, sample->raw_data,
sample->raw_size); sample->raw_size);
if (PRINT_FIELD(ADDR)) if (PRINT_FIELD(ADDR))
@ -431,7 +446,7 @@ static void process_event(union perf_event *event __unused,
printf(" "); printf(" ");
else else
printf("\n"); printf("\n");
perf_event__print_ip(event, sample, machine, evsel, perf_event__print_ip(event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO), PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET)); PRINT_FIELD(SYMOFFSET));
} }
@ -451,7 +466,8 @@ static int default_stop_script(void)
return 0; return 0;
} }
static int default_generate_script(const char *outfile __unused) static int default_generate_script(struct pevent *pevent __unused,
const char *outfile __unused)
{ {
return 0; return 0;
} }
@ -489,6 +505,7 @@ static int process_sample_event(struct perf_tool *tool __used,
struct machine *machine) struct machine *machine)
{ {
struct addr_location al; struct addr_location al;
struct perf_script *scr = container_of(tool, struct perf_script, tool);
struct thread *thread = machine__findnew_thread(machine, event->ip.tid); struct thread *thread = machine__findnew_thread(machine, event->ip.tid);
if (thread == NULL) { if (thread == NULL) {
@ -520,24 +537,27 @@ static int process_sample_event(struct perf_tool *tool __used,
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
return 0; return 0;
scripting_ops->process_event(event, sample, evsel, machine, thread); scripting_ops->process_event(event, scr->session->pevent,
sample, evsel, machine, thread);
evsel->hists.stats.total_period += sample->period; evsel->hists.stats.total_period += sample->period;
return 0; return 0;
} }
static struct perf_tool perf_script = { static struct perf_script perf_script = {
.sample = process_sample_event, .tool = {
.mmap = perf_event__process_mmap, .sample = process_sample_event,
.comm = perf_event__process_comm, .mmap = perf_event__process_mmap,
.exit = perf_event__process_task, .comm = perf_event__process_comm,
.fork = perf_event__process_task, .exit = perf_event__process_task,
.attr = perf_event__process_attr, .fork = perf_event__process_task,
.event_type = perf_event__process_event_type, .attr = perf_event__process_attr,
.tracing_data = perf_event__process_tracing_data, .event_type = perf_event__process_event_type,
.build_id = perf_event__process_build_id, .tracing_data = perf_event__process_tracing_data,
.ordered_samples = true, .build_id = perf_event__process_build_id,
.ordering_requires_timestamps = true, .ordered_samples = true,
.ordering_requires_timestamps = true,
},
}; };
extern volatile int session_done; extern volatile int session_done;
@ -553,7 +573,7 @@ static int __cmd_script(struct perf_session *session)
signal(SIGINT, sig_handler); signal(SIGINT, sig_handler);
ret = perf_session__process_events(session, &perf_script); ret = perf_session__process_events(session, &perf_script.tool);
if (debug_mode) if (debug_mode)
pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);
@ -1335,10 +1355,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
if (!script_name) if (!script_name)
setup_pager(); setup_pager();
session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_script); session = perf_session__new(input_name, O_RDONLY, 0, false,
&perf_script.tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
perf_script.session = session;
if (cpu_list) { if (cpu_list) {
if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))
return -1; return -1;
@ -1384,7 +1407,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
return -1; return -1;
} }
err = scripting_ops->generate_script("perf-script"); err = scripting_ops->generate_script(session->pevent,
"perf-script");
goto out; goto out;
} }

View File

@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
if (verbose) { if (verbose) {
fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", fprintf(output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
event_name(counter), count[0], count[1], count[2]); perf_evsel__name(counter), count[0], count[1], count[2]);
} }
/* /*
@ -496,7 +496,7 @@ static int run_perf_stat(int argc __used, const char **argv)
errno == ENXIO) { errno == ENXIO) {
if (verbose) if (verbose)
ui__warning("%s event is not supported by the kernel.\n", ui__warning("%s event is not supported by the kernel.\n",
event_name(counter)); perf_evsel__name(counter));
counter->supported = false; counter->supported = false;
continue; continue;
} }
@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
csv_output ? 0 : -4, csv_output ? 0 : -4,
evsel_list->cpus->map[cpu], csv_sep); evsel_list->cpus->map[cpu], csv_sep);
fprintf(output, fmt, cpustr, msecs, csv_sep, event_name(evsel)); fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));
if (evsel->cgrp) if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@ -792,7 +792,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
else else
cpu = 0; cpu = 0;
fprintf(output, fmt, cpustr, avg, csv_sep, event_name(evsel)); fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel));
if (evsel->cgrp) if (evsel->cgrp)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@ -908,7 +908,7 @@ static void print_counter_aggr(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep, csv_sep,
csv_output ? 0 : -24, csv_output ? 0 : -24,
event_name(counter)); perf_evsel__name(counter));
if (counter->cgrp) if (counter->cgrp)
fprintf(output, "%s%s", csv_sep, counter->cgrp->name); fprintf(output, "%s%s", csv_sep, counter->cgrp->name);
@ -961,7 +961,7 @@ static void print_counter(struct perf_evsel *counter)
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep, csv_sep,
csv_output ? 0 : -24, csv_output ? 0 : -24,
event_name(counter)); perf_evsel__name(counter));
if (counter->cgrp) if (counter->cgrp)
fprintf(output, "%s%s", fprintf(output, "%s%s",

View File

@ -583,7 +583,7 @@ static int test__basic_mmap(void)
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
pr_debug("expected %d %s events, got %d\n", pr_debug("expected %d %s events, got %d\n",
expected_nr_events[evsel->idx], expected_nr_events[evsel->idx],
event_name(evsel), nr_events[evsel->idx]); perf_evsel__name(evsel), nr_events[evsel->idx]);
goto out_munmap; goto out_munmap;
} }
} }

View File

@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL) if (notes->src == NULL)
goto out_unlock; goto out_unlock;
printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name); printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
@ -408,7 +408,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries); fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries);
if (top->evlist->nr_entries > 1) if (top->evlist->nr_entries > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top->sym_evsel)); fprintf(stdout, "\t[E] active event counter. \t(%s)\n", perf_evsel__name(top->sym_evsel));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter); fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
fprintf(stderr, "\nAvailable events:"); fprintf(stderr, "\nAvailable events:");
list_for_each_entry(top->sym_evsel, &top->evlist->entries, node) list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel)); fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));
prompt_integer(&counter, "Enter details event counter"); prompt_integer(&counter, "Enter details event counter");
if (counter >= top->evlist->nr_entries) { if (counter >= top->evlist->nr_entries) {
top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node); top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel)); fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
sleep(1); sleep(1);
break; break;
} }
@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) && if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) { sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al.thread, err = machine__resolve_callchain(machine, al.thread,
sample->callchain, &parent); sample->callchain, &parent);
if (err) if (err)
return; return;
@ -960,7 +960,7 @@ static void perf_top__start_counters(struct perf_top *top)
if (err == ENOENT) { if (err == ENOENT) {
ui__error("The %s event is not supported.\n", ui__error("The %s event is not supported.\n",
event_name(counter)); perf_evsel__name(counter));
goto out_err; goto out_err;
} else if (err == EMFILE) { } else if (err == EMFILE) {
ui__error("Too many events are opened.\n" ui__error("Too many events are opened.\n"

View File

@ -78,6 +78,19 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
endef endef
define SOURCE_GTK2_INFOBAR
#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
#include <gtk/gtk.h>
#pragma GCC diagnostic error \"-Wstrict-prototypes\"
int main(void)
{
gtk_info_bar_new();
return 0;
}
endef
endif endif
ifndef NO_LIBPERL ifndef NO_LIBPERL

View File

@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
{ {
struct disasm_line *pos, *n; struct disasm_line *pos, *n;
struct annotation *notes; struct annotation *notes;
const size_t size = symbol__size(sym); size_t size;
struct map_symbol ms = { struct map_symbol ms = {
.map = map, .map = map,
.sym = sym, .sym = sym,
@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
if (sym == NULL) if (sym == NULL)
return -1; return -1;
size = symbol__size(sym);
if (map->dso->annotate_warned) if (map->dso->annotate_warned)
return -1; return -1;

View File

@ -23,6 +23,7 @@ struct hist_browser {
struct hists *hists; struct hists *hists;
struct hist_entry *he_selection; struct hist_entry *he_selection;
struct map_symbol *selection; struct map_symbol *selection;
int print_seq;
bool has_symbols; bool has_symbols;
}; };
@ -800,6 +801,196 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
} }
} }
static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
struct callchain_node *chain_node,
u64 total, int level,
FILE *fp)
{
struct rb_node *node;
int offset = level * LEVEL_OFFSET_STEP;
u64 new_total, remaining;
int printed = 0;
if (callchain_param.mode == CHAIN_GRAPH_REL)
new_total = chain_node->children_hit;
else
new_total = total;
remaining = new_total;
node = rb_first(&chain_node->rb_root);
while (node) {
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
struct rb_node *next = rb_next(node);
u64 cumul = callchain_cumul_hits(child);
struct callchain_list *chain;
char folded_sign = ' ';
int first = true;
int extra_offset = 0;
remaining -= cumul;
list_for_each_entry(chain, &child->val, list) {
char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
const char *str;
bool was_first = first;
if (first)
first = false;
else
extra_offset = LEVEL_OFFSET_STEP;
folded_sign = callchain_list__folded(chain);
alloc_str = NULL;
str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
if (was_first) {
double percent = cumul * 100.0 / new_total;
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
str = "Not enough memory!";
else
str = alloc_str;
}
printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
free(alloc_str);
if (folded_sign == '+')
break;
}
if (folded_sign == '-') {
const int new_level = level + (extra_offset ? 2 : 1);
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
new_level, fp);
}
node = next;
}
return printed;
}
static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
struct callchain_node *node,
int level, FILE *fp)
{
struct callchain_list *chain;
int offset = level * LEVEL_OFFSET_STEP;
char folded_sign = ' ';
int printed = 0;
list_for_each_entry(chain, &node->val, list) {
char ipstr[BITS_PER_LONG / 4 + 1], *s;
folded_sign = callchain_list__folded(chain);
s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
}
if (folded_sign == '-')
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
browser->hists->stats.total_period,
level + 1, fp);
return printed;
}
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
struct rb_root *chain, int level, FILE *fp)
{
struct rb_node *nd;
int printed = 0;
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
}
return printed;
}
static int hist_browser__fprintf_entry(struct hist_browser *browser,
struct hist_entry *he, FILE *fp)
{
char s[8192];
double percent;
int printed = 0;
char folded_sign = ' ';
if (symbol_conf.use_callchain)
folded_sign = hist_entry__folded(he);
hist_entry__snprintf(he, s, sizeof(s), browser->hists);
percent = (he->period * 100.0) / browser->hists->stats.total_period;
if (symbol_conf.use_callchain)
printed += fprintf(fp, "%c ", folded_sign);
printed += fprintf(fp, " %5.2f%%", percent);
if (symbol_conf.show_nr_samples)
printed += fprintf(fp, " %11u", he->nr_events);
if (symbol_conf.show_total_period)
printed += fprintf(fp, " %12" PRIu64, he->period);
printed += fprintf(fp, "%s\n", rtrim(s));
if (folded_sign == '-')
printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
return printed;
}
static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
{
struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
int printed = 0;
while (nd) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
printed += hist_browser__fprintf_entry(browser, h, fp);
nd = hists__filter_entries(rb_next(nd));
}
return printed;
}
static int hist_browser__dump(struct hist_browser *browser)
{
char filename[64];
FILE *fp;
while (1) {
scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
if (access(filename, F_OK))
break;
/*
* XXX: Just an arbitrary lazy upper limit
*/
if (++browser->print_seq == 8192) {
ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
return -1;
}
}
fp = fopen(filename, "w");
if (fp == NULL) {
char bf[64];
strerror_r(errno, bf, sizeof(bf));
ui_helpline__fpush("Couldn't write to %s: %s", filename, bf);
return -1;
}
++browser->print_seq;
hist_browser__fprintf(browser, fp);
fclose(fp);
ui_helpline__fpush("%s written!", filename);
return 0;
}
static struct hist_browser *hist_browser__new(struct hists *hists) static struct hist_browser *hist_browser__new(struct hists *hists)
{ {
struct hist_browser *browser = zalloc(sizeof(*browser)); struct hist_browser *browser = zalloc(sizeof(*browser));
@ -937,6 +1128,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
browser->selection->map->dso->annotate_warned) browser->selection->map->dso->annotate_warned)
continue; continue;
goto do_annotate; goto do_annotate;
case 'P':
hist_browser__dump(browser);
continue;
case 'd': case 'd':
goto zoom_dso; goto zoom_dso;
case 't': case 't':
@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"E Expand all callchains\n" "E Expand all callchains\n"
"d Zoom into current DSO\n" "d Zoom into current DSO\n"
"t Zoom into current Thread\n" "t Zoom into current Thread\n"
"P Print histograms to perf.hist.N\n"
"/ Filter symbol by name"); "/ Filter symbol by name");
continue; continue;
case K_ENTER: case K_ENTER:
@ -1172,7 +1367,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
bool current_entry = ui_browser__is_current_entry(browser, row); bool current_entry = ui_browser__is_current_entry(browser, row);
unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
const char *ev_name = event_name(evsel); const char *ev_name = perf_evsel__name(evsel);
char bf[256], unit; char bf[256], unit;
const char *warn = " "; const char *warn = " ";
size_t printed; size_t printed;
@ -1240,7 +1435,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
*/ */
if (timer) if (timer)
timer(arg); timer(arg);
ev_name = event_name(pos); ev_name = perf_evsel__name(pos);
key = perf_evsel__hists_browse(pos, nr_events, help, key = perf_evsel__hists_browse(pos, nr_events, help,
ev_name, true, timer, ev_name, true, timer,
arg, delay_secs); arg, delay_secs);
@ -1309,17 +1504,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
ui_helpline__push("Press ESC to exit"); ui_helpline__push("Press ESC to exit");
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
const char *ev_name = event_name(pos); const char *ev_name = perf_evsel__name(pos);
size_t line_len = strlen(ev_name) + 7; size_t line_len = strlen(ev_name) + 7;
if (menu.b.width < line_len) if (menu.b.width < line_len)
menu.b.width = line_len; menu.b.width = line_len;
/*
* Cache the evsel name, tracepoints have a _high_ cost per
* event_name() call.
*/
if (pos->name == NULL)
pos->name = strdup(ev_name);
} }
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
void(*timer)(void *arg), void *arg, void(*timer)(void *arg), void *arg,
int delay_secs) int delay_secs)
{ {
if (evlist->nr_entries == 1) { if (evlist->nr_entries == 1) {
struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel *first = list_entry(evlist->entries.next,
struct perf_evsel, node); struct perf_evsel, node);
const char *ev_name = event_name(first); const char *ev_name = perf_evsel__name(first);
return perf_evsel__hists_browse(first, evlist->nr_entries, help, return perf_evsel__hists_browse(first, evlist->nr_entries, help,
ev_name, false, timer, arg, ev_name, false, timer, arg,
delay_secs); delay_secs);

View File

@ -11,8 +11,8 @@
static void perf_gtk__signal(int sig) static void perf_gtk__signal(int sig)
{ {
perf_gtk__exit(false);
psignal(sig, "perf"); psignal(sig, "perf");
gtk_main_quit();
} }
static void perf_gtk__resize_window(GtkWidget *window) static void perf_gtk__resize_window(GtkWidget *window)
@ -122,13 +122,59 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
gtk_container_add(GTK_CONTAINER(window), view); gtk_container_add(GTK_CONTAINER(window), view);
} }
#ifdef HAVE_GTK_INFO_BAR
static GtkWidget *perf_gtk__setup_info_bar(void)
{
GtkWidget *info_bar;
GtkWidget *label;
GtkWidget *content_area;
info_bar = gtk_info_bar_new();
gtk_widget_set_no_show_all(info_bar, TRUE);
label = gtk_label_new("");
gtk_widget_show(label);
content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));
gtk_container_add(GTK_CONTAINER(content_area), label);
gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,
GTK_RESPONSE_OK);
g_signal_connect(info_bar, "response",
G_CALLBACK(gtk_widget_hide), NULL);
pgctx->info_bar = info_bar;
pgctx->message_label = label;
return info_bar;
}
#endif
static GtkWidget *perf_gtk__setup_statusbar(void)
{
GtkWidget *stbar;
unsigned ctxid;
stbar = gtk_statusbar_new();
ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(stbar),
"perf report");
pgctx->statbar = stbar;
pgctx->statbar_ctx_id = ctxid;
return stbar;
}
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help __used, const char *help __used,
void (*timer) (void *arg)__used, void (*timer) (void *arg)__used,
void *arg __used, int delay_secs __used) void *arg __used, int delay_secs __used)
{ {
struct perf_evsel *pos; struct perf_evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook; GtkWidget *notebook;
GtkWidget *info_bar;
GtkWidget *statbar;
GtkWidget *window; GtkWidget *window;
signal(SIGSEGV, perf_gtk__signal); signal(SIGSEGV, perf_gtk__signal);
@ -143,11 +189,17 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
pgctx = perf_gtk__activate_context(window);
if (!pgctx)
return -1;
vbox = gtk_vbox_new(FALSE, 0);
notebook = gtk_notebook_new(); notebook = gtk_notebook_new();
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
struct hists *hists = &pos->hists; struct hists *hists = &pos->hists;
const char *evname = event_name(pos); const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window; GtkWidget *scrolled_window;
GtkWidget *tab_label; GtkWidget *tab_label;
@ -164,7 +216,16 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
} }
gtk_container_add(GTK_CONTAINER(window), notebook); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
info_bar = perf_gtk__setup_info_bar();
if (info_bar)
gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
statbar = perf_gtk__setup_statusbar();
gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window); gtk_widget_show_all(window);
@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_main(); gtk_main();
perf_gtk__deactivate_context(&pgctx);
return 0; return 0;
} }

View File

@ -1,8 +1,39 @@
#ifndef _PERF_GTK_H_ #ifndef _PERF_GTK_H_
#define _PERF_GTK_H_ 1 #define _PERF_GTK_H_ 1
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#pragma GCC diagnostic error "-Wstrict-prototypes" #pragma GCC diagnostic error "-Wstrict-prototypes"
struct perf_gtk_context {
GtkWidget *main_window;
#ifdef HAVE_GTK_INFO_BAR
GtkWidget *info_bar;
GtkWidget *message_label;
#endif
GtkWidget *statbar;
guint statbar_ctx_id;
};
extern struct perf_gtk_context *pgctx;
static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx)
{
return ctx && ctx->main_window;
}
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
#ifndef HAVE_GTK_INFO_BAR
static inline GtkWidget *perf_gtk__setup_info_bar(void)
{
return NULL;
}
#endif
#endif /* _PERF_GTK_H_ */ #endif /* _PERF_GTK_H_ */

View File

@ -1,12 +1,17 @@
#include "gtk.h" #include "gtk.h"
#include "../../util/cache.h" #include "../../util/cache.h"
#include "../../util/debug.h"
extern struct perf_error_ops perf_gtk_eops;
int perf_gtk__init(void) int perf_gtk__init(void)
{ {
perf_error__register(&perf_gtk_eops);
return gtk_init_check(NULL, NULL) ? 0 : -1; return gtk_init_check(NULL, NULL) ? 0 : -1;
} }
void perf_gtk__exit(bool wait_for_ok __used) void perf_gtk__exit(bool wait_for_ok __used)
{ {
perf_error__unregister(&perf_gtk_eops);
gtk_main_quit(); gtk_main_quit();
} }

129
tools/perf/ui/gtk/util.c Normal file
View File

@ -0,0 +1,129 @@
#include "../util.h"
#include "../../util/debug.h"
#include "gtk.h"
#include <string.h>
struct perf_gtk_context *pgctx;
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window)
{
struct perf_gtk_context *ctx;
ctx = malloc(sizeof(*pgctx));
if (ctx)
ctx->main_window = window;
return ctx;
}
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)
{
if (!perf_gtk__is_active_context(*ctx))
return -1;
free(*ctx);
*ctx = NULL;
return 0;
}
static int perf_gtk__error(const char *format, va_list args)
{
char *msg;
GtkWidget *dialog;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"<b>Error</b>\n\n%s", msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
free(msg);
return 0;
}
#ifdef HAVE_GTK_INFO_BAR
static int perf_gtk__warning_info_bar(const char *format, va_list args)
{
char *msg;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg);
gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar),
GTK_MESSAGE_WARNING);
gtk_widget_show(pgctx->info_bar);
free(msg);
return 0;
}
#else
static int perf_gtk__warning_statusbar(const char *format, va_list args)
{
char *msg, *p;
if (!perf_gtk__is_active_context(pgctx) ||
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
return -1;
}
gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id);
/* Only first line can be displayed */
p = strchr(msg, '\n');
if (p)
*p = '\0';
gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
pgctx->statbar_ctx_id, msg);
free(msg);
return 0;
}
#endif
struct perf_error_ops perf_gtk_eops = {
.error = perf_gtk__error,
#ifdef HAVE_GTK_INFO_BAR
.warning = perf_gtk__warning_info_bar,
#else
.warning = perf_gtk__warning_statusbar,
#endif
};
/*
* FIXME: Functions below should be implemented properly.
* For now, just add stubs for NO_NEWT=1 build.
*/
#ifdef NO_NEWT_SUPPORT
int ui_helpline__show_help(const char *format __used, va_list ap __used)
{
return 0;
}
void ui_progress__update(u64 curr __used, u64 total __used,
const char *title __used)
{
}
#endif

View File

@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
static volatile int ui__need_resize; static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops;
void ui__refresh_dimensions(bool force) void ui__refresh_dimensions(bool force)
{ {
if (force || ui__need_resize) { if (force || ui__need_resize) {
@ -122,6 +124,8 @@ int ui__init(void)
signal(SIGINT, ui__signal); signal(SIGINT, ui__signal);
signal(SIGQUIT, ui__signal); signal(SIGQUIT, ui__signal);
signal(SIGTERM, ui__signal); signal(SIGTERM, ui__signal);
perf_error__register(&perf_tui_eops);
out: out:
return err; return err;
} }
@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)
SLsmg_refresh(); SLsmg_refresh();
SLsmg_reset_smg(); SLsmg_reset_smg();
SLang_reset_tty(); SLang_reset_tty();
perf_error__unregister(&perf_tui_eops);
} }

243
tools/perf/ui/tui/util.c Normal file
View File

@ -0,0 +1,243 @@
#include "../../util/util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../browser.h"
#include "../keysyms.h"
#include "../helpline.h"
#include "../ui.h"
#include "../util.h"
#include "../libslang.h"
static void ui_browser__argv_write(struct ui_browser *browser,
void *entry, int row)
{
char **arg = entry;
bool current_entry = ui_browser__is_current_entry(browser, row);
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
slsmg_write_nstring(*arg, browser->width);
}
static int popup_menu__run(struct ui_browser *menu)
{
int key;
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
while (1) {
key = ui_browser__run(menu, 0);
switch (key) {
case K_RIGHT:
case K_ENTER:
key = menu->index;
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
key = -1;
break;
default:
continue;
}
break;
}
ui_browser__hide(menu);
return key;
}
int ui__popup_menu(int argc, char * const argv[])
{
struct ui_browser menu = {
.entries = (void *)argv,
.refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
return popup_menu__run(&menu);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_secs)
{
int x, y, len, key;
int max_len = 60, nr_lines = 0;
static char buf[50];
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 8;
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
y += nr_lines;
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
SLsmg_write_nstring((char *)" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
if (key == K_BKSPC) {
if (len == 0)
goto next_key;
SLsmg_gotorc(y, x + --len);
SLsmg_write_char(' ');
} else {
buf[len] = key;
SLsmg_gotorc(y, x + len++);
SLsmg_write_char(key);
}
SLsmg_refresh();
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
ui_helpline__push("maximum size of symbol name reached!");
key = K_ENTER;
break;
}
next_key:
key = ui__getch(delay_secs);
}
buf[len] = '\0';
strncpy(input, buf, len+1);
return key;
}
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
int x, y;
int max_len = 0, nr_lines = 0;
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
int len;
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 4;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
SLsmg_gotorc(y + nr_lines - 2, x);
SLsmg_write_nstring((char *)" ", max_len);
SLsmg_gotorc(y + nr_lines - 1, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
return ui__getch(delay_secs);
}
int ui__help_window(const char *text)
{
return ui__question_window("Help", text, "Press any key...", 0);
}
int ui__dialog_yesno(const char *msg)
{
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
static int __ui__warning(const char *title, const char *format, va_list args)
{
char *s;
if (vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock);
key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
free(s);
return key;
}
fprintf(stderr, "%s\n", title);
vfprintf(stderr, format, args);
return K_ESC;
}
static int perf_tui__error(const char *format, va_list args)
{
return __ui__warning("Error:", format, args);
}
static int perf_tui__warning(const char *format, va_list args)
{
return __ui__warning("Warning:", format, args);
}
struct perf_error_ops perf_tui_eops = {
.error = perf_tui__error,
.warning = perf_tui__warning,
};

View File

@ -1,250 +1,85 @@
#include "../util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ttydefaults.h>
#include "../cache.h"
#include "../debug.h"
#include "browser.h"
#include "keysyms.h"
#include "helpline.h"
#include "ui.h"
#include "util.h" #include "util.h"
#include "libslang.h" #include "../debug.h"
static void ui_browser__argv_write(struct ui_browser *browser,
void *entry, int row) /*
* Default error logging functions
*/
static int perf_stdio__error(const char *format, va_list args)
{ {
char **arg = entry; fprintf(stderr, "Error:\n");
bool current_entry = ui_browser__is_current_entry(browser, row);
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL);
slsmg_write_nstring(*arg, browser->width);
}
static int popup_menu__run(struct ui_browser *menu)
{
int key;
if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
return -1;
while (1) {
key = ui_browser__run(menu, 0);
switch (key) {
case K_RIGHT:
case K_ENTER:
key = menu->index;
break;
case K_LEFT:
case K_ESC:
case 'q':
case CTRL('c'):
key = -1;
break;
default:
continue;
}
break;
}
ui_browser__hide(menu);
return key;
}
int ui__popup_menu(int argc, char * const argv[])
{
struct ui_browser menu = {
.entries = (void *)argv,
.refresh = ui_browser__argv_refresh,
.seek = ui_browser__argv_seek,
.write = ui_browser__argv_write,
.nr_entries = argc,
};
return popup_menu__run(&menu);
}
int ui_browser__input_window(const char *title, const char *text, char *input,
const char *exit_msg, int delay_secs)
{
int x, y, len, key;
int max_len = 60, nr_lines = 0;
static char buf[50];
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 8;
y = SLtt_Screen_Rows / 2 - nr_lines / 2;
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 7;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
y += nr_lines;
len = 5;
while (len--) {
SLsmg_gotorc(y + len - 1, x);
SLsmg_write_nstring((char *)" ", max_len);
}
SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
SLsmg_gotorc(y + 3, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
x += 2;
len = 0;
key = ui__getch(delay_secs);
while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
if (key == K_BKSPC) {
if (len == 0)
goto next_key;
SLsmg_gotorc(y, x + --len);
SLsmg_write_char(' ');
} else {
buf[len] = key;
SLsmg_gotorc(y, x + len++);
SLsmg_write_char(key);
}
SLsmg_refresh();
/* XXX more graceful overflow handling needed */
if (len == sizeof(buf) - 1) {
ui_helpline__push("maximum size of symbol name reached!");
key = K_ENTER;
break;
}
next_key:
key = ui__getch(delay_secs);
}
buf[len] = '\0';
strncpy(input, buf, len+1);
return key;
}
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs)
{
int x, y;
int max_len = 0, nr_lines = 0;
const char *t;
t = text;
while (1) {
const char *sep = strchr(t, '\n');
int len;
if (sep == NULL)
sep = strchr(t, '\0');
len = sep - t;
if (max_len < len)
max_len = len;
++nr_lines;
if (*sep == '\0')
break;
t = sep + 1;
}
max_len += 2;
nr_lines += 4;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
SLsmg_set_color(0);
SLsmg_draw_box(y, x++, nr_lines, max_len);
if (title) {
SLsmg_gotorc(y, x + 1);
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
SLsmg_gotorc(y + nr_lines - 2, x);
SLsmg_write_nstring((char *)" ", max_len);
SLsmg_gotorc(y + nr_lines - 1, x);
SLsmg_write_nstring((char *)exit_msg, max_len);
SLsmg_refresh();
return ui__getch(delay_secs);
}
int ui__help_window(const char *text)
{
return ui__question_window("Help", text, "Press any key...", 0);
}
int ui__dialog_yesno(const char *msg)
{
return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
}
int __ui__warning(const char *title, const char *format, va_list args)
{
char *s;
if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
int key;
pthread_mutex_lock(&ui__lock);
key = ui__question_window(title, s, "Press any key...", 0);
pthread_mutex_unlock(&ui__lock);
free(s);
return key;
}
fprintf(stderr, "%s:\n", title);
vfprintf(stderr, format, args); vfprintf(stderr, format, args);
return K_ESC; return 0;
}
static int perf_stdio__warning(const char *format, va_list args)
{
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
return 0;
}
static struct perf_error_ops default_eops =
{
.error = perf_stdio__error,
.warning = perf_stdio__warning,
};
static struct perf_error_ops *perf_eops = &default_eops;
int ui__error(const char *format, ...)
{
int ret;
va_list args;
va_start(args, format);
ret = perf_eops->error(format, args);
va_end(args);
return ret;
} }
int ui__warning(const char *format, ...) int ui__warning(const char *format, ...)
{ {
int key; int ret;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
key = __ui__warning("Warning", format, args); ret = perf_eops->warning(format, args);
va_end(args); va_end(args);
return key;
return ret;
} }
int ui__error(const char *format, ...)
/**
* perf_error__register - Register error logging functions
* @eops: The pointer to error logging function struct
*
* Register UI-specific error logging functions. Before calling this,
* other logging functions should be unregistered, if any.
*/
int perf_error__register(struct perf_error_ops *eops)
{ {
int key; if (perf_eops != &default_eops)
va_list args; return -1;
va_start(args, format); perf_eops = eops;
key = __ui__warning("Error", format, args); return 0;
va_end(args); }
return key;
/**
* perf_error__unregister - Unregister error logging functions
* @eops: The pointer to error logging function struct
*
* Unregister already registered error logging functions.
*/
int perf_error__unregister(struct perf_error_ops *eops)
{
if (perf_eops != eops)
return -1;
perf_eops = &default_eops;
return 0;
} }

View File

@ -9,6 +9,13 @@ int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg); int ui__dialog_yesno(const char *msg);
int ui__question_window(const char *title, const char *text, int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs); const char *exit_msg, int delay_secs);
int __ui__warning(const char *title, const char *format, va_list args);
struct perf_error_ops {
int (*error)(const char *format, va_list args);
int (*warning)(const char *format, va_list args);
};
int perf_error__register(struct perf_error_ops *eops);
int perf_error__unregister(struct perf_error_ops *eops);
#endif /* _PERF_UI_UTIL_H_ */ #endif /* _PERF_UI_UTIL_H_ */

View File

@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)
return ret; return ret;
} }
#ifdef NO_NEWT_SUPPORT #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
int ui__warning(const char *format, ...) int ui__warning(const char *format, ...)
{ {
va_list args; va_list args;

View File

@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(union perf_event *event); void trace_event(union perf_event *event);
struct ui_progress; struct ui_progress;
struct perf_error_ops;
#ifdef NO_NEWT_SUPPORT #if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
static inline int ui_helpline__show_help(const char *format __used, va_list ap __used) static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
{ {
return 0; return 0;
@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
const char *title __used) {} const char *title __used) {}
#define ui__error(format, arg...) ui__warning(format, ##arg) #define ui__error(format, arg...) ui__warning(format, ##arg)
#else
static inline int
perf_error__register(struct perf_error_ops *eops __used)
{
return 0;
}
static inline int
perf_error__unregister(struct perf_error_ops *eops __used)
{
return 0;
}
#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
extern char ui_helpline__last_msg[]; extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap); int ui_helpline__show_help(const char *format, va_list ap);
#include "../ui/progress.h" #include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif #include "../ui/util.h"
#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
int ui__error_paranoid(void); int ui__error_paranoid(void);

View File

@ -224,8 +224,8 @@ int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
return err; return err;
} }
static struct perf_evsel * struct perf_evsel *
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;

View File

@ -73,6 +73,9 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \ #define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
struct perf_evsel *
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, u64 id); int cpu, int thread, u64 id);

View File

@ -15,7 +15,7 @@
#include "cpumap.h" #include "cpumap.h"
#include "thread_map.h" #include "thread_map.h"
#include "target.h" #include "target.h"
#include "../../include/linux/perf_event.h" #include "../../../include/linux/hw_breakpoint.h"
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
@ -78,7 +78,7 @@ static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
"ref-cycles", "ref-cycles",
}; };
const char *__perf_evsel__hw_name(u64 config) static const char *__perf_evsel__hw_name(u64 config)
{ {
if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config]) if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
return perf_evsel__hw_names[config]; return perf_evsel__hw_names[config];
@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config)
return "unknown-hardware"; return "unknown-hardware";
} }
static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t size)
{ {
int colon = 0; int colon = 0, r = 0;
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
bool exclude_guest_default = false; bool exclude_guest_default = false;
#define MOD_PRINT(context, mod) do { \ #define MOD_PRINT(context, mod) do { \
if (!attr->exclude_##context) { \ if (!attr->exclude_##context) { \
if (!colon) colon = r++; \ if (!colon) colon = ++r; \
r += scnprintf(bf + r, size - r, "%c", mod); \ r += scnprintf(bf + r, size - r, "%c", mod); \
} } while(0) } } while(0)
@ -108,7 +107,7 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
if (attr->precise_ip) { if (attr->precise_ip) {
if (!colon) if (!colon)
colon = r++; colon = ++r;
r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp"); r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
exclude_guest_default = true; exclude_guest_default = true;
} }
@ -119,39 +118,211 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
} }
#undef MOD_PRINT #undef MOD_PRINT
if (colon) if (colon)
bf[colon] = ':'; bf[colon - 1] = ':';
return r; return r;
} }
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size) static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
{ {
int ret; int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
"cpu-clock",
"task-clock",
"page-faults",
"context-switches",
"CPU-migrations",
"minor-faults",
"major-faults",
"alignment-faults",
"emulation-faults",
};
static const char *__perf_evsel__sw_name(u64 config)
{
if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config])
return perf_evsel__sw_names[config];
return "unknown-software";
}
static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
{
int r;
r = scnprintf(bf, size, "mem:0x%" PRIx64 ":", addr);
if (type & HW_BREAKPOINT_R)
r += scnprintf(bf + r, size - r, "r");
if (type & HW_BREAKPOINT_W)
r += scnprintf(bf + r, size - r, "w");
if (type & HW_BREAKPOINT_X)
r += scnprintf(bf + r, size - r, "x");
return r;
}
static int perf_evsel__bp_name(struct perf_evsel *evsel, char *bf, size_t size)
{
struct perf_event_attr *attr = &evsel->attr;
int r = __perf_evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type);
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2", },
{ "dTLB", "d-tlb", "Data-TLB", },
{ "iTLB", "i-tlb", "Instruction-TLB", },
{ "branch", "branches", "bpu", "btb", "bpc", },
{ "node", },
};
const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "load", "loads", "read", },
{ "store", "stores", "write", },
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
};
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[PERF_EVSEL__MAX_ALIASES] = {
{ "refs", "Reference", "ops", "access", },
{ "misses", "miss", },
};
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_READ (1 << C(OP_READ))
#define CACHE_WRITE (1 << C(OP_WRITE))
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
#define COP(x) (1 << x)
/*
* cache operartion stat
* L1I : Read and prefetch only
* ITLB and BPU : Read-only
*/
static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(ITLB)] = (CACHE_READ),
[C(BPU)] = (CACHE_READ),
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
};
bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
{
if (perf_evsel__hw_cache_stat[type] & COP(op))
return true; /* valid */
else
return false; /* invalid */
}
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size)
{
if (result) {
return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
perf_evsel__hw_cache_op[op][0],
perf_evsel__hw_cache_result[result][0]);
}
return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0],
perf_evsel__hw_cache_op[op][1]);
}
static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
{
u8 op, result, type = (config >> 0) & 0xff;
const char *err = "unknown-ext-hardware-cache-type";
if (type > PERF_COUNT_HW_CACHE_MAX)
goto out_err;
op = (config >> 8) & 0xff;
err = "unknown-ext-hardware-cache-op";
if (op > PERF_COUNT_HW_CACHE_OP_MAX)
goto out_err;
result = (config >> 16) & 0xff;
err = "unknown-ext-hardware-cache-result";
if (result > PERF_COUNT_HW_CACHE_RESULT_MAX)
goto out_err;
err = "invalid-cache";
if (!perf_evsel__is_cache_op_valid(type, op))
goto out_err;
return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
out_err:
return scnprintf(bf, size, "%s", err);
}
static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
static int perf_evsel__raw_name(struct perf_evsel *evsel, char *bf, size_t size)
{
int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
const char *perf_evsel__name(struct perf_evsel *evsel)
{
char bf[128];
if (evsel->name)
return evsel->name;
switch (evsel->attr.type) { switch (evsel->attr.type) {
case PERF_TYPE_RAW: case PERF_TYPE_RAW:
ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config); perf_evsel__raw_name(evsel, bf, sizeof(bf));
break; break;
case PERF_TYPE_HARDWARE: case PERF_TYPE_HARDWARE:
ret = perf_evsel__hw_name(evsel, bf, size); perf_evsel__hw_name(evsel, bf, sizeof(bf));
break; break;
case PERF_TYPE_HW_CACHE:
perf_evsel__hw_cache_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_SOFTWARE:
perf_evsel__sw_name(evsel, bf, sizeof(bf));
break;
case PERF_TYPE_TRACEPOINT:
scnprintf(bf, sizeof(bf), "%s", "unknown tracepoint");
break;
case PERF_TYPE_BREAKPOINT:
perf_evsel__bp_name(evsel, bf, sizeof(bf));
break;
default: default:
/* scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
* FIXME break;
*
* This is the minimal perf_evsel__name so that we can
* reconstruct event names taking into account event modifiers.
*
* The old event_name uses it now for raw anr hw events, so that
* we don't drag all the parsing stuff into the python binding.
*
* On the next devel cycle the rest of the event naming will be
* brought here.
*/
return 0;
} }
return ret; evsel->name = strdup(bf);
return evsel->name ?: "unknown";
} }
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,

View File

@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel,
struct perf_record_opts *opts, struct perf_record_opts *opts,
struct perf_evsel *first); struct perf_evsel *first);
const char* __perf_evsel__hw_name(u64 config); bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
#define PERF_EVSEL__MAX_ALIASES 8
extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
[PERF_EVSEL__MAX_ALIASES];
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_EVSEL__MAX_ALIASES];
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[PERF_EVSEL__MAX_ALIASES];
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size);
const char *perf_evsel__name(struct perf_evsel *evsel);
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);

View File

@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
/* /*
* write event string as passed on cmdline * write event string as passed on cmdline
*/ */
ret = do_write_string(fd, event_name(attr)); ret = do_write_string(fd, perf_evsel__name(attr));
if (ret < 0) if (ret < 0)
return ret; return ret;
/* /*
@ -1474,15 +1474,15 @@ static int perf_header__read_build_ids(struct perf_header *header,
static int process_tracing_data(struct perf_file_section *section __unused, static int process_tracing_data(struct perf_file_section *section __unused,
struct perf_header *ph __unused, struct perf_header *ph __unused,
int feat __unused, int fd) int feat __unused, int fd, void *data)
{ {
trace_report(fd, false); trace_report(fd, data, false);
return 0; return 0;
} }
static int process_build_id(struct perf_file_section *section, static int process_build_id(struct perf_file_section *section,
struct perf_header *ph, struct perf_header *ph,
int feat __unused, int fd) int feat __unused, int fd, void *data __used)
{ {
if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
pr_debug("Failed to read buildids, continuing...\n"); pr_debug("Failed to read buildids, continuing...\n");
@ -1493,7 +1493,7 @@ struct feature_ops {
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
void (*print)(struct perf_header *h, int fd, FILE *fp); void (*print)(struct perf_header *h, int fd, FILE *fp);
int (*process)(struct perf_file_section *section, int (*process)(struct perf_file_section *section,
struct perf_header *h, int feat, int fd); struct perf_header *h, int feat, int fd, void *data);
const char *name; const char *name;
bool full_only; bool full_only;
}; };
@ -1988,7 +1988,7 @@ int perf_file_header__read(struct perf_file_header *header,
static int perf_file_section__process(struct perf_file_section *section, static int perf_file_section__process(struct perf_file_section *section,
struct perf_header *ph, struct perf_header *ph,
int feat, int fd, void *data __used) int feat, int fd, void *data)
{ {
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
pr_debug("Failed to lseek to %" PRIu64 " offset for feature " pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
@ -2004,7 +2004,7 @@ static int perf_file_section__process(struct perf_file_section *section,
if (!feat_ops[feat].process) if (!feat_ops[feat].process)
return 0; return 0;
return feat_ops[feat].process(section, ph, feat, fd); return feat_ops[feat].process(section, ph, feat, fd, data);
} }
static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
@ -2093,9 +2093,11 @@ static int read_attr(int fd, struct perf_header *ph,
return ret <= 0 ? -1 : 0; return ret <= 0 ? -1 : 0;
} }
static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel) static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel,
struct pevent *pevent)
{ {
struct event_format *event = trace_find_event(evsel->attr.config); struct event_format *event = pevent_find_event(pevent,
evsel->attr.config);
char bf[128]; char bf[128];
if (event == NULL) if (event == NULL)
@ -2109,13 +2111,14 @@ static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel)
return 0; return 0;
} }
static int perf_evlist__set_tracepoint_names(struct perf_evlist *evlist) static int perf_evlist__set_tracepoint_names(struct perf_evlist *evlist,
struct pevent *pevent)
{ {
struct perf_evsel *pos; struct perf_evsel *pos;
list_for_each_entry(pos, &evlist->entries, node) { list_for_each_entry(pos, &evlist->entries, node) {
if (pos->attr.type == PERF_TYPE_TRACEPOINT && if (pos->attr.type == PERF_TYPE_TRACEPOINT &&
perf_evsel__set_tracepoint_name(pos)) perf_evsel__set_tracepoint_name(pos, pevent))
return -1; return -1;
} }
@ -2198,12 +2201,12 @@ int perf_session__read_header(struct perf_session *session, int fd)
event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
} }
perf_header__process_sections(header, fd, NULL, perf_header__process_sections(header, fd, &session->pevent,
perf_file_section__process); perf_file_section__process);
lseek(fd, header->data_offset, SEEK_SET); lseek(fd, header->data_offset, SEEK_SET);
if (perf_evlist__set_tracepoint_names(session->evlist)) if (perf_evlist__set_tracepoint_names(session->evlist, session->pevent))
goto out_delete_evlist; goto out_delete_evlist;
header->frozen = 1; header->frozen = 1;
@ -2419,8 +2422,8 @@ int perf_event__process_tracing_data(union perf_event *event,
lseek(session->fd, offset + sizeof(struct tracing_data_event), lseek(session->fd, offset + sizeof(struct tracing_data_event),
SEEK_SET); SEEK_SET);
size_read = trace_report(session->fd, session->repipe); size_read = trace_report(session->fd, &session->pevent,
session->repipe);
padding = ALIGN(size_read, sizeof(u64)) - size_read; padding = ALIGN(size_read, sizeof(u64)) - size_read;
if (read(session->fd, buf, padding) < 0) if (read(session->fd, buf, padding) < 0)

View File

@ -47,6 +47,7 @@ enum hist_column {
HISTC_SYMBOL_TO, HISTC_SYMBOL_TO,
HISTC_DSO_FROM, HISTC_DSO_FROM,
HISTC_DSO_TO, HISTC_DSO_TO,
HISTC_SRCLINE,
HISTC_NR_COLS, /* Last entry */ HISTC_NR_COLS, /* Last entry */
}; };

View File

@ -108,4 +108,14 @@ int eprintf(int level,
#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
/*
* This looks more complex than it should be. But we need to
* get the type for the ~ right in round_down (it needs to be
* as wide as the result!), and we want to evaluate the macro
* arguments just once each.
*/
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
#define round_down(x, y) ((x) & ~__round_mask(x, y))
#endif #endif

View File

@ -157,7 +157,7 @@ void machine__exit(struct machine *self);
void machine__delete(struct machine *self); void machine__delete(struct machine *self);
int machine__resolve_callchain(struct machine *machine, int machine__resolve_callchain(struct machine *machine,
struct perf_evsel *evsel, struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent); struct symbol **parent);
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,

View File

@ -181,6 +181,22 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
return 0; return 0;
} }
static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = list_entry(evlist->entries.next,
struct perf_evsel, node);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type",
PERF_TYPE_BREAKPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
TEST_ASSERT_VAL("wrong bp_type",
(HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len",
HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
return 0;
}
static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
{ {
struct perf_evsel *evsel = list_entry(evlist->entries.next, struct perf_evsel *evsel = list_entry(evlist->entries.next,
@ -309,6 +325,8 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u"));
return test__checkevent_breakpoint(evlist); return test__checkevent_breakpoint(evlist);
} }
@ -322,6 +340,8 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:x:k"));
return test__checkevent_breakpoint_x(evlist); return test__checkevent_breakpoint_x(evlist);
} }
@ -335,6 +355,8 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp"));
return test__checkevent_breakpoint_r(evlist); return test__checkevent_breakpoint_r(evlist);
} }
@ -348,10 +370,27 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:w:up"));
return test__checkevent_breakpoint_w(evlist); return test__checkevent_breakpoint_w(evlist);
} }
static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = list_entry(evlist->entries.next,
struct perf_evsel, node);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp"));
return test__checkevent_breakpoint_rw(evlist);
}
static int test__checkevent_pmu(struct perf_evlist *evlist) static int test__checkevent_pmu(struct perf_evlist *evlist)
{ {
@ -413,19 +452,63 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
/* cpu/config=1,name=krava1/u */ /* cpu/config=1,name=krava/u */
evsel = list_entry(evlist->entries.next, struct perf_evsel, node); evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava")); TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
/* cpu/config=2/" */ /* cpu/config=2/u" */
evsel = list_entry(evsel->node.next, struct perf_evsel, node); evsel = list_entry(evsel->node.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2")); TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "raw 0x2:u"));
return 0;
}
static int test__checkterms_simple(struct list_head *terms)
{
struct parse_events__term *term;
/* config=10 */
term = list_entry(terms->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 == 10);
TEST_ASSERT_VAL("wrong config", !term->config);
/* config1 */
term = list_entry(term->list.next, struct parse_events__term, list);
TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1);
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", !term->config);
/* config2=3 */
term = list_entry(term->list.next, struct parse_events__term, list);
TEST_ASSERT_VAL("wrong type term",
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2);
TEST_ASSERT_VAL("wrong type val",
term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
TEST_ASSERT_VAL("wrong val", term->val.num == 3);
TEST_ASSERT_VAL("wrong config", !term->config);
/* umask=1*/
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, "umask"));
return 0; return 0;
} }
@ -541,10 +624,16 @@ static struct test__event_st test__events[] = {
.name = "instructions:H", .name = "instructions:H",
.check = test__checkevent_exclude_guest_modifier, .check = test__checkevent_exclude_guest_modifier,
}, },
[26] = {
.name = "mem:0:rw",
.check = test__checkevent_breakpoint_rw,
},
[27] = {
.name = "mem:0:rw:kp",
.check = test__checkevent_breakpoint_rw_modifier,
},
}; };
#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
static struct test__event_st test__events_pmu[] = { static struct test__event_st test__events_pmu[] = {
[0] = { [0] = {
.name = "cpu/config=10,config1,config2=3,period=1000/u", .name = "cpu/config=10,config1,config2=3,period=1000/u",
@ -556,10 +645,23 @@ static struct test__event_st test__events_pmu[] = {
}, },
}; };
#define TEST__EVENTS_PMU_CNT (sizeof(test__events_pmu) / \ struct test__term {
sizeof(struct test__event_st)) const char *str;
__u32 type;
int (*check)(struct list_head *terms);
};
static int test(struct test__event_st *e) static struct test__term test__terms[] = {
[0] = {
.str = "config=10,config1,config2=3,umask=1",
.check = test__checkterms_simple,
},
};
#define TEST__TERMS_CNT (sizeof(test__terms) / \
sizeof(struct test__term))
static int test_event(struct test__event_st *e)
{ {
struct perf_evlist *evlist; struct perf_evlist *evlist;
int ret; int ret;
@ -590,7 +692,48 @@ static int test_events(struct test__event_st *events, unsigned cnt)
struct test__event_st *e = &events[i]; struct test__event_st *e = &events[i];
pr_debug("running test %d '%s'\n", i, e->name); pr_debug("running test %d '%s'\n", i, e->name);
ret = test(e); ret = test_event(e);
if (ret)
break;
}
return ret;
}
static int test_term(struct test__term *t)
{
struct list_head *terms;
int ret;
terms = malloc(sizeof(*terms));
if (!terms)
return -ENOMEM;
INIT_LIST_HEAD(terms);
ret = parse_events_terms(terms, t->str);
if (ret) {
pr_debug("failed to parse terms '%s', err %d\n",
t->str , ret);
return ret;
}
ret = t->check(terms);
parse_events__free_terms(terms);
return ret;
}
static int test_terms(struct test__term *terms, unsigned cnt)
{
int ret = 0;
unsigned i;
for (i = 0; i < cnt; i++) {
struct test__term *t = &terms[i];
pr_debug("running test %d '%s'\n", i, t->str);
ret = test_term(t);
if (ret) if (ret)
break; break;
} }
@ -617,9 +760,17 @@ int parse_events__test(void)
{ {
int ret; int ret;
ret = test_events(test__events, TEST__EVENTS_CNT); #define TEST_EVENTS(tests) \
if (!ret && test_pmu()) do { \
ret = test_events(test__events_pmu, TEST__EVENTS_PMU_CNT); ret = test_events(tests, ARRAY_SIZE(tests)); \
if (ret) \
return ret; \
} while (0)
return ret; TEST_EVENTS(test__events);
if (test_pmu())
TEST_EVENTS(test__events_pmu);
return test_terms(test__terms, ARRAY_SIZE(test__terms));
} }

View File

@ -11,14 +11,14 @@
#include "cache.h" #include "cache.h"
#include "header.h" #include "header.h"
#include "debugfs.h" #include "debugfs.h"
#include "parse-events-bison.h"
#define YY_EXTRA_TYPE int
#include "parse-events-flex.h" #include "parse-events-flex.h"
#include "pmu.h" #include "pmu.h"
#define MAX_NAME_LEN 100 #define MAX_NAME_LEN 100
struct event_symbol { struct event_symbol {
u8 type;
u64 config;
const char *symbol; const char *symbol;
const char *alias; const char *alias;
}; };
@ -26,32 +26,88 @@ struct event_symbol {
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
extern int parse_events_debug; extern int parse_events_debug;
#endif #endif
int parse_events_parse(struct list_head *list, int *idx); int parse_events_parse(void *data, void *scanner);
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x [PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles",
.alias = "cycles",
},
[PERF_COUNT_HW_INSTRUCTIONS] = {
.symbol = "instructions",
.alias = "",
},
[PERF_COUNT_HW_CACHE_REFERENCES] = {
.symbol = "cache-references",
.alias = "",
},
[PERF_COUNT_HW_CACHE_MISSES] = {
.symbol = "cache-misses",
.alias = "",
},
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = {
.symbol = "branch-instructions",
.alias = "branches",
},
[PERF_COUNT_HW_BRANCH_MISSES] = {
.symbol = "branch-misses",
.alias = "",
},
[PERF_COUNT_HW_BUS_CYCLES] = {
.symbol = "bus-cycles",
.alias = "",
},
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = {
.symbol = "stalled-cycles-frontend",
.alias = "idle-cycles-frontend",
},
[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = {
.symbol = "stalled-cycles-backend",
.alias = "idle-cycles-backend",
},
[PERF_COUNT_HW_REF_CPU_CYCLES] = {
.symbol = "ref-cycles",
.alias = "",
},
};
static struct event_symbol event_symbols[] = { static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
{ CHW(CPU_CYCLES), "cpu-cycles", "cycles" }, [PERF_COUNT_SW_CPU_CLOCK] = {
{ CHW(STALLED_CYCLES_FRONTEND), "stalled-cycles-frontend", "idle-cycles-frontend" }, .symbol = "cpu-clock",
{ CHW(STALLED_CYCLES_BACKEND), "stalled-cycles-backend", "idle-cycles-backend" }, .alias = "",
{ CHW(INSTRUCTIONS), "instructions", "" }, },
{ CHW(CACHE_REFERENCES), "cache-references", "" }, [PERF_COUNT_SW_TASK_CLOCK] = {
{ CHW(CACHE_MISSES), "cache-misses", "" }, .symbol = "task-clock",
{ CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" }, .alias = "",
{ CHW(BRANCH_MISSES), "branch-misses", "" }, },
{ CHW(BUS_CYCLES), "bus-cycles", "" }, [PERF_COUNT_SW_PAGE_FAULTS] = {
{ CHW(REF_CPU_CYCLES), "ref-cycles", "" }, .symbol = "page-faults",
.alias = "faults",
{ CSW(CPU_CLOCK), "cpu-clock", "" }, },
{ CSW(TASK_CLOCK), "task-clock", "" }, [PERF_COUNT_SW_CONTEXT_SWITCHES] = {
{ CSW(PAGE_FAULTS), "page-faults", "faults" }, .symbol = "context-switches",
{ CSW(PAGE_FAULTS_MIN), "minor-faults", "" }, .alias = "cs",
{ CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, },
{ CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, [PERF_COUNT_SW_CPU_MIGRATIONS] = {
{ CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, .symbol = "cpu-migrations",
{ CSW(ALIGNMENT_FAULTS), "alignment-faults", "" }, .alias = "migrations",
{ CSW(EMULATION_FAULTS), "emulation-faults", "" }, },
[PERF_COUNT_SW_PAGE_FAULTS_MIN] = {
.symbol = "minor-faults",
.alias = "",
},
[PERF_COUNT_SW_PAGE_FAULTS_MAJ] = {
.symbol = "major-faults",
.alias = "",
},
[PERF_COUNT_SW_ALIGNMENT_FAULTS] = {
.symbol = "alignment-faults",
.alias = "",
},
[PERF_COUNT_SW_EMULATION_FAULTS] = {
.symbol = "emulation-faults",
.alias = "",
},
}; };
#define __PERF_EVENT_FIELD(config, name) \ #define __PERF_EVENT_FIELD(config, name) \
@ -62,63 +118,6 @@ static struct event_symbol event_symbols[] = {
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) #define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) #define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
static const char *sw_event_names[PERF_COUNT_SW_MAX] = {
"cpu-clock",
"task-clock",
"page-faults",
"context-switches",
"CPU-migrations",
"minor-faults",
"major-faults",
"alignment-faults",
"emulation-faults",
};
#define MAX_ALIASES 8
static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = {
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2", },
{ "dTLB", "d-tlb", "Data-TLB", },
{ "iTLB", "i-tlb", "Instruction-TLB", },
{ "branch", "branches", "bpu", "btb", "bpc", },
{ "node", },
};
static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = {
{ "load", "loads", "read", },
{ "store", "stores", "write", },
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
};
static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
[MAX_ALIASES] = {
{ "refs", "Reference", "ops", "access", },
{ "misses", "miss", },
};
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_READ (1 << C(OP_READ))
#define CACHE_WRITE (1 << C(OP_WRITE))
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
#define COP(x) (1 << x)
/*
* cache operartion stat
* L1I : Read and prefetch only
* ITLB and BPU : Read-only
*/
static unsigned long hw_cache_stat[C(MAX)] = {
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(ITLB)] = (CACHE_READ),
[C(BPU)] = (CACHE_READ),
[C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
};
#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
if (sys_dirent.d_type == DT_DIR && \ if (sys_dirent.d_type == DT_DIR && \
@ -218,48 +217,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
return NULL; return NULL;
} }
#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
static const char *tracepoint_id_to_name(u64 config)
{
static char buf[TP_PATH_LEN];
struct tracepoint_path *path;
path = tracepoint_id_to_path(config);
if (path) {
snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
free(path->name);
free(path->system);
free(path);
} else
snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
return buf;
}
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
{
if (hw_cache_stat[cache_type] & COP(cache_op))
return 1; /* valid */
else
return 0; /* invalid */
}
static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
{
static char name[50];
if (cache_result) {
sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][0],
hw_cache_result[cache_result][0]);
} else {
sprintf(name, "%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][1]);
}
return name;
}
const char *event_type(int type) const char *event_type(int type)
{ {
switch (type) { switch (type) {
@ -282,76 +239,6 @@ const char *event_type(int type)
return "unknown"; return "unknown";
} }
const char *event_name(struct perf_evsel *evsel)
{
u64 config = evsel->attr.config;
int type = evsel->attr.type;
if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) {
/*
* XXX minimal fix, see comment on perf_evsen__name, this static buffer
* will go away together with event_name in the next devel cycle.
*/
static char bf[128];
perf_evsel__name(evsel, bf, sizeof(bf));
return bf;
}
if (evsel->name)
return evsel->name;
return __event_name(type, config);
}
const char *__event_name(int type, u64 config)
{
static char buf[32];
if (type == PERF_TYPE_RAW) {
sprintf(buf, "raw 0x%" PRIx64, config);
return buf;
}
switch (type) {
case PERF_TYPE_HARDWARE:
return __perf_evsel__hw_name(config);
case PERF_TYPE_HW_CACHE: {
u8 cache_type, cache_op, cache_result;
cache_type = (config >> 0) & 0xff;
if (cache_type > PERF_COUNT_HW_CACHE_MAX)
return "unknown-ext-hardware-cache-type";
cache_op = (config >> 8) & 0xff;
if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX)
return "unknown-ext-hardware-cache-op";
cache_result = (config >> 16) & 0xff;
if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
return "unknown-ext-hardware-cache-result";
if (!is_cache_op_valid(cache_type, cache_op))
return "invalid-cache";
return event_cache_name(cache_type, cache_op, cache_result);
}
case PERF_TYPE_SOFTWARE:
if (config < PERF_COUNT_SW_MAX && sw_event_names[config])
return sw_event_names[config];
return "unknown-software";
case PERF_TYPE_TRACEPOINT:
return tracepoint_id_to_name(config);
default:
break;
}
return "unknown";
}
static int add_event(struct list_head **_list, int *idx, static int add_event(struct list_head **_list, int *idx,
struct perf_event_attr *attr, char *name) struct perf_event_attr *attr, char *name)
{ {
@ -373,19 +260,20 @@ static int add_event(struct list_head **_list, int *idx,
return -ENOMEM; return -ENOMEM;
} }
evsel->name = strdup(name); if (name)
evsel->name = strdup(name);
list_add_tail(&evsel->node, list); list_add_tail(&evsel->node, list);
*_list = list; *_list = list;
return 0; return 0;
} }
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
{ {
int i, j; int i, j;
int n, longest = -1; int n, longest = -1;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {
n = strlen(names[i][j]); n = strlen(names[i][j]);
if (n > longest && !strncasecmp(str, names[i][j], n)) if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n; longest = n;
@ -410,7 +298,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,
* No fallback - if we cannot get a clear cache type * No fallback - if we cannot get a clear cache type
* then bail out: * then bail out:
*/ */
cache_type = parse_aliases(type, hw_cache, cache_type = parse_aliases(type, perf_evsel__hw_cache,
PERF_COUNT_HW_CACHE_MAX); PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1) if (cache_type == -1)
return -EINVAL; return -EINVAL;
@ -423,18 +311,18 @@ int parse_events_add_cache(struct list_head **list, int *idx,
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
if (cache_op == -1) { if (cache_op == -1) {
cache_op = parse_aliases(str, hw_cache_op, cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
PERF_COUNT_HW_CACHE_OP_MAX); PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) { if (cache_op >= 0) {
if (!is_cache_op_valid(cache_type, cache_op)) if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))
return -EINVAL; return -EINVAL;
continue; continue;
} }
} }
if (cache_result == -1) { if (cache_result == -1) {
cache_result = parse_aliases(str, hw_cache_result, cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX); PERF_COUNT_HW_CACHE_RESULT_MAX);
if (cache_result >= 0) if (cache_result >= 0)
continue; continue;
} }
@ -549,21 +437,31 @@ parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
if (!type || !type[i]) if (!type || !type[i])
break; break;
#define CHECK_SET_TYPE(bit) \
do { \
if (attr->bp_type & bit) \
return -EINVAL; \
else \
attr->bp_type |= bit; \
} while (0)
switch (type[i]) { switch (type[i]) {
case 'r': case 'r':
attr->bp_type |= HW_BREAKPOINT_R; CHECK_SET_TYPE(HW_BREAKPOINT_R);
break; break;
case 'w': case 'w':
attr->bp_type |= HW_BREAKPOINT_W; CHECK_SET_TYPE(HW_BREAKPOINT_W);
break; break;
case 'x': case 'x':
attr->bp_type |= HW_BREAKPOINT_X; CHECK_SET_TYPE(HW_BREAKPOINT_X);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
} }
#undef CHECK_SET_TYPE
if (!attr->bp_type) /* Default */ if (!attr->bp_type) /* Default */
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
@ -574,7 +472,6 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
void *ptr, char *type) void *ptr, char *type)
{ {
struct perf_event_attr attr; struct perf_event_attr attr;
char name[MAX_NAME_LEN];
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
attr.bp_addr = (unsigned long) ptr; attr.bp_addr = (unsigned long) ptr;
@ -593,8 +490,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
attr.type = PERF_TYPE_BREAKPOINT; attr.type = PERF_TYPE_BREAKPOINT;
snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); return add_event(list, idx, &attr, NULL);
return add_event(list, idx, &attr, name);
} }
static int config_term(struct perf_event_attr *attr, static int config_term(struct perf_event_attr *attr,
@ -666,8 +562,7 @@ int parse_events_add_numeric(struct list_head **list, int *idx,
config_attr(&attr, head_config, 1)) config_attr(&attr, head_config, 1))
return -EINVAL; return -EINVAL;
return add_event(list, idx, &attr, return add_event(list, idx, &attr, NULL);
(char *) __event_name(type, config));
} }
static int parse_events__is_name_term(struct parse_events__term *term) static int parse_events__is_name_term(struct parse_events__term *term)
@ -675,8 +570,7 @@ static int parse_events__is_name_term(struct parse_events__term *term)
return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
} }
static char *pmu_event_name(struct perf_event_attr *attr, static char *pmu_event_name(struct list_head *head_terms)
struct list_head *head_terms)
{ {
struct parse_events__term *term; struct parse_events__term *term;
@ -684,7 +578,7 @@ static char *pmu_event_name(struct perf_event_attr *attr,
if (parse_events__is_name_term(term)) if (parse_events__is_name_term(term))
return term->val.str; return term->val.str;
return (char *) __event_name(PERF_TYPE_RAW, attr->config); return NULL;
} }
int parse_events_add_pmu(struct list_head **list, int *idx, int parse_events_add_pmu(struct list_head **list, int *idx,
@ -699,6 +593,9 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
if (perf_pmu__check_alias(pmu, head_config))
return -EINVAL;
/* /*
* Configure hardcoded terms first, no need to check * Configure hardcoded terms first, no need to check
* return value when called with fail == 0 ;) * return value when called with fail == 0 ;)
@ -709,7 +606,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
return -EINVAL; return -EINVAL;
return add_event(list, idx, &attr, return add_event(list, idx, &attr,
pmu_event_name(&attr, head_config)); pmu_event_name(head_config));
} }
void parse_events_update_lists(struct list_head *list_event, void parse_events_update_lists(struct list_head *list_event,
@ -787,27 +684,62 @@ int parse_events_modifier(struct list_head *list, char *str)
return 0; return 0;
} }
int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) static int parse_events__scanner(const char *str, void *data, int start_token)
{ {
LIST_HEAD(list);
LIST_HEAD(list_tmp);
YY_BUFFER_STATE buffer; YY_BUFFER_STATE buffer;
int ret, idx = evlist->nr_entries; void *scanner;
int ret;
buffer = parse_events__scan_string(str); ret = parse_events_lex_init_extra(start_token, &scanner);
if (ret)
return ret;
buffer = parse_events__scan_string(str, scanner);
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
parse_events_debug = 1; parse_events_debug = 1;
#endif #endif
ret = parse_events_parse(&list, &idx); ret = parse_events_parse(data, scanner);
parse_events__flush_buffer(buffer); parse_events__flush_buffer(buffer, scanner);
parse_events__delete_buffer(buffer); parse_events__delete_buffer(buffer, scanner);
parse_events_lex_destroy(); parse_events_lex_destroy(scanner);
return ret;
}
/*
* parse event config string, return a list of event terms.
*/
int parse_events_terms(struct list_head *terms, const char *str)
{
struct parse_events_data__terms data = {
.terms = NULL,
};
int ret;
ret = parse_events__scanner(str, &data, PE_START_TERMS);
if (!ret) { if (!ret) {
int entries = idx - evlist->nr_entries; list_splice(data.terms, terms);
perf_evlist__splice_list_tail(evlist, &list, entries); free(data.terms);
return 0;
}
parse_events__free_terms(data.terms);
return ret;
}
int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
{
struct parse_events_data__events data = {
.list = LIST_HEAD_INIT(data.list),
.idx = evlist->nr_entries,
};
int ret;
ret = parse_events__scanner(str, &data, PE_START_EVENTS);
if (!ret) {
int entries = data.idx - evlist->nr_entries;
perf_evlist__splice_list_tail(evlist, &data.list, entries);
return 0; return 0;
} }
@ -946,16 +878,13 @@ int is_valid_tracepoint(const char *event_string)
return 0; return 0;
} }
void print_events_type(u8 type) static void __print_events_type(u8 type, struct event_symbol *syms,
unsigned max)
{ {
struct event_symbol *syms = event_symbols;
unsigned int i;
char name[64]; char name[64];
unsigned i;
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { for (i = 0; i < max ; i++, syms++) {
if (type != syms->type)
continue;
if (strlen(syms->alias)) if (strlen(syms->alias))
snprintf(name, sizeof(name), "%s OR %s", snprintf(name, sizeof(name), "%s OR %s",
syms->symbol, syms->alias); syms->symbol, syms->alias);
@ -967,19 +896,28 @@ void print_events_type(u8 type)
} }
} }
void print_events_type(u8 type)
{
if (type == PERF_TYPE_SOFTWARE)
__print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX);
else
__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
}
int print_hwcache_events(const char *event_glob) int print_hwcache_events(const char *event_glob)
{ {
unsigned int type, op, i, printed = 0; unsigned int type, op, i, printed = 0;
char name[64];
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
/* skip invalid cache type */ /* skip invalid cache type */
if (!is_cache_op_valid(type, op)) if (!perf_evsel__is_cache_op_valid(type, op))
continue; continue;
for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
char *name = event_cache_name(type, op, i); __perf_evsel__hw_cache_type_op_res_name(type, op, i,
name, sizeof(name));
if (event_glob != NULL && !strglobmatch(name, event_glob)) if (event_glob != NULL && !strglobmatch(name, event_glob))
continue; continue;
@ -993,26 +931,13 @@ int print_hwcache_events(const char *event_glob)
return printed; return printed;
} }
/* static void print_symbol_events(const char *event_glob, unsigned type,
* Print the help text for the event symbols: struct event_symbol *syms, unsigned max)
*/
void print_events(const char *event_glob)
{ {
unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0; unsigned i, printed = 0;
struct event_symbol *syms = event_symbols;
char name[MAX_NAME_LEN]; char name[MAX_NAME_LEN];
printf("\n"); for (i = 0; i < max; i++, syms++) {
printf("List of pre-defined events (to be used in -e):\n");
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
type = syms->type;
if (type != prev_type && printed) {
printf("\n");
printed = 0;
ntypes_printed++;
}
if (event_glob != NULL && if (event_glob != NULL &&
!(strglobmatch(syms->symbol, event_glob) || !(strglobmatch(syms->symbol, event_glob) ||
@ -1023,17 +948,31 @@ void print_events(const char *event_glob)
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else else
strncpy(name, syms->symbol, MAX_NAME_LEN); strncpy(name, syms->symbol, MAX_NAME_LEN);
printf(" %-50s [%s]\n", name,
event_type_descriptors[type]);
prev_type = type; printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
++printed;
printed++;
} }
if (ntypes_printed) { if (printed)
printed = 0;
printf("\n"); printf("\n");
} }
/*
* Print the help text for the event symbols:
*/
void print_events(const char *event_glob)
{
printf("\n");
printf("List of pre-defined events (to be used in -e):\n");
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
event_symbols_hw, PERF_COUNT_HW_MAX);
print_symbol_events(event_glob, PERF_TYPE_SOFTWARE,
event_symbols_sw, PERF_COUNT_SW_MAX);
print_hwcache_events(event_glob); print_hwcache_events(event_glob);
if (event_glob != NULL) if (event_glob != NULL)
@ -1106,6 +1045,13 @@ int parse_events__term_str(struct parse_events__term **term,
config, str, 0); config, str, 0);
} }
int parse_events__term_clone(struct parse_events__term **new,
struct parse_events__term *term)
{
return new_term(new, term->type_val, term->type_term, term->config,
term->val.str, term->val.num);
}
void parse_events__free_terms(struct list_head *terms) void parse_events__free_terms(struct list_head *terms)
{ {
struct parse_events__term *term, *h; struct parse_events__term *term, *h;

View File

@ -26,13 +26,12 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern bool have_tracepoints(struct list_head *evlist); extern bool have_tracepoints(struct list_head *evlist);
const char *event_type(int type); const char *event_type(int type);
const char *event_name(struct perf_evsel *event);
extern const char *__event_name(int type, u64 config);
extern int parse_events_option(const struct option *opt, const char *str, extern int parse_events_option(const struct option *opt, const char *str,
int unset); int unset);
extern int parse_events(struct perf_evlist *evlist, const char *str, extern int parse_events(struct perf_evlist *evlist, const char *str,
int unset); int unset);
extern int parse_events_terms(struct list_head *terms, const char *str);
extern int parse_filter(const struct option *opt, const char *str, int unset); extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024) #define EVENTS_HELP_MAX (128*1024)
@ -63,11 +62,22 @@ struct parse_events__term {
struct list_head list; struct list_head list;
}; };
struct parse_events_data__events {
struct list_head list;
int idx;
};
struct parse_events_data__terms {
struct list_head *terms;
};
int parse_events__is_hardcoded_term(struct parse_events__term *term); int parse_events__is_hardcoded_term(struct parse_events__term *term);
int parse_events__term_num(struct parse_events__term **_term, int parse_events__term_num(struct parse_events__term **_term,
int type_term, char *config, long num); int type_term, char *config, long num);
int parse_events__term_str(struct parse_events__term **_term, int parse_events__term_str(struct parse_events__term **_term,
int type_term, char *config, char *str); int type_term, char *config, char *str);
int parse_events__term_clone(struct parse_events__term **new,
struct parse_events__term *term);
void parse_events__free_terms(struct list_head *terms); void parse_events__free_terms(struct list_head *terms);
int parse_events_modifier(struct list_head *list, char *str); int parse_events_modifier(struct list_head *list, char *str);
int parse_events_add_tracepoint(struct list_head **list, int *idx, int parse_events_add_tracepoint(struct list_head **list, int *idx,
@ -83,8 +93,7 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
char *pmu , struct list_head *head_config); char *pmu , struct list_head *head_config);
void parse_events_update_lists(struct list_head *list_event, void parse_events_update_lists(struct list_head *list_event,
struct list_head *list_all); struct list_head *list_all);
void parse_events_error(struct list_head *list_all, void parse_events_error(void *data, void *scanner, char const *msg);
int *idx, char const *msg);
int parse_events__test(void); int parse_events__test(void);
void print_events(const char *event_glob); void print_events(const char *event_glob);

View File

@ -1,4 +1,6 @@
%option reentrant
%option bison-bridge
%option prefix="parse_events_" %option prefix="parse_events_"
%option stack %option stack
@ -8,7 +10,10 @@
#include "parse-events-bison.h" #include "parse-events-bison.h"
#include "parse-events.h" #include "parse-events.h"
static int __value(char *str, int base, int token) char *parse_events_get_text(yyscan_t yyscanner);
YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
static int __value(YYSTYPE *yylval, char *str, int base, int token)
{ {
long num; long num;
@ -17,35 +22,48 @@ static int __value(char *str, int base, int token)
if (errno) if (errno)
return PE_ERROR; return PE_ERROR;
parse_events_lval.num = num; yylval->num = num;
return token; return token;
} }
static int value(int base) static int value(yyscan_t scanner, int base)
{ {
return __value(parse_events_text, base, PE_VALUE); YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
return __value(yylval, text, base, PE_VALUE);
} }
static int raw(void) static int raw(yyscan_t scanner)
{ {
return __value(parse_events_text + 1, 16, PE_RAW); YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
return __value(yylval, text + 1, 16, PE_RAW);
} }
static int str(int token) static int str(yyscan_t scanner, int token)
{ {
parse_events_lval.str = strdup(parse_events_text); YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
yylval->str = strdup(text);
return token; return token;
} }
static int sym(int type, int config) static int sym(yyscan_t scanner, int type, int config)
{ {
parse_events_lval.num = (type << 16) + config; YYSTYPE *yylval = parse_events_get_lval(scanner);
return PE_VALUE_SYM;
yylval->num = (type << 16) + config;
return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
} }
static int term(int type) static int term(yyscan_t scanner, int type)
{ {
parse_events_lval.num = type; YYSTYPE *yylval = parse_events_get_lval(scanner);
yylval->num = type;
return PE_TERM; return PE_TERM;
} }
@ -58,28 +76,41 @@ num_hex 0x[a-fA-F0-9]+
num_raw_hex [a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+
name [a-zA-Z_*?][a-zA-Z0-9_*?]* name [a-zA-Z_*?][a-zA-Z0-9_*?]*
modifier_event [ukhpGH]{1,8} modifier_event [ukhpGH]{1,8}
modifier_bp [rwx] modifier_bp [rwx]{1,3}
%% %%
cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } %{
stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } {
instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } int start_token;
cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } start_token = (int) parse_events_get_extra(yyscanner);
branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } if (start_token) {
branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } parse_events_set_extra(NULL, yyscanner);
bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } return start_token;
ref-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } }
cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } }
task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } %}
page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } instructions { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } cache-references { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } cache-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
branch-instructions|branches { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
branch-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
bus-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
ref-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); }
cpu-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
task-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
page-faults|faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
minor-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
major-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
context-switches|cs { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
L1-dcache|l1-d|l1d|L1-data | L1-dcache|l1-d|l1d|L1-data |
L1-icache|l1-i|l1i|L1-instruction | L1-icache|l1-i|l1i|L1-instruction |
@ -87,14 +118,14 @@ LLC|L2 |
dTLB|d-tlb|Data-TLB | dTLB|d-tlb|Data-TLB |
iTLB|i-tlb|Instruction-TLB | iTLB|i-tlb|Instruction-TLB |
branch|branches|bpu|btb|bpc | branch|branches|bpu|btb|bpc |
node { return str(PE_NAME_CACHE_TYPE); } node { return str(yyscanner, PE_NAME_CACHE_TYPE); }
load|loads|read | load|loads|read |
store|stores|write | store|stores|write |
prefetch|prefetches | prefetch|prefetches |
speculative-read|speculative-load | speculative-read|speculative-load |
refs|Reference|ops|access | refs|Reference|ops|access |
misses|miss { return str(PE_NAME_CACHE_OP_RESULT); } misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
/* /*
* These are event config hardcoded term names to be specified * These are event config hardcoded term names to be specified
@ -102,38 +133,39 @@ misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
* so we can put them here directly. In case the we have a conflict * so we can put them here directly. In case the we have a conflict
* in future, this needs to go into '//' condition block. * in future, this needs to go into '//' condition block.
*/ */
config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
name { return term(PARSE_EVENTS__TERM_TYPE_NAME); } name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
mem: { BEGIN(mem); return PE_PREFIX_MEM; } mem: { BEGIN(mem); return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(); } r{num_raw_hex} { return raw(yyscanner); }
{num_dec} { return value(10); } {num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(16); } {num_hex} { return value(yyscanner, 16); }
{modifier_event} { return str(PE_MODIFIER_EVENT); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return str(PE_NAME); } {name} { return str(yyscanner, PE_NAME); }
"/" { return '/'; } "/" { return '/'; }
- { return '-'; } - { return '-'; }
, { return ','; } , { return ','; }
: { return ':'; } : { return ':'; }
= { return '='; } = { return '='; }
\n { }
<mem>{ <mem>{
{modifier_bp} { return str(PE_MODIFIER_BP); } {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
: { return ':'; } : { return ':'; }
{num_dec} { return value(10); } {num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(16); } {num_hex} { return value(yyscanner, 16); }
/* /*
* We need to separate 'mem:' scanner part, in order to get specific * We need to separate 'mem:' scanner part, in order to get specific
* modifier bits parsed out. Otherwise we would need to handle PE_NAME * modifier bits parsed out. Otherwise we would need to handle PE_NAME
* and we'd need to parse it manually. During the escape from <mem> * and we'd need to parse it manually. During the escape from <mem>
* state we need to put the escaping char back, so we dont miss it. * state we need to put the escaping char back, so we dont miss it.
*/ */
. { unput(*parse_events_text); BEGIN(INITIAL); } . { unput(*yytext); BEGIN(INITIAL); }
/* /*
* We destroy the scanner after reaching EOF, * We destroy the scanner after reaching EOF,
* but anyway just to be sure get back to INIT state. * but anyway just to be sure get back to INIT state.
@ -143,7 +175,7 @@ r{num_raw_hex} { return raw(); }
%% %%
int parse_events_wrap(void) int parse_events_wrap(void *scanner __used)
{ {
return 1; return 1;
} }

View File

@ -1,7 +1,8 @@
%pure-parser
%name-prefix "parse_events_" %name-prefix "parse_events_"
%parse-param {struct list_head *list_all} %parse-param {void *_data}
%parse-param {int *idx} %parse-param {void *scanner}
%lex-param {void* scanner}
%{ %{
@ -12,8 +13,9 @@
#include "types.h" #include "types.h"
#include "util.h" #include "util.h"
#include "parse-events.h" #include "parse-events.h"
#include "parse-events-bison.h"
extern int parse_events_lex (void); extern int parse_events_lex (YYSTYPE* lvalp, void* scanner);
#define ABORT_ON(val) \ #define ABORT_ON(val) \
do { \ do { \
@ -23,14 +25,16 @@ do { \
%} %}
%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM %token PE_START_EVENTS PE_START_TERMS
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_NAME %token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP %token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW %token PE_PREFIX_MEM PE_PREFIX_RAW
%token PE_ERROR %token PE_ERROR
%type <num> PE_VALUE %type <num> PE_VALUE
%type <num> PE_VALUE_SYM %type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
%type <num> PE_RAW %type <num> PE_RAW
%type <num> PE_TERM %type <num> PE_TERM
%type <str> PE_NAME %type <str> PE_NAME
@ -38,6 +42,7 @@ do { \
%type <str> PE_NAME_CACHE_OP_RESULT %type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT %type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP %type <str> PE_MODIFIER_BP
%type <num> value_sym
%type <head> event_config %type <head> event_config
%type <term> event_term %type <term> event_term
%type <head> event_pmu %type <head> event_pmu
@ -58,24 +63,33 @@ do { \
} }
%% %%
start:
PE_START_EVENTS events
|
PE_START_TERMS terms
events: events:
events ',' event | event events ',' event | event
event: event:
event_def PE_MODIFIER_EVENT event_def PE_MODIFIER_EVENT
{ {
struct parse_events_data__events *data = _data;
/* /*
* Apply modifier on all events added by single event definition * Apply modifier on all events added by single event definition
* (there could be more events added for multiple tracepoint * (there could be more events added for multiple tracepoint
* definitions via '*?'. * definitions via '*?'.
*/ */
ABORT_ON(parse_events_modifier($1, $2)); ABORT_ON(parse_events_modifier($1, $2));
parse_events_update_lists($1, list_all); parse_events_update_lists($1, &data->list);
} }
| |
event_def event_def
{ {
parse_events_update_lists($1, list_all); struct parse_events_data__events *data = _data;
parse_events_update_lists($1, &data->list);
} }
event_def: event_pmu | event_def: event_pmu |
@ -89,104 +103,131 @@ event_def: event_pmu |
event_pmu: event_pmu:
PE_NAME '/' event_config '/' PE_NAME '/' event_config '/'
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_pmu(&list, idx, $1, $3)); ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3));
parse_events__free_terms($3); parse_events__free_terms($3);
$$ = list; $$ = list;
} }
value_sym:
PE_VALUE_SYM_HW
|
PE_VALUE_SYM_SW
event_legacy_symbol: event_legacy_symbol:
PE_VALUE_SYM '/' event_config '/' value_sym '/' event_config '/'
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
ABORT_ON(parse_events_add_numeric(&list, idx, type, config, $3)); ABORT_ON(parse_events_add_numeric(&list, &data->idx,
type, config, $3));
parse_events__free_terms($3); parse_events__free_terms($3);
$$ = list; $$ = list;
} }
| |
PE_VALUE_SYM sep_slash_dc value_sym sep_slash_dc
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
int type = $1 >> 16; int type = $1 >> 16;
int config = $1 & 255; int config = $1 & 255;
ABORT_ON(parse_events_add_numeric(&list, idx, type, config, NULL)); ABORT_ON(parse_events_add_numeric(&list, &data->idx,
type, config, NULL));
$$ = list; $$ = list;
} }
event_legacy_cache: event_legacy_cache:
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, $5)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5));
$$ = list; $$ = list;
} }
| |
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, NULL)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL));
$$ = list; $$ = list;
} }
| |
PE_NAME_CACHE_TYPE PE_NAME_CACHE_TYPE
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_cache(&list, idx, $1, NULL, NULL)); ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL));
$$ = list; $$ = list;
} }
event_legacy_mem: event_legacy_mem:
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, $4)); ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
(void *) $2, $4));
$$ = list; $$ = list;
} }
| |
PE_PREFIX_MEM PE_VALUE sep_dc PE_PREFIX_MEM PE_VALUE sep_dc
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, NULL)); ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
(void *) $2, NULL));
$$ = list; $$ = list;
} }
event_legacy_tracepoint: event_legacy_tracepoint:
PE_NAME ':' PE_NAME PE_NAME ':' PE_NAME
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_tracepoint(&list, idx, $1, $3)); ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3));
$$ = list; $$ = list;
} }
event_legacy_numeric: event_legacy_numeric:
PE_VALUE ':' PE_VALUE PE_VALUE ':' PE_VALUE
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_numeric(&list, idx, $1, $3, NULL)); ABORT_ON(parse_events_add_numeric(&list, &data->idx, $1, $3, NULL));
$$ = list; $$ = list;
} }
event_legacy_raw: event_legacy_raw:
PE_RAW PE_RAW
{ {
struct parse_events_data__events *data = _data;
struct list_head *list = NULL; struct list_head *list = NULL;
ABORT_ON(parse_events_add_numeric(&list, idx, PERF_TYPE_RAW, $1, NULL)); ABORT_ON(parse_events_add_numeric(&list, &data->idx,
PERF_TYPE_RAW, $1, NULL));
$$ = list; $$ = list;
} }
terms: event_config
{
struct parse_events_data__terms *data = _data;
data->terms = $1;
}
event_config: event_config:
event_config ',' event_term event_config ',' event_term
{ {
@ -267,8 +308,7 @@ sep_slash_dc: '/' | ':' |
%% %%
void parse_events_error(struct list_head *list_all __used, void parse_events_error(void *data __used, void *scanner __used,
int *idx __used,
char const *msg __used) char const *msg __used)
{ {
} }

View File

@ -72,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format)
"%s/bus/event_source/devices/%s/format", sysfs, name); "%s/bus/event_source/devices/%s/format", sysfs, name);
if (stat(path, &st) < 0) if (stat(path, &st) < 0)
return -1; return 0; /* no error if format does not exist */
if (pmu_format_parse(path, format)) if (pmu_format_parse(path, format))
return -1; return -1;
@ -80,6 +80,114 @@ static int pmu_format(char *name, struct list_head *format)
return 0; return 0;
} }
static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
{
struct perf_pmu__alias *alias;
char buf[256];
int ret;
ret = fread(buf, 1, sizeof(buf), file);
if (ret == 0)
return -EINVAL;
buf[ret] = 0;
alias = malloc(sizeof(*alias));
if (!alias)
return -ENOMEM;
INIT_LIST_HEAD(&alias->terms);
ret = parse_events_terms(&alias->terms, buf);
if (ret) {
free(alias);
return ret;
}
alias->name = strdup(name);
list_add_tail(&alias->list, list);
return 0;
}
/*
* Process all the sysfs attributes located under the directory
* specified in 'dir' parameter.
*/
static int pmu_aliases_parse(char *dir, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
int ret = 0;
event_dir = opendir(dir);
if (!event_dir)
return -EINVAL;
while (!ret && (evt_ent = readdir(event_dir))) {
char path[PATH_MAX];
char *name = evt_ent->d_name;
FILE *file;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
snprintf(path, PATH_MAX, "%s/%s", dir, name);
ret = -EINVAL;
file = fopen(path, "r");
if (!file)
break;
ret = perf_pmu__new_alias(head, name, file);
fclose(file);
}
closedir(event_dir);
return ret;
}
/*
* Reading the pmu event aliases definition, which should be located at:
* /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
*/
static int pmu_aliases(char *name, struct list_head *head)
{
struct stat st;
char path[PATH_MAX];
const char *sysfs;
sysfs = sysfs_find_mountpoint();
if (!sysfs)
return -1;
snprintf(path, PATH_MAX,
"%s/bus/event_source/devices/%s/events", sysfs, name);
if (stat(path, &st) < 0)
return -1;
if (pmu_aliases_parse(path, head))
return -1;
return 0;
}
static int pmu_alias_terms(struct perf_pmu__alias *alias,
struct list_head *terms)
{
struct parse_events__term *term, *clone;
LIST_HEAD(list);
int ret;
list_for_each_entry(term, &alias->terms, list) {
ret = parse_events__term_clone(&clone, term);
if (ret) {
parse_events__free_terms(&list);
return ret;
}
list_add_tail(&clone->list, &list);
}
list_splice(&list, terms);
return 0;
}
/* /*
* Reading/parsing the default pmu type value, which should be * Reading/parsing the default pmu type value, which should be
* located at: * located at:
@ -118,6 +226,7 @@ static struct perf_pmu *pmu_lookup(char *name)
{ {
struct perf_pmu *pmu; struct perf_pmu *pmu;
LIST_HEAD(format); LIST_HEAD(format);
LIST_HEAD(aliases);
__u32 type; __u32 type;
/* /*
@ -135,10 +244,15 @@ static struct perf_pmu *pmu_lookup(char *name)
if (!pmu) if (!pmu)
return NULL; return NULL;
pmu_aliases(name, &aliases);
INIT_LIST_HEAD(&pmu->format); INIT_LIST_HEAD(&pmu->format);
INIT_LIST_HEAD(&pmu->aliases);
list_splice(&format, &pmu->format); list_splice(&format, &pmu->format);
list_splice(&aliases, &pmu->aliases);
pmu->name = strdup(name); pmu->name = strdup(name);
pmu->type = type; pmu->type = type;
list_add_tail(&pmu->list, &pmus);
return pmu; return pmu;
} }
@ -279,6 +393,59 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
return pmu_config(&pmu->format, attr, head_terms); return pmu_config(&pmu->format, attr, head_terms);
} }
static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
struct parse_events__term *term)
{
struct perf_pmu__alias *alias;
char *name;
if (parse_events__is_hardcoded_term(term))
return NULL;
if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
if (term->val.num != 1)
return NULL;
if (pmu_find_format(&pmu->format, term->config))
return NULL;
name = term->config;
} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcasecmp(term->config, "event"))
return NULL;
name = term->val.str;
} else {
return NULL;
}
list_for_each_entry(alias, &pmu->aliases, list) {
if (!strcasecmp(alias->name, name))
return alias;
}
return NULL;
}
/*
* Find alias in the terms list and replace it with the terms
* defined for the alias
*/
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
{
struct parse_events__term *term, *h;
struct perf_pmu__alias *alias;
int ret;
list_for_each_entry_safe(term, h, head_terms, list) {
alias = pmu_find_alias(pmu, term);
if (!alias)
continue;
ret = pmu_alias_terms(alias, &term->list);
if (ret)
return ret;
list_del(&term->list);
free(term);
}
return 0;
}
int perf_pmu__new_format(struct list_head *list, char *name, int perf_pmu__new_format(struct list_head *list, char *name,
int config, unsigned long *bits) int config, unsigned long *bits)
{ {

View File

@ -19,17 +19,26 @@ struct perf_pmu__format {
struct list_head list; struct list_head list;
}; };
struct perf_pmu__alias {
char *name;
struct list_head terms;
struct list_head list;
};
struct perf_pmu { struct perf_pmu {
char *name; char *name;
__u32 type; __u32 type;
struct list_head format; struct list_head format;
struct list_head aliases;
struct list_head list; struct list_head list;
}; };
struct perf_pmu *perf_pmu__find(char *name); struct perf_pmu *perf_pmu__find(char *name);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms); struct list_head *head_terms);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
struct list_head *head_terms);
int perf_pmu_wrap(void); int perf_pmu_wrap(void);
void perf_pmu_error(struct list_head *list, char *name, char const *msg); void perf_pmu_error(struct list_head *list, char *name, char const *msg);

View File

@ -209,6 +209,10 @@ static void define_event_symbols(struct event_format *event,
define_symbolic_values(args->symbol.symbols, ev_name, define_symbolic_values(args->symbol.symbols, ev_name,
cur_field_name); cur_field_name);
break; break;
case PRINT_HEX:
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
case PRINT_BSTRING: case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY: case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING: case PRINT_STRING:
@ -233,7 +237,8 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next); define_event_symbols(event, ev_name, args->next);
} }
static inline struct event_format *find_cache_event(int type) static inline
struct event_format *find_cache_event(struct pevent *pevent, int type)
{ {
static char ev_name[256]; static char ev_name[256];
struct event_format *event; struct event_format *event;
@ -241,7 +246,7 @@ static inline struct event_format *find_cache_event(int type)
if (events[type]) if (events[type])
return events[type]; return events[type];
events[type] = event = trace_find_event(type); events[type] = event = pevent_find_event(pevent, type);
if (!event) if (!event)
return NULL; return NULL;
@ -252,7 +257,8 @@ static inline struct event_format *find_cache_event(int type)
return event; return event;
} }
static void perl_process_tracepoint(union perf_event *pevent __unused, static void perl_process_tracepoint(union perf_event *perf_event __unused,
struct pevent *pevent,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct machine *machine __unused, struct machine *machine __unused,
@ -275,13 +281,13 @@ static void perl_process_tracepoint(union perf_event *pevent __unused,
if (evsel->attr.type != PERF_TYPE_TRACEPOINT) if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return; return;
type = trace_parse_common_type(data); type = trace_parse_common_type(pevent, data);
event = find_cache_event(type); event = find_cache_event(pevent, type);
if (!event) if (!event)
die("ug! no event found for type %d", type); die("ug! no event found for type %d", type);
pid = trace_parse_common_pid(data); pid = trace_parse_common_pid(pevent, data);
sprintf(handler, "%s::%s", event->system, event->name); sprintf(handler, "%s::%s", event->system, event->name);
@ -314,7 +320,8 @@ static void perl_process_tracepoint(union perf_event *pevent __unused,
offset = field->offset; offset = field->offset;
XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
} else { /* FIELD_IS_NUMERIC */ } else { /* FIELD_IS_NUMERIC */
val = read_size(data + field->offset, field->size); val = read_size(pevent, data + field->offset,
field->size);
if (field->flags & FIELD_IS_SIGNED) { if (field->flags & FIELD_IS_SIGNED) {
XPUSHs(sv_2mortal(newSViv(val))); XPUSHs(sv_2mortal(newSViv(val)));
} else { } else {
@ -368,14 +375,15 @@ static void perl_process_event_generic(union perf_event *pevent __unused,
LEAVE; LEAVE;
} }
static void perl_process_event(union perf_event *pevent, static void perl_process_event(union perf_event *event,
struct pevent *pevent,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct machine *machine, struct machine *machine,
struct thread *thread) struct thread *thread)
{ {
perl_process_tracepoint(pevent, sample, evsel, machine, thread); perl_process_tracepoint(event, pevent, sample, evsel, machine, thread);
perl_process_event_generic(pevent, sample, evsel, machine, thread); perl_process_event_generic(event, sample, evsel, machine, thread);
} }
static void run_start_sub(void) static void run_start_sub(void)
@ -448,7 +456,7 @@ static int perl_stop_script(void)
return 0; return 0;
} }
static int perl_generate_script(const char *outfile) static int perl_generate_script(struct pevent *pevent, const char *outfile)
{ {
struct event_format *event = NULL; struct event_format *event = NULL;
struct format_field *f; struct format_field *f;
@ -495,7 +503,7 @@ static int perl_generate_script(const char *outfile)
fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n");
fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n");
while ((event = trace_find_next_event(event))) { while ((event = trace_find_next_event(pevent, event))) {
fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name);
fprintf(ofp, "\tmy ("); fprintf(ofp, "\tmy (");

View File

@ -166,6 +166,10 @@ static void define_event_symbols(struct event_format *event,
define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,
cur_field_name); cur_field_name);
break; break;
case PRINT_HEX:
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
case PRINT_STRING: case PRINT_STRING:
break; break;
case PRINT_TYPE: case PRINT_TYPE:
@ -190,7 +194,8 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next); define_event_symbols(event, ev_name, args->next);
} }
static inline struct event_format *find_cache_event(int type) static inline
struct event_format *find_cache_event(struct pevent *pevent, int type)
{ {
static char ev_name[256]; static char ev_name[256];
struct event_format *event; struct event_format *event;
@ -198,7 +203,7 @@ static inline struct event_format *find_cache_event(int type)
if (events[type]) if (events[type])
return events[type]; return events[type];
events[type] = event = trace_find_event(type); events[type] = event = pevent_find_event(pevent, type);
if (!event) if (!event)
return NULL; return NULL;
@ -209,7 +214,8 @@ static inline struct event_format *find_cache_event(int type)
return event; return event;
} }
static void python_process_event(union perf_event *pevent __unused, static void python_process_event(union perf_event *perf_event __unused,
struct pevent *pevent,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel __unused, struct perf_evsel *evsel __unused,
struct machine *machine __unused, struct machine *machine __unused,
@ -233,13 +239,13 @@ static void python_process_event(union perf_event *pevent __unused,
if (!t) if (!t)
Py_FatalError("couldn't create Python tuple"); Py_FatalError("couldn't create Python tuple");
type = trace_parse_common_type(data); type = trace_parse_common_type(pevent, data);
event = find_cache_event(type); event = find_cache_event(pevent, type);
if (!event) if (!event)
die("ug! no event found for type %d", type); die("ug! no event found for type %d", type);
pid = trace_parse_common_pid(data); pid = trace_parse_common_pid(pevent, data);
sprintf(handler_name, "%s__%s", event->system, event->name); sprintf(handler_name, "%s__%s", event->system, event->name);
@ -284,7 +290,8 @@ static void python_process_event(union perf_event *pevent __unused,
offset = field->offset; offset = field->offset;
obj = PyString_FromString((char *)data + offset); obj = PyString_FromString((char *)data + offset);
} else { /* FIELD_IS_NUMERIC */ } else { /* FIELD_IS_NUMERIC */
val = read_size(data + field->offset, field->size); val = read_size(pevent, data + field->offset,
field->size);
if (field->flags & FIELD_IS_SIGNED) { if (field->flags & FIELD_IS_SIGNED) {
if ((long long)val >= LONG_MIN && if ((long long)val >= LONG_MIN &&
(long long)val <= LONG_MAX) (long long)val <= LONG_MAX)
@ -438,7 +445,7 @@ static int python_stop_script(void)
return err; return err;
} }
static int python_generate_script(const char *outfile) static int python_generate_script(struct pevent *pevent, const char *outfile)
{ {
struct event_format *event = NULL; struct event_format *event = NULL;
struct format_field *f; struct format_field *f;
@ -487,7 +494,7 @@ static int python_generate_script(const char *outfile)
fprintf(ofp, "def trace_end():\n"); fprintf(ofp, "def trace_end():\n");
fprintf(ofp, "\tprint \"in trace_end\"\n\n"); fprintf(ofp, "\tprint \"in trace_end\"\n\n");
while ((event = trace_find_next_event(event))) { while ((event = trace_find_next_event(pevent, event))) {
fprintf(ofp, "def %s__%s(", event->system, event->name); fprintf(ofp, "def %s__%s(", event->system, event->name);
fprintf(ofp, "event_name, "); fprintf(ofp, "event_name, ");
fprintf(ofp, "context, "); fprintf(ofp, "context, ");

View File

@ -14,6 +14,7 @@
#include "sort.h" #include "sort.h"
#include "util.h" #include "util.h"
#include "cpumap.h" #include "cpumap.h"
#include "event-parse.h"
static int perf_session__open(struct perf_session *self, bool force) static int perf_session__open(struct perf_session *self, bool force)
{ {
@ -289,7 +290,6 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
} }
int machine__resolve_callchain(struct machine *self, int machine__resolve_callchain(struct machine *self,
struct perf_evsel *evsel __used,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent) struct symbol **parent)
@ -1449,7 +1449,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
ret += hists__fprintf_nr_events(&session->hists, fp); ret += hists__fprintf_nr_events(&session->hists, fp);
list_for_each_entry(pos, &session->evlist->entries, node) { list_for_each_entry(pos, &session->evlist->entries, node) {
ret += fprintf(fp, "%s stats:\n", event_name(pos)); ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
ret += hists__fprintf_nr_events(&pos->hists, fp); ret += hists__fprintf_nr_events(&pos->hists, fp);
} }
@ -1490,8 +1490,8 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
} }
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel, struct machine *machine, int print_sym,
int print_sym, int print_dso, int print_symoffset) int print_dso, int print_symoffset)
{ {
struct addr_location al; struct addr_location al;
struct callchain_cursor_node *node; struct callchain_cursor_node *node;
@ -1505,7 +1505,7 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) { if (symbol_conf.use_callchain && sample->callchain) {
if (machine__resolve_callchain(machine, evsel, al.thread, if (machine__resolve_callchain(machine, al.thread,
sample->callchain, NULL) != 0) { sample->callchain, NULL) != 0) {
if (verbose) if (verbose)
error("Failed to resolve callchain. Skipping\n"); error("Failed to resolve callchain. Skipping\n");
@ -1611,3 +1611,58 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
perf_header__fprintf_info(session, fp, full); perf_header__fprintf_info(session, fp, full);
fprintf(fp, "# ========\n#\n"); fprintf(fp, "# ========\n#\n");
} }
int __perf_session__set_tracepoints_handlers(struct perf_session *session,
const struct perf_evsel_str_handler *assocs,
size_t nr_assocs)
{
struct perf_evlist *evlist = session->evlist;
struct event_format *format;
struct perf_evsel *evsel;
char *tracepoint, *name;
size_t i;
int err;
for (i = 0; i < nr_assocs; i++) {
err = -ENOMEM;
tracepoint = strdup(assocs[i].name);
if (tracepoint == NULL)
goto out;
err = -ENOENT;
name = strchr(tracepoint, ':');
if (name == NULL)
goto out_free;
*name++ = '\0';
format = pevent_find_event_by_name(session->pevent,
tracepoint, name);
if (format == NULL) {
/*
* Adding a handler for an event not in the session,
* just ignore it.
*/
goto next;
}
evsel = perf_evlist__find_tracepoint_by_id(evlist, format->id);
if (evsel == NULL)
goto next;
err = -EEXIST;
if (evsel->handler.func != NULL)
goto out_free;
evsel->handler.func = assocs[i].handler;
next:
free(tracepoint);
}
err = 0;
out:
return err;
out_free:
free(tracepoint);
goto out;
}

View File

@ -33,6 +33,7 @@ struct perf_session {
struct machine host_machine; struct machine host_machine;
struct rb_root machines; struct rb_root machines;
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct pevent *pevent;
/* /*
* FIXME: Need to split this up further, we need global * FIXME: Need to split this up further, we need global
* stats + per event stats. 'perf diff' also needs * stats + per event stats. 'perf diff' also needs
@ -151,11 +152,20 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type); unsigned int type);
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel, struct machine *machine, int print_sym,
int print_sym, int print_dso, int print_symoffset); int print_dso, int print_symoffset);
int perf_session__cpu_bitmap(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap); const char *cpu_list, unsigned long *cpu_bitmap);
void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full);
struct perf_evsel_str_handler;
int __perf_session__set_tracepoints_handlers(struct perf_session *session,
const struct perf_evsel_str_handler *assocs,
size_t nr_assocs);
#define perf_session__set_tracepoints_handlers(session, array) \
__perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array))
#endif /* __PERF_SESSION_H */ #endif /* __PERF_SESSION_H */

View File

@ -241,6 +241,54 @@ struct sort_entry sort_sym = {
.se_width_idx = HISTC_SYMBOL, .se_width_idx = HISTC_SYMBOL,
}; };
/* --sort srcline */
static int64_t
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
{
return (int64_t)(right->ip - left->ip);
}
static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width __used)
{
FILE *fp;
char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
size_t line_len;
if (path != NULL)
goto out_path;
snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
self->ms.map->dso->long_name, self->ip);
fp = popen(cmd, "r");
if (!fp)
goto out_ip;
if (getline(&path, &line_len, fp) < 0 || !line_len)
goto out_ip;
fclose(fp);
self->srcline = strdup(path);
if (self->srcline == NULL)
goto out_ip;
nl = strchr(self->srcline, '\n');
if (nl != NULL)
*nl = '\0';
path = self->srcline;
out_path:
return repsep_snprintf(bf, size, "%s", path);
out_ip:
return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
}
struct sort_entry sort_srcline = {
.se_header = "Source:Line",
.se_cmp = sort__srcline_cmp,
.se_snprintf = hist_entry__srcline_snprintf,
.se_width_idx = HISTC_SRCLINE,
};
/* --sort parent */ /* --sort parent */
static int64_t static int64_t
@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {
DIM(SORT_PARENT, "parent", sort_parent), DIM(SORT_PARENT, "parent", sort_parent),
DIM(SORT_CPU, "cpu", sort_cpu), DIM(SORT_CPU, "cpu", sort_cpu),
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
DIM(SORT_SRCLINE, "srcline", sort_srcline),
}; };
int sort_dimension__add(const char *tok) int sort_dimension__add(const char *tok)

View File

@ -71,6 +71,7 @@ struct hist_entry {
char level; char level;
bool used; bool used;
u8 filtered; u8 filtered;
char *srcline;
struct symbol *parent; struct symbol *parent;
union { union {
unsigned long position; unsigned long position;
@ -93,6 +94,7 @@ enum sort_type {
SORT_SYM_FROM, SORT_SYM_FROM,
SORT_SYM_TO, SORT_SYM_TO,
SORT_MISPREDICT, SORT_MISPREDICT,
SORT_SRCLINE,
}; };
/* /*

Some files were not shown because too many files have changed in this diff Show More