mirror of https://gitee.com/openkylin/linux.git
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:
commit
2eafeb6a41
|
@ -313,8 +313,8 @@ struct kvm_pmu {
|
|||
u64 counter_bitmask[2];
|
||||
u64 global_ctrl_mask;
|
||||
u8 version;
|
||||
struct kvm_pmc gp_counters[X86_PMC_MAX_GENERIC];
|
||||
struct kvm_pmc fixed_counters[X86_PMC_MAX_FIXED];
|
||||
struct kvm_pmc gp_counters[INTEL_PMC_MAX_GENERIC];
|
||||
struct kvm_pmc fixed_counters[INTEL_PMC_MAX_FIXED];
|
||||
struct irq_work irq_work;
|
||||
u64 reprogram_pmi;
|
||||
};
|
||||
|
|
|
@ -115,8 +115,8 @@ notrace static inline int native_write_msr_safe(unsigned int msr,
|
|||
|
||||
extern unsigned long long native_read_tsc(void);
|
||||
|
||||
extern int native_rdmsr_safe_regs(u32 regs[8]);
|
||||
extern int native_wrmsr_safe_regs(u32 regs[8]);
|
||||
extern int rdmsr_safe_regs(u32 regs[8]);
|
||||
extern int wrmsr_safe_regs(u32 regs[8]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) \
|
||||
((low) = (u32)__native_read_tsc())
|
||||
|
||||
|
@ -237,6 +200,8 @@ do { \
|
|||
(high) = (u32)(_l >> 32); \
|
||||
} while (0)
|
||||
|
||||
#define rdpmcl(counter, val) ((val) = native_read_pmc(counter))
|
||||
|
||||
#define rdtscp(low, high, aux) \
|
||||
do { \
|
||||
unsigned long long _val = native_read_tscp(&(aux)); \
|
||||
|
@ -248,8 +213,7 @@ do { \
|
|||
|
||||
#endif /* !CONFIG_PARAVIRT */
|
||||
|
||||
|
||||
#define checking_wrmsrl(msr, val) wrmsr_safe((msr), (u32)(val), \
|
||||
#define wrmsrl_safe(msr, val) wrmsr_safe((msr), (u32)(val), \
|
||||
(u32)((val) >> 32))
|
||||
|
||||
#define write_tsc(val1, val2) wrmsr(MSR_IA32_TSC, (val1), (val2))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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. */
|
||||
#define rdmsr(msr, val1, val2) \
|
||||
do { \
|
||||
|
@ -176,9 +166,6 @@ do { \
|
|||
_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)
|
||||
{
|
||||
int err;
|
||||
|
@ -186,32 +173,6 @@ static inline int rdmsrl_safe(unsigned msr, unsigned long long *p)
|
|||
*p = paravirt_read_msr(msr, &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)
|
||||
{
|
||||
|
@ -252,6 +213,8 @@ do { \
|
|||
high = _l >> 32; \
|
||||
} while (0)
|
||||
|
||||
#define rdpmcl(counter, val) ((val) = paravirt_read_pmc(counter))
|
||||
|
||||
static inline unsigned long long paravirt_rdtscp(unsigned int *aux)
|
||||
{
|
||||
return PVOP_CALL1(u64, pv_cpu_ops.read_tscp, aux);
|
||||
|
|
|
@ -153,9 +153,7 @@ struct pv_cpu_ops {
|
|||
/* MSR, PMC and TSR operations.
|
||||
err = 0/-EFAULT. wrmsr returns 0/-EFAULT. */
|
||||
u64 (*read_msr)(unsigned int msr, int *err);
|
||||
int (*rdmsr_regs)(u32 *regs);
|
||||
int (*write_msr)(unsigned int msr, unsigned low, unsigned high);
|
||||
int (*wrmsr_regs)(u32 *regs);
|
||||
|
||||
u64 (*read_tsc)(void);
|
||||
u64 (*read_pmc)(int counter);
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
* Performance event hw details:
|
||||
*/
|
||||
|
||||
#define X86_PMC_MAX_GENERIC 32
|
||||
#define X86_PMC_MAX_FIXED 3
|
||||
#define INTEL_PMC_MAX_GENERIC 32
|
||||
#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 MSR_ARCH_PERFMON_PERFCTR0 0xc1
|
||||
|
@ -48,8 +47,7 @@
|
|||
(X86_RAW_EVENT_MASK | \
|
||||
AMD64_EVENTSEL_EVENT)
|
||||
#define AMD64_NUM_COUNTERS 4
|
||||
#define AMD64_NUM_COUNTERS_F15H 6
|
||||
#define AMD64_NUM_COUNTERS_MAX AMD64_NUM_COUNTERS_F15H
|
||||
#define AMD64_NUM_COUNTERS_CORE 6
|
||||
|
||||
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c
|
||||
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8)
|
||||
|
@ -121,16 +119,16 @@ struct x86_pmu_capability {
|
|||
|
||||
/* Instr_Retired.Any: */
|
||||
#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: */
|
||||
#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: */
|
||||
#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b
|
||||
#define X86_PMC_IDX_FIXED_REF_CYCLES (X86_PMC_IDX_FIXED + 2)
|
||||
#define X86_PMC_MSK_FIXED_REF_CYCLES (1ULL << X86_PMC_IDX_FIXED_REF_CYCLES)
|
||||
#define INTEL_PMC_IDX_FIXED_REF_CYCLES (INTEL_PMC_IDX_FIXED + 2)
|
||||
#define INTEL_PMC_MSK_FIXED_REF_CYCLES (1ULL << INTEL_PMC_IDX_FIXED_REF_CYCLES)
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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
|
||||
|
@ -234,6 +232,7 @@ struct perf_guest_switch_msr {
|
|||
|
||||
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_check_microcode(void);
|
||||
#else
|
||||
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_check_microcode(void) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
|
||||
|
|
|
@ -48,7 +48,7 @@ struct arch_uprobe_task {
|
|||
#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_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
|
||||
extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
|
||||
|
|
|
@ -32,7 +32,9 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o
|
|||
|
||||
ifdef CONFIG_PERF_EVENTS
|
||||
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
|
||||
|
||||
obj-$(CONFIG_X86_MCE) += mcheck/
|
||||
|
|
|
@ -19,6 +19,39 @@
|
|||
|
||||
#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
|
||||
/*
|
||||
* 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)) {
|
||||
u64 val;
|
||||
|
||||
if (!rdmsrl_amd_safe(0xc0011005, &val)) {
|
||||
if (!rdmsrl_safe(0xc0011005, &val)) {
|
||||
val |= 1ULL << 54;
|
||||
wrmsrl_amd_safe(0xc0011005, val);
|
||||
wrmsrl_safe(0xc0011005, val);
|
||||
rdmsrl(0xc0011005, val);
|
||||
if (val & (1ULL << 54)) {
|
||||
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);
|
||||
if (err == 0) {
|
||||
mask |= (1 << 10);
|
||||
checking_wrmsrl(MSR_AMD64_MCx_MASK(4), mask);
|
||||
wrmsrl_safe(MSR_AMD64_MCx_MASK(4), mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -947,7 +947,7 @@ static void __cpuinit __print_cpu_msr(void)
|
|||
index_max = msr_range_array[i].max;
|
||||
|
||||
for (index = index_min; index < index_max; index++) {
|
||||
if (rdmsrl_amd_safe(index, &val))
|
||||
if (rdmsrl_safe(index, &val))
|
||||
continue;
|
||||
printk(KERN_INFO " MSR%08x: %016llx\n", index, val);
|
||||
}
|
||||
|
|
|
@ -35,17 +35,6 @@
|
|||
|
||||
#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;
|
||||
|
||||
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;
|
||||
s64 delta;
|
||||
|
||||
if (idx == X86_PMC_IDX_FIXED_BTS)
|
||||
if (idx == INTEL_PMC_IDX_FIXED_BTS)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -86,7 +75,7 @@ u64 x86_perf_event_update(struct perf_event *event)
|
|||
*/
|
||||
again:
|
||||
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,
|
||||
new_raw_count) != prev_raw_count)
|
||||
|
@ -189,7 +178,7 @@ static void release_pmc_hardware(void) {}
|
|||
|
||||
static bool check_hw_exists(void)
|
||||
{
|
||||
u64 val, val_new = 0;
|
||||
u64 val, val_new = ~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.
|
||||
*/
|
||||
val = 0xabcdUL;
|
||||
ret = checking_wrmsrl(x86_pmu_event_addr(0), val);
|
||||
ret |= rdmsrl_safe(x86_pmu_event_addr(0), &val_new);
|
||||
reg = x86_pmu_event_addr(0);
|
||||
ret = wrmsrl_safe(reg, val);
|
||||
ret |= rdmsrl_safe(reg, &val_new);
|
||||
if (ret || val != val_new)
|
||||
goto msr_fail;
|
||||
|
||||
|
@ -240,6 +230,7 @@ static bool check_hw_exists(void)
|
|||
|
||||
msr_fail:
|
||||
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;
|
||||
}
|
||||
|
@ -388,7 +379,7 @@ int x86_pmu_hw_config(struct perf_event *event)
|
|||
int precise = 0;
|
||||
|
||||
/* Support for constant skid */
|
||||
if (x86_pmu.pebs_active) {
|
||||
if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
|
||||
precise++;
|
||||
|
||||
/* Support for IP fixup */
|
||||
|
@ -637,8 +628,8 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
|
|||
c = sched->constraints[sched->state.event];
|
||||
|
||||
/* Prefer fixed purpose counters */
|
||||
if (x86_pmu.num_counters_fixed) {
|
||||
idx = X86_PMC_IDX_FIXED;
|
||||
if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) {
|
||||
idx = INTEL_PMC_IDX_FIXED;
|
||||
for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) {
|
||||
if (!__test_and_set_bit(idx, sched->state.used))
|
||||
goto done;
|
||||
|
@ -646,7 +637,7 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
|
|||
}
|
||||
/* Grab the first unused counter starting with idx */
|
||||
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))
|
||||
goto done;
|
||||
}
|
||||
|
@ -704,8 +695,8 @@ static bool perf_sched_next_event(struct perf_sched *sched)
|
|||
/*
|
||||
* Assign a counter for each event.
|
||||
*/
|
||||
static int perf_assign_events(struct event_constraint **constraints, int n,
|
||||
int wmin, int wmax, int *assign)
|
||||
int perf_assign_events(struct event_constraint **constraints, int n,
|
||||
int wmin, int wmax, int *assign)
|
||||
{
|
||||
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_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->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->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 {
|
||||
hwc->config_base = x86_pmu_config_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;
|
||||
int ret = 0, idx = hwc->idx;
|
||||
|
||||
if (idx == X86_PMC_IDX_FIXED_BTS)
|
||||
if (idx == INTEL_PMC_IDX_FIXED_BTS)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -1316,7 +1309,6 @@ static struct attribute_group x86_pmu_format_group = {
|
|||
static int __init init_hw_perf_events(void)
|
||||
{
|
||||
struct x86_pmu_quirk *quirk;
|
||||
struct event_constraint *c;
|
||||
int err;
|
||||
|
||||
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)
|
||||
quirk->func();
|
||||
|
||||
if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) {
|
||||
WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!",
|
||||
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;
|
||||
if (!x86_pmu.intel_ctrl)
|
||||
x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1;
|
||||
|
||||
perf_events_lapic_init();
|
||||
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,
|
||||
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_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)
|
||||
return 0;
|
||||
|
||||
if (x86_pmu.num_counters_fixed && idx >= X86_PMC_IDX_FIXED) {
|
||||
idx -= X86_PMC_IDX_FIXED;
|
||||
if (x86_pmu.num_counters_fixed && idx >= INTEL_PMC_IDX_FIXED) {
|
||||
idx -= INTEL_PMC_IDX_FIXED;
|
||||
idx |= 1 << 30;
|
||||
}
|
||||
|
||||
|
@ -1649,7 +1612,12 @@ static ssize_t set_attr_rdpmc(struct device *cdev,
|
|||
struct device_attribute *attr,
|
||||
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) {
|
||||
x86_pmu.attr_rdpmc = !!val;
|
||||
|
@ -1682,13 +1650,20 @@ static void x86_pmu_flush_branch_stack(void)
|
|||
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 = {
|
||||
.pmu_enable = x86_pmu_enable,
|
||||
.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,
|
||||
.del = x86_pmu_del,
|
||||
|
@ -1696,11 +1671,11 @@ static struct pmu pmu = {
|
|||
.stop = x86_pmu_stop,
|
||||
.read = x86_pmu_read,
|
||||
|
||||
.start_txn = x86_pmu_start_txn,
|
||||
.cancel_txn = x86_pmu_cancel_txn,
|
||||
.commit_txn = x86_pmu_commit_txn,
|
||||
.start_txn = x86_pmu_start_txn,
|
||||
.cancel_txn = x86_pmu_cancel_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,
|
||||
};
|
||||
|
||||
|
@ -1863,7 +1838,7 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
|
|||
else
|
||||
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
|
||||
} else {
|
||||
if (user_mode(regs))
|
||||
if (!kernel_ip(regs->ip))
|
||||
misc |= PERF_RECORD_MISC_USER;
|
||||
else
|
||||
misc |= PERF_RECORD_MISC_KERNEL;
|
||||
|
|
|
@ -14,6 +14,18 @@
|
|||
|
||||
#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 |
|
||||
* register -------------------------------
|
||||
|
@ -57,7 +69,7 @@ struct amd_nb {
|
|||
};
|
||||
|
||||
/* The maximal number of PEBS events: */
|
||||
#define MAX_PEBS_EVENTS 4
|
||||
#define MAX_PEBS_EVENTS 8
|
||||
|
||||
/*
|
||||
* A debug store configuration.
|
||||
|
@ -349,6 +361,8 @@ struct x86_pmu {
|
|||
void (*cpu_starting)(int cpu);
|
||||
void (*cpu_dying)(int cpu);
|
||||
void (*cpu_dead)(int cpu);
|
||||
|
||||
void (*check_microcode)(void);
|
||||
void (*flush_branch_stack)(void);
|
||||
|
||||
/*
|
||||
|
@ -360,12 +374,16 @@ struct x86_pmu {
|
|||
/*
|
||||
* Intel DebugStore bits
|
||||
*/
|
||||
int bts, pebs;
|
||||
int bts_active, pebs_active;
|
||||
int bts :1,
|
||||
bts_active :1,
|
||||
pebs :1,
|
||||
pebs_active :1,
|
||||
pebs_broken :1;
|
||||
int pebs_record_size;
|
||||
void (*drain_pebs)(struct pt_regs *regs);
|
||||
struct event_constraint *pebs_constraints;
|
||||
void (*pebs_aliases)(struct perf_event *event);
|
||||
int max_pebs_events;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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);
|
||||
|
||||
void x86_pmu_stop(struct perf_event *event, int flags);
|
||||
|
|
|
@ -366,7 +366,7 @@ static void amd_pmu_cpu_starting(int cpu)
|
|||
|
||||
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;
|
||||
|
||||
nb_id = amd_get_nb_id(cpu);
|
||||
|
@ -422,35 +422,6 @@ static struct attribute *amd_format_attr[] = {
|
|||
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 */
|
||||
|
||||
#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 = {
|
||||
.name = "AMD Family 15h",
|
||||
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,
|
||||
|
@ -606,50 +577,68 @@ static __initconst const struct x86_pmu amd_pmu_f15h = {
|
|||
.disable = x86_pmu_disable_event,
|
||||
.hw_config = amd_pmu_hw_config,
|
||||
.schedule_events = x86_schedule_events,
|
||||
.eventsel = MSR_F15H_PERF_CTL,
|
||||
.perfctr = MSR_F15H_PERF_CTR,
|
||||
.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_F15H,
|
||||
.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_f15h,
|
||||
/* nortbridge counters not yet implemented: */
|
||||
#if 0
|
||||
.get_event_constraints = amd_get_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,
|
||||
|
||||
.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)
|
||||
{
|
||||
/* Performance-monitoring supported from K7 and later: */
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* If core performance counter extensions exists, it must be
|
||||
* family 15h, otherwise fail. See x86_pmu_addr_offset().
|
||||
*/
|
||||
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;
|
||||
}
|
||||
x86_pmu = amd_pmu;
|
||||
|
||||
setup_event_constraints();
|
||||
setup_perfctr_core();
|
||||
|
||||
/* Events are common for all AMDs */
|
||||
memcpy(hw_cache_event_ids, amd_hw_cache_event_ids,
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
*/
|
||||
static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly =
|
||||
{
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = 0x003c,
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
||||
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e,
|
||||
[PERF_COUNT_HW_CACHE_MISSES] = 0x412e,
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5,
|
||||
[PERF_COUNT_HW_BUS_CYCLES] = 0x013c,
|
||||
[PERF_COUNT_HW_REF_CPU_CYCLES] = 0x0300, /* pseudo-encoding */
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = 0x003c,
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
||||
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e,
|
||||
[PERF_COUNT_HW_CACHE_MISSES] = 0x412e,
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5,
|
||||
[PERF_COUNT_HW_BUS_CYCLES] = 0x013c,
|
||||
[PERF_COUNT_HW_REF_CPU_CYCLES] = 0x0300, /* pseudo-encoding */
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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_pebs_disable_all();
|
||||
|
@ -763,9 +763,9 @@ static void intel_pmu_enable_all(int added)
|
|||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL,
|
||||
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 =
|
||||
cpuc->events[X86_PMC_IDX_FIXED_BTS];
|
||||
cpuc->events[INTEL_PMC_IDX_FIXED_BTS];
|
||||
|
||||
if (WARN_ON_ONCE(!event))
|
||||
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)
|
||||
{
|
||||
int idx = hwc->idx - X86_PMC_IDX_FIXED;
|
||||
int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
|
||||
u64 ctrl_val, mask;
|
||||
|
||||
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 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_drain_bts_buffer();
|
||||
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)
|
||||
{
|
||||
int idx = hwc->idx - X86_PMC_IDX_FIXED;
|
||||
int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
|
||||
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 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))
|
||||
return;
|
||||
|
||||
|
@ -1003,11 +1003,11 @@ static void intel_pmu_reset(void)
|
|||
printk("clearing PMU state on CPU#%d\n", smp_processor_id());
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
checking_wrmsrl(x86_pmu_config_addr(idx), 0ull);
|
||||
checking_wrmsrl(x86_pmu_event_addr(idx), 0ull);
|
||||
wrmsrl_safe(x86_pmu_config_addr(idx), 0ull);
|
||||
wrmsrl_safe(x86_pmu_event_addr(idx), 0ull);
|
||||
}
|
||||
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)
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
|
@ -1712,11 +1712,56 @@ static __init void intel_clovertown_quirk(void)
|
|||
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)
|
||||
{
|
||||
printk(KERN_WARNING "PEBS disabled due to CPU errata.\n");
|
||||
x86_pmu.pebs = 0;
|
||||
x86_pmu.pebs_constraints = NULL;
|
||||
x86_pmu.check_microcode = intel_snb_check_microcode;
|
||||
intel_snb_check_microcode();
|
||||
}
|
||||
|
||||
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_eax eax;
|
||||
union cpuid10_ebx ebx;
|
||||
struct event_constraint *c;
|
||||
unsigned int unused;
|
||||
int version;
|
||||
|
||||
|
@ -1800,6 +1846,8 @@ __init int intel_pmu_init(void)
|
|||
x86_pmu.events_maskl = ebx.full;
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -248,7 +248,7 @@ void reserve_ds_buffers(void)
|
|||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -295,7 +295,7 @@ int intel_pmu_drain_bts_buffer(void)
|
|||
u64 to;
|
||||
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 perf_output_handle handle;
|
||||
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
|
||||
* set a reset value.
|
||||
*/
|
||||
WARN_ON_ONCE(n > 1);
|
||||
WARN_ONCE(n > 1, "bad leftover pebs %d\n", n);
|
||||
at += n - 1;
|
||||
|
||||
__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
|
||||
* 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_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];
|
||||
if (!test_bit(bit, cpuc->active_mask))
|
||||
continue;
|
||||
|
@ -670,7 +670,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!event || bit >= MAX_PEBS_EVENTS)
|
||||
if (!event || bit >= x86_pmu.max_pebs_events)
|
||||
continue;
|
||||
|
||||
__intel_pmu_pebs_event(event, iregs, at);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -895,8 +895,8 @@ static void p4_pmu_disable_pebs(void)
|
|||
* So at moment let leave metrics turned on forever -- it's
|
||||
* ok for now but need to be revisited!
|
||||
*
|
||||
* (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)0);
|
||||
* (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)0);
|
||||
* (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (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
|
||||
* asserted again and again
|
||||
*/
|
||||
(void)checking_wrmsrl(hwc->config_base,
|
||||
(void)wrmsrl_safe(hwc->config_base,
|
||||
(u64)(p4_config_unpack_cccr(hwc->config)) &
|
||||
~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];
|
||||
|
||||
(void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs);
|
||||
(void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert);
|
||||
(void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs);
|
||||
(void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
(void)checking_wrmsrl(escr_addr, escr_conf);
|
||||
(void)checking_wrmsrl(hwc->config_base,
|
||||
(void)wrmsrl_safe(escr_addr, escr_conf);
|
||||
(void)wrmsrl_safe(hwc->config_base,
|
||||
(cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE);
|
||||
}
|
||||
|
||||
|
@ -1325,7 +1325,7 @@ __init int p4_pmu_init(void)
|
|||
unsigned int low, high;
|
||||
|
||||
/* 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);
|
||||
if (!(low & (1 << 7))) {
|
||||
|
|
|
@ -71,7 +71,7 @@ p6_pmu_disable_event(struct perf_event *event)
|
|||
if (cpuc->enabled)
|
||||
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)
|
||||
|
@ -84,7 +84,7 @@ static void p6_pmu_enable_event(struct perf_event *event)
|
|||
if (cpuc->enabled)
|
||||
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" );
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
#include <asm/microcode.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/perf_event.h>
|
||||
|
||||
MODULE_DESCRIPTION("Microcode Update Driver");
|
||||
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;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(µcode_mutex);
|
||||
if (uci->valid) {
|
||||
enum ucode_state ustate;
|
||||
|
||||
|
@ -288,7 +288,6 @@ static int reload_for_cpu(int cpu)
|
|||
if (ustate == UCODE_ERROR)
|
||||
err = -EINVAL;
|
||||
}
|
||||
mutex_unlock(µcode_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -298,19 +297,31 @@ static ssize_t reload_store(struct device *dev,
|
|||
const char *buf, size_t size)
|
||||
{
|
||||
unsigned long val;
|
||||
int cpu = dev->id;
|
||||
ssize_t ret = 0;
|
||||
int cpu;
|
||||
ssize_t ret = 0, tmp_ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == 1) {
|
||||
get_online_cpus();
|
||||
if (cpu_online(cpu))
|
||||
ret = reload_for_cpu(cpu);
|
||||
put_online_cpus();
|
||||
if (val != 1)
|
||||
return size;
|
||||
|
||||
get_online_cpus();
|
||||
mutex_lock(µcode_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(µcode_mutex);
|
||||
put_online_cpus();
|
||||
|
||||
if (!ret)
|
||||
ret = size;
|
||||
|
@ -339,7 +350,6 @@ static DEVICE_ATTR(version, 0400, version_show, NULL);
|
|||
static DEVICE_ATTR(processor_flags, 0400, pf_show, NULL);
|
||||
|
||||
static struct attribute *mc_default_attrs[] = {
|
||||
&dev_attr_reload.attr,
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_processor_flags.attr,
|
||||
NULL
|
||||
|
@ -516,6 +526,16 @@ static const struct x86_cpu_id microcode_id[] = {
|
|||
MODULE_DEVICE_TABLE(x86cpu, microcode_id);
|
||||
#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)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
@ -540,16 +560,25 @@ static int __init microcode_init(void)
|
|||
mutex_lock(µcode_mutex);
|
||||
|
||||
error = subsys_interface_register(&mc_cpu_interface);
|
||||
|
||||
if (!error)
|
||||
perf_check_microcode();
|
||||
mutex_unlock(µcode_mutex);
|
||||
put_online_cpus();
|
||||
|
||||
if (error)
|
||||
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();
|
||||
if (error)
|
||||
goto out_driver;
|
||||
goto out_ucode_group;
|
||||
|
||||
register_syscore_ops(&mc_syscore_ops);
|
||||
register_hotcpu_notifier(&mc_cpu_notifier);
|
||||
|
@ -559,7 +588,11 @@ static int __init microcode_init(void)
|
|||
|
||||
return 0;
|
||||
|
||||
out_driver:
|
||||
out_ucode_group:
|
||||
sysfs_remove_group(&cpu_subsys.dev_root->kobj,
|
||||
&cpu_root_microcode_group);
|
||||
|
||||
out_driver:
|
||||
get_online_cpus();
|
||||
mutex_lock(µcode_mutex);
|
||||
|
||||
|
@ -568,7 +601,7 @@ static int __init microcode_init(void)
|
|||
mutex_unlock(µcode_mutex);
|
||||
put_online_cpus();
|
||||
|
||||
out_pdev:
|
||||
out_pdev:
|
||||
platform_device_unregister(microcode_pdev);
|
||||
return error;
|
||||
|
||||
|
@ -584,6 +617,9 @@ static void __exit microcode_exit(void)
|
|||
unregister_hotcpu_notifier(&mc_cpu_notifier);
|
||||
unregister_syscore_ops(&mc_syscore_ops);
|
||||
|
||||
sysfs_remove_group(&cpu_subsys.dev_root->kobj,
|
||||
&cpu_root_microcode_group);
|
||||
|
||||
get_online_cpus();
|
||||
mutex_lock(µcode_mutex);
|
||||
|
||||
|
|
|
@ -352,9 +352,7 @@ struct pv_cpu_ops pv_cpu_ops = {
|
|||
#endif
|
||||
.wbinvd = native_wbinvd,
|
||||
.read_msr = native_read_msr_safe,
|
||||
.rdmsr_regs = native_rdmsr_safe_regs,
|
||||
.write_msr = native_write_msr_safe,
|
||||
.wrmsr_regs = native_wrmsr_safe_regs,
|
||||
.read_tsc = native_read_tsc,
|
||||
.read_pmc = native_read_pmc,
|
||||
.read_tscp = native_read_tscp,
|
||||
|
|
|
@ -466,7 +466,7 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
|
|||
task->thread.gs = addr;
|
||||
if (doit) {
|
||||
load_gs_index(0);
|
||||
ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr);
|
||||
ret = wrmsrl_safe(MSR_KERNEL_GS_BASE, addr);
|
||||
}
|
||||
}
|
||||
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
|
||||
__switch_to */
|
||||
loadsegment(fs, 0);
|
||||
ret = checking_wrmsrl(MSR_FS_BASE, addr);
|
||||
ret = wrmsrl_safe(MSR_FS_BASE, addr);
|
||||
}
|
||||
}
|
||||
put_cpu();
|
||||
|
|
|
@ -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.
|
||||
* @mm: the probed address space.
|
||||
* @arch_uprobe: the probepoint information.
|
||||
* @addr: virtual address at which to install the probepoint
|
||||
* 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;
|
||||
struct insn insn;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (idx < X86_PMC_IDX_FIXED)
|
||||
if (idx < INTEL_PMC_IDX_FIXED)
|
||||
return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + idx, MSR_P6_EVNTSEL0);
|
||||
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)
|
||||
|
@ -291,7 +291,7 @@ static void reprogram_idx(struct kvm_pmu *pmu, int idx)
|
|||
if (pmc_is_gp(pmc))
|
||||
reprogram_gp_counter(pmc, pmc->eventsel);
|
||||
else {
|
||||
int fidx = idx - X86_PMC_IDX_FIXED;
|
||||
int fidx = idx - INTEL_PMC_IDX_FIXED;
|
||||
reprogram_fixed_counter(pmc,
|
||||
fixed_en_pmi(pmu->fixed_ctr_ctrl, fidx), fidx);
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu)
|
|||
return;
|
||||
|
||||
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] =
|
||||
((u64)1 << ((entry->eax >> 16) & 0xff)) - 1;
|
||||
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;
|
||||
} else {
|
||||
pmu->nr_arch_fixed_counters = min((int)(entry->edx & 0x1f),
|
||||
X86_PMC_MAX_FIXED);
|
||||
INTEL_PMC_MAX_FIXED);
|
||||
pmu->counter_bitmask[KVM_PMC_FIXED] =
|
||||
((u64)1 << ((entry->edx >> 5) & 0xff)) - 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;
|
||||
}
|
||||
|
||||
|
@ -478,15 +478,15 @@ void kvm_pmu_init(struct kvm_vcpu *vcpu)
|
|||
struct kvm_pmu *pmu = &vcpu->arch.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].vcpu = vcpu;
|
||||
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].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);
|
||||
kvm_pmu_cpuid_update(vcpu);
|
||||
|
@ -498,13 +498,13 @@ void kvm_pmu_reset(struct kvm_vcpu *vcpu)
|
|||
int i;
|
||||
|
||||
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];
|
||||
stop_counter(pmc);
|
||||
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]);
|
||||
|
||||
pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status =
|
||||
|
|
|
@ -710,16 +710,6 @@ TRACE_EVENT(kvm_skinit,
|
|||
__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_EFL_VM (1 << 1)
|
||||
#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",
|
||||
__entry->csbase, __entry->rip,
|
||||
__print_insn(__entry->insn, __entry->len),
|
||||
__print_hex(__entry->insn, __entry->len),
|
||||
__print_symbolic(__entry->flags,
|
||||
kvm_trace_symbol_emul_flags),
|
||||
__entry->failed ? " failed" : ""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <linux/module.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
EXPORT_SYMBOL(native_rdmsr_safe_regs);
|
||||
EXPORT_SYMBOL(native_wrmsr_safe_regs);
|
||||
EXPORT_SYMBOL(rdmsr_safe_regs);
|
||||
EXPORT_SYMBOL(wrmsr_safe_regs);
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
#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]
|
||||
*
|
||||
*/
|
||||
.macro op_safe_regs op
|
||||
ENTRY(native_\op\()_safe_regs)
|
||||
ENTRY(\op\()_safe_regs)
|
||||
CFI_STARTPROC
|
||||
pushq_cfi %rbx
|
||||
pushq_cfi %rbp
|
||||
|
@ -45,13 +45,13 @@ ENTRY(native_\op\()_safe_regs)
|
|||
|
||||
_ASM_EXTABLE(1b, 3b)
|
||||
CFI_ENDPROC
|
||||
ENDPROC(native_\op\()_safe_regs)
|
||||
ENDPROC(\op\()_safe_regs)
|
||||
.endm
|
||||
|
||||
#else /* X86_32 */
|
||||
|
||||
.macro op_safe_regs op
|
||||
ENTRY(native_\op\()_safe_regs)
|
||||
ENTRY(\op\()_safe_regs)
|
||||
CFI_STARTPROC
|
||||
pushl_cfi %ebx
|
||||
pushl_cfi %ebp
|
||||
|
@ -92,7 +92,7 @@ ENTRY(native_\op\()_safe_regs)
|
|||
|
||||
_ASM_EXTABLE(1b, 3b)
|
||||
CFI_ENDPROC
|
||||
ENDPROC(native_\op\()_safe_regs)
|
||||
ENDPROC(\op\()_safe_regs)
|
||||
.endm
|
||||
|
||||
#endif
|
||||
|
|
|
@ -312,7 +312,7 @@ static int op_amd_fill_in_addresses(struct op_msrs * const msrs)
|
|||
goto fail;
|
||||
}
|
||||
/* 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->controls[i].addr = MSR_F15H_PERF_CTL + (i << 1);
|
||||
} else {
|
||||
|
@ -514,7 +514,7 @@ static int op_amd_init(struct oprofile_operations *ops)
|
|||
ops->create_files = setup_ibs_files;
|
||||
|
||||
if (boot_cpu_data.x86 == 0x15) {
|
||||
num_counters = AMD64_NUM_COUNTERS_F15H;
|
||||
num_counters = AMD64_NUM_COUNTERS_CORE;
|
||||
} else {
|
||||
num_counters = AMD64_NUM_COUNTERS;
|
||||
}
|
||||
|
|
|
@ -205,9 +205,9 @@ void syscall32_cpu_init(void)
|
|||
{
|
||||
/* Load these always in case some future AMD CPU supports
|
||||
SYSENTER from compat mode too. */
|
||||
checking_wrmsrl(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS);
|
||||
checking_wrmsrl(MSR_IA32_SYSENTER_ESP, 0ULL);
|
||||
checking_wrmsrl(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target);
|
||||
wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS);
|
||||
wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL);
|
||||
wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target);
|
||||
|
||||
wrmsrl(MSR_CSTAR, ia32_cstar_target);
|
||||
}
|
||||
|
|
|
@ -1124,9 +1124,7 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
|
|||
.wbinvd = native_wbinvd,
|
||||
|
||||
.read_msr = native_read_msr_safe,
|
||||
.rdmsr_regs = native_rdmsr_safe_regs,
|
||||
.write_msr = xen_write_msr_safe,
|
||||
.wrmsr_regs = native_wrmsr_safe_regs,
|
||||
|
||||
.read_tsc = native_read_tsc,
|
||||
.read_pmc = native_read_pmc,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2010 ARM Ltd.
|
||||
* Copyright 2012 Advanced Micro Devices, Inc., Robert Richter
|
||||
*
|
||||
* Perf-events backend for OProfile.
|
||||
*/
|
||||
|
@ -25,7 +26,7 @@ static int oprofile_perf_enabled;
|
|||
static DEFINE_MUTEX(oprofile_perf_mutex);
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -38,7 +39,7 @@ static void op_overflow_handler(struct perf_event *event,
|
|||
u32 cpu = smp_processor_id();
|
||||
|
||||
for (id = 0; id < num_counters; ++id)
|
||||
if (perf_events[cpu][id] == event)
|
||||
if (per_cpu(perf_events, cpu)[id] == event)
|
||||
break;
|
||||
|
||||
if (id != num_counters)
|
||||
|
@ -74,7 +75,7 @@ static int op_create_counter(int cpu, int event)
|
|||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
perf_events[cpu][event] = pevent;
|
||||
per_cpu(perf_events, cpu)[event] = pevent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
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 (id = 0; id < num_counters; ++id) {
|
||||
event = perf_events[cpu][id];
|
||||
event = per_cpu(perf_events, cpu)[id];
|
||||
if (event)
|
||||
perf_event_release_kernel(event);
|
||||
}
|
||||
|
||||
kfree(perf_events[cpu]);
|
||||
kfree(per_cpu(perf_events, cpu));
|
||||
}
|
||||
|
||||
kfree(counter_config);
|
||||
|
@ -277,8 +278,6 @@ int __init oprofile_perf_init(struct oprofile_operations *ops)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(&perf_events, 0, sizeof(perf_events));
|
||||
|
||||
num_counters = perf_num_counters();
|
||||
if (num_counters <= 0) {
|
||||
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) {
|
||||
perf_events[cpu] = kcalloc(num_counters,
|
||||
per_cpu(perf_events, cpu) = kcalloc(num_counters,
|
||||
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 "
|
||||
"for cpu %d\n", num_counters, cpu);
|
||||
ret = -ENOMEM;
|
||||
|
|
|
@ -65,7 +65,7 @@ struct trace_iterator {
|
|||
void *private;
|
||||
int cpu_file;
|
||||
struct mutex mutex;
|
||||
struct ring_buffer_iter *buffer_iter[NR_CPUS];
|
||||
struct ring_buffer_iter **buffer_iter;
|
||||
unsigned long iter_flags;
|
||||
|
||||
/* trace_seq for __print_flags() and __print_symbolic() etc. */
|
||||
|
@ -207,6 +207,9 @@ struct ftrace_event_call {
|
|||
* bit 1: enabled
|
||||
* bit 2: filter_active
|
||||
* 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.
|
||||
*
|
||||
|
|
|
@ -42,8 +42,7 @@
|
|||
* allowed.
|
||||
*
|
||||
* 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
|
||||
* equivalent with static_branch().
|
||||
* same as using STATIC_KEY_INIT_FALSE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -107,12 +106,6 @@ static __always_inline bool static_key_true(struct static_key *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 __stop___jump_table[];
|
||||
|
||||
|
@ -166,14 +159,6 @@ static __always_inline bool static_key_true(struct static_key *key)
|
|||
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)
|
||||
{
|
||||
atomic_inc(&key->enabled);
|
||||
|
|
|
@ -2755,6 +2755,17 @@
|
|||
#define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27
|
||||
#define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e
|
||||
#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_5100_16 0x65f0
|
||||
#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5
|
||||
|
|
|
@ -677,6 +677,7 @@ struct hw_perf_event {
|
|||
u64 last_tag;
|
||||
unsigned long config_base;
|
||||
unsigned long event_base;
|
||||
int event_base_rdpmc;
|
||||
int idx;
|
||||
int last_cpu;
|
||||
|
||||
|
@ -1106,6 +1107,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr,
|
|||
struct task_struct *task,
|
||||
perf_overflow_handler_t callback,
|
||||
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,
|
||||
u64 *enabled, u64 *running);
|
||||
|
||||
|
|
|
@ -1581,7 +1581,6 @@ struct task_struct {
|
|||
#endif
|
||||
#ifdef CONFIG_UPROBES
|
||||
struct uprobe_task *utask;
|
||||
int uprobe_srcu_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ static inline void tracepoint_synchronize_unregister(void)
|
|||
} \
|
||||
static inline void trace_##name##_rcuidle(proto) \
|
||||
{ \
|
||||
if (static_branch(&__tracepoint_##name.key)) \
|
||||
if (static_key_false(&__tracepoint_##name.key)) \
|
||||
__DO_TRACE(&__tracepoint_##name, \
|
||||
TP_PROTO(data_proto), \
|
||||
TP_ARGS(data_args), \
|
||||
|
|
|
@ -571,6 +571,7 @@ static inline void ftrace_test_probe_##call(void) \
|
|||
|
||||
#undef __print_flags
|
||||
#undef __print_symbolic
|
||||
#undef __print_hex
|
||||
#undef __get_dynamic_array
|
||||
#undef __get_str
|
||||
|
||||
|
|
|
@ -1645,6 +1645,8 @@ perf_install_in_context(struct perf_event_context *ctx,
|
|||
lockdep_assert_held(&ctx->mutex);
|
||||
|
||||
event->ctx = ctx;
|
||||
if (event->cpu != -1)
|
||||
event->cpu = cpu;
|
||||
|
||||
if (!task) {
|
||||
/*
|
||||
|
@ -6252,6 +6254,8 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
}
|
||||
}
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
event = perf_event_alloc(&attr, cpu, task, group_leader, NULL,
|
||||
NULL, NULL);
|
||||
if (IS_ERR(event)) {
|
||||
|
@ -6304,7 +6308,7 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
/*
|
||||
* 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)) {
|
||||
err = PTR_ERR(ctx);
|
||||
goto err_alloc;
|
||||
|
@ -6377,20 +6381,23 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
mutex_lock(&ctx->mutex);
|
||||
|
||||
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);
|
||||
list_for_each_entry(sibling, &group_leader->sibling_list,
|
||||
group_entry) {
|
||||
perf_install_in_context(ctx, sibling, cpu);
|
||||
perf_install_in_context(ctx, sibling, event->cpu);
|
||||
get_ctx(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
perf_install_in_context(ctx, event, cpu);
|
||||
perf_install_in_context(ctx, event, event->cpu);
|
||||
++ctx->generation;
|
||||
perf_unpin_context(ctx);
|
||||
mutex_unlock(&ctx->mutex);
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
event->owner = current;
|
||||
|
||||
mutex_lock(¤t->perf_event_mutex);
|
||||
|
@ -6419,6 +6426,7 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
err_alloc:
|
||||
free_event(event);
|
||||
err_task:
|
||||
put_online_cpus();
|
||||
if (task)
|
||||
put_task_struct(task);
|
||||
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);
|
||||
|
||||
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,
|
||||
struct task_struct *child)
|
||||
{
|
||||
|
|
|
@ -38,13 +38,29 @@
|
|||
#define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES)
|
||||
#define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE
|
||||
|
||||
static struct srcu_struct uprobes_srcu;
|
||||
static struct rb_root uprobes_tree = RB_ROOT;
|
||||
|
||||
static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */
|
||||
|
||||
#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 */
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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 rb_node rb_node; /* node in the rb tree */
|
||||
atomic_t ref;
|
||||
|
@ -100,7 +105,8 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register)
|
|||
if (!is_register)
|
||||
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 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)
|
||||
{
|
||||
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;
|
||||
int err = -EFAULT;
|
||||
spinlock_t *ptl;
|
||||
pte_t *ptep;
|
||||
|
||||
addr = page_address_in_vma(page, vma);
|
||||
if (addr == -EFAULT)
|
||||
goto out;
|
||||
return -EFAULT;
|
||||
|
||||
pgd = pgd_offset(mm, addr);
|
||||
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);
|
||||
ptep = page_check_address(page, mm, addr, &ptl, 0);
|
||||
if (!ptep)
|
||||
goto out;
|
||||
return -EAGAIN;
|
||||
|
||||
get_page(kpage);
|
||||
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);
|
||||
put_page(page);
|
||||
pte_unmap_unlock(ptep, ptl);
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,9 +210,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
|||
void *vaddr_old, *vaddr_new;
|
||||
struct vm_area_struct *vma;
|
||||
struct uprobe *uprobe;
|
||||
loff_t addr;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
/* Read the page with vaddr into memory */
|
||||
ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma);
|
||||
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)
|
||||
goto put_out;
|
||||
|
||||
addr = vma_address(vma, uprobe->offset);
|
||||
if (vaddr != (unsigned long)addr)
|
||||
goto put_out;
|
||||
|
||||
ret = -ENOMEM;
|
||||
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
|
||||
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);
|
||||
|
||||
memcpy(vaddr_new, vaddr_old, PAGE_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);
|
||||
memcpy(vaddr_new + (vaddr & ~PAGE_MASK), &opcode, UPROBE_SWBP_INSN_SIZE);
|
||||
|
||||
kunmap_atomic(vaddr_new);
|
||||
kunmap_atomic(vaddr_old);
|
||||
|
@ -291,6 +270,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
|||
put_out:
|
||||
put_page(old_page);
|
||||
|
||||
if (unlikely(ret == -EAGAIN))
|
||||
goto retry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -312,7 +293,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_
|
|||
void *vaddr_new;
|
||||
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)
|
||||
return ret;
|
||||
|
||||
|
@ -333,10 +314,20 @@ static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr)
|
|||
uprobe_opcode_t opcode;
|
||||
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);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
out:
|
||||
if (is_swbp_insn(&opcode))
|
||||
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 result;
|
||||
|
||||
/*
|
||||
* See the comment near uprobes_hash().
|
||||
*/
|
||||
result = is_swbp_at_addr(mm, vaddr);
|
||||
if (result == 1)
|
||||
return -EEXIST;
|
||||
|
@ -520,7 +513,6 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset)
|
|||
uprobe->inode = igrab(inode);
|
||||
uprobe->offset = offset;
|
||||
init_rwsem(&uprobe->consumer_rwsem);
|
||||
INIT_LIST_HEAD(&uprobe->pending_list);
|
||||
|
||||
/* add to uprobes_tree, sorted on inode:offset */
|
||||
cur_uprobe = insert_uprobe(uprobe);
|
||||
|
@ -588,20 +580,22 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)
|
|||
}
|
||||
|
||||
static int
|
||||
__copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insn,
|
||||
unsigned long nbytes, unsigned long offset)
|
||||
__copy_insn(struct address_space *mapping, struct file *filp, char *insn,
|
||||
unsigned long nbytes, loff_t offset)
|
||||
{
|
||||
struct file *filp = vma->vm_file;
|
||||
struct page *page;
|
||||
void *vaddr;
|
||||
unsigned long off1;
|
||||
unsigned long idx;
|
||||
unsigned long off;
|
||||
pgoff_t idx;
|
||||
|
||||
if (!filp)
|
||||
return -EINVAL;
|
||||
|
||||
idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT);
|
||||
off1 = offset &= ~PAGE_MASK;
|
||||
if (!mapping->a_ops->readpage)
|
||||
return -EIO;
|
||||
|
||||
idx = offset >> PAGE_CACHE_SHIFT;
|
||||
off = offset & ~PAGE_MASK;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
vaddr = kmap_atomic(page);
|
||||
memcpy(insn, vaddr + off1, nbytes);
|
||||
memcpy(insn, vaddr + off, nbytes);
|
||||
kunmap_atomic(vaddr);
|
||||
page_cache_release(page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)
|
||||
static int copy_insn(struct uprobe *uprobe, struct file *filp)
|
||||
{
|
||||
struct address_space *mapping;
|
||||
unsigned long nbytes;
|
||||
int bytes;
|
||||
|
||||
addr &= ~PAGE_MASK;
|
||||
nbytes = PAGE_SIZE - addr;
|
||||
nbytes = PAGE_SIZE - (uprobe->offset & ~PAGE_MASK);
|
||||
mapping = uprobe->inode->i_mapping;
|
||||
|
||||
/* 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 */
|
||||
if (nbytes < bytes) {
|
||||
if (__copy_insn(mapping, vma, uprobe->arch.insn + nbytes,
|
||||
bytes - nbytes, uprobe->offset + nbytes))
|
||||
return -ENOMEM;
|
||||
|
||||
int err = __copy_insn(mapping, filp, uprobe->arch.insn + nbytes,
|
||||
bytes - nbytes, uprobe->offset + nbytes);
|
||||
if (err)
|
||||
return err;
|
||||
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
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -687,20 +678,22 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
|
|||
if (!uprobe->consumers)
|
||||
return -EEXIST;
|
||||
|
||||
addr = (unsigned long)vaddr;
|
||||
|
||||
if (!(uprobe->flags & UPROBE_COPY_INSN)) {
|
||||
ret = copy_insn(uprobe, vma, addr);
|
||||
ret = copy_insn(uprobe, vma->vm_file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -713,7 +706,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
|
|||
* Hence increment before and decrement on failure.
|
||||
*/
|
||||
atomic_inc(&mm->uprobes_state.count);
|
||||
ret = set_swbp(&uprobe->arch, mm, addr);
|
||||
ret = set_swbp(&uprobe->arch, mm, vaddr);
|
||||
if (ret)
|
||||
atomic_dec(&mm->uprobes_state.count);
|
||||
|
||||
|
@ -721,27 +714,21 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* There could be threads that have hit the breakpoint and are entering the
|
||||
* notifier code and trying to acquire the uprobes_treelock. The thread
|
||||
* calling delete_uprobe() that is removing the uprobe from the rb_tree can
|
||||
* 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.
|
||||
* There could be threads that have already hit the breakpoint. They
|
||||
* will recheck the current insn and restart if find_uprobe() fails.
|
||||
* See find_active_uprobe().
|
||||
*/
|
||||
static void delete_uprobe(struct uprobe *uprobe)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
synchronize_srcu(&uprobes_srcu);
|
||||
spin_lock_irqsave(&uprobes_treelock, flags);
|
||||
rb_erase(&uprobe->rb_node, &uprobes_tree);
|
||||
spin_unlock_irqrestore(&uprobes_treelock, flags);
|
||||
|
@ -750,139 +737,135 @@ static void delete_uprobe(struct uprobe *uprobe)
|
|||
atomic_dec(&uprobe_events);
|
||||
}
|
||||
|
||||
static struct vma_info *
|
||||
__find_next_vma_info(struct address_space *mapping, struct list_head *head,
|
||||
struct vma_info *vi, loff_t offset, bool is_register)
|
||||
struct map_info {
|
||||
struct map_info *next;
|
||||
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 vm_area_struct *vma;
|
||||
struct vma_info *tmpvi;
|
||||
unsigned long pgoff;
|
||||
int existing_vma;
|
||||
loff_t vaddr;
|
||||
|
||||
pgoff = offset >> PAGE_SHIFT;
|
||||
struct map_info *curr = NULL;
|
||||
struct map_info *prev = NULL;
|
||||
struct map_info *info;
|
||||
int more = 0;
|
||||
|
||||
again:
|
||||
mutex_lock(&mapping->i_mmap_mutex);
|
||||
vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
|
||||
if (!valid_vma(vma, is_register))
|
||||
continue;
|
||||
|
||||
existing_vma = 0;
|
||||
vaddr = vma_address(vma, offset);
|
||||
|
||||
list_for_each_entry(tmpvi, head, probe_list) {
|
||||
if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) {
|
||||
existing_vma = 1;
|
||||
break;
|
||||
}
|
||||
if (!prev && !more) {
|
||||
/*
|
||||
* Needs GFP_NOWAIT to avoid i_mmap_mutex recursion through
|
||||
* reclaim. This is optimistic, no harm done if it fails.
|
||||
*/
|
||||
prev = kmalloc(sizeof(struct map_info),
|
||||
GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
if (prev)
|
||||
prev->next = NULL;
|
||||
}
|
||||
if (!prev) {
|
||||
more++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Another vma needs a probe to be installed. However skip
|
||||
* 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);
|
||||
if (!atomic_inc_not_zero(&vma->vm_mm->mm_users))
|
||||
continue;
|
||||
|
||||
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);
|
||||
|
||||
if (!retvi)
|
||||
kfree(vi);
|
||||
if (!more)
|
||||
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)
|
||||
{
|
||||
struct list_head try_list;
|
||||
struct vm_area_struct *vma;
|
||||
struct address_space *mapping;
|
||||
struct vma_info *vi, *tmpvi;
|
||||
struct mm_struct *mm;
|
||||
loff_t vaddr;
|
||||
int ret;
|
||||
struct map_info *info;
|
||||
int err = 0;
|
||||
|
||||
mapping = uprobe->inode->i_mapping;
|
||||
INIT_LIST_HEAD(&try_list);
|
||||
info = build_map_info(uprobe->inode->i_mapping,
|
||||
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 (;;) {
|
||||
vi = find_next_vma_info(mapping, &try_list, uprobe->offset, is_register);
|
||||
if (!vi)
|
||||
break;
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
if (IS_ERR(vi)) {
|
||||
ret = PTR_ERR(vi);
|
||||
break;
|
||||
}
|
||||
down_write(&mm->mmap_sem);
|
||||
vma = find_vma(mm, (unsigned long)info->vaddr);
|
||||
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 ||
|
||||
vaddr != vi->vaddr) {
|
||||
list_del(&vi->probe_list);
|
||||
kfree(vi);
|
||||
up_read(&mm->mmap_sem);
|
||||
mmput(mm);
|
||||
continue;
|
||||
}
|
||||
vma_address(vma, uprobe->offset) != info->vaddr)
|
||||
goto unlock;
|
||||
|
||||
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 (ret && ret == -EEXIST)
|
||||
ret = 0;
|
||||
if (ret)
|
||||
break;
|
||||
err = install_breakpoint(uprobe, mm, vma, info->vaddr);
|
||||
/*
|
||||
* We can race against uprobe_mmap(), see the
|
||||
* 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) {
|
||||
list_del(&vi->probe_list);
|
||||
kfree(vi);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct list_head tmp_list;
|
||||
struct uprobe *uprobe, *u;
|
||||
struct uprobe *uprobe;
|
||||
struct inode *inode;
|
||||
int ret, count;
|
||||
|
||||
|
@ -1066,12 +1049,9 @@ int uprobe_mmap(struct vm_area_struct *vma)
|
|||
ret = 0;
|
||||
count = 0;
|
||||
|
||||
list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
|
||||
loff_t vaddr;
|
||||
|
||||
list_del(&uprobe->pending_list);
|
||||
list_for_each_entry(uprobe, &tmp_list, pending_list) {
|
||||
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) {
|
||||
put_uprobe(uprobe);
|
||||
|
@ -1079,8 +1059,10 @@ int uprobe_mmap(struct vm_area_struct *vma)
|
|||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
struct list_head tmp_list;
|
||||
struct uprobe *uprobe, *u;
|
||||
struct uprobe *uprobe;
|
||||
struct inode *inode;
|
||||
|
||||
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));
|
||||
build_probe_list(inode, &tmp_list);
|
||||
|
||||
list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
|
||||
loff_t vaddr;
|
||||
|
||||
list_del(&uprobe->pending_list);
|
||||
vaddr = vma_address(vma, uprobe->offset);
|
||||
list_for_each_entry(uprobe, &tmp_list, pending_list) {
|
||||
loff_t vaddr = vma_address(vma, uprobe->offset);
|
||||
|
||||
if (vaddr >= start && vaddr < end) {
|
||||
/*
|
||||
|
@ -1378,9 +1357,6 @@ void uprobe_free_utask(struct task_struct *t)
|
|||
{
|
||||
struct uprobe_task *utask = t->utask;
|
||||
|
||||
if (t->uprobe_srcu_id != -1)
|
||||
srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id);
|
||||
|
||||
if (!utask)
|
||||
return;
|
||||
|
||||
|
@ -1398,7 +1374,6 @@ void uprobe_free_utask(struct task_struct *t)
|
|||
void uprobe_copy_process(struct task_struct *t)
|
||||
{
|
||||
t->utask = NULL;
|
||||
t->uprobe_srcu_id = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1417,7 +1392,6 @@ static struct uprobe_task *add_utask(void)
|
|||
if (unlikely(!utask))
|
||||
return NULL;
|
||||
|
||||
utask->active_uprobe = NULL;
|
||||
current->utask = utask;
|
||||
return utask;
|
||||
}
|
||||
|
@ -1479,41 +1453,64 @@ static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs)
|
|||
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.
|
||||
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
|
||||
*/
|
||||
static void handle_swbp(struct pt_regs *regs)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
struct uprobe_task *utask;
|
||||
struct uprobe *uprobe;
|
||||
struct mm_struct *mm;
|
||||
unsigned long bp_vaddr;
|
||||
int uninitialized_var(is_swbp);
|
||||
|
||||
uprobe = NULL;
|
||||
bp_vaddr = uprobe_get_swbp_addr(regs);
|
||||
mm = current->mm;
|
||||
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);
|
||||
uprobe = find_active_uprobe(bp_vaddr, &is_swbp);
|
||||
|
||||
if (!uprobe) {
|
||||
/* No matching uprobe; signal SIGTRAP. */
|
||||
send_sig(SIGTRAP, current, 0);
|
||||
if (is_swbp > 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;
|
||||
}
|
||||
|
||||
|
@ -1620,7 +1617,6 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs)
|
|||
utask->state = UTASK_BP_HIT;
|
||||
|
||||
set_thread_flag(TIF_UPROBE);
|
||||
current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1655,7 +1651,6 @@ static int __init init_uprobes(void)
|
|||
mutex_init(&uprobes_mutex[i]);
|
||||
mutex_init(&uprobes_mmap_mutex[i]);
|
||||
}
|
||||
init_srcu_struct(&uprobes_srcu);
|
||||
|
||||
return register_die_notifier(&uprobe_exception_nb);
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,
|
|||
|
||||
static int __register_ftrace_function(struct ftrace_ops *ops)
|
||||
{
|
||||
if (ftrace_disabled)
|
||||
if (unlikely(ftrace_disabled))
|
||||
return -ENODEV;
|
||||
|
||||
if (FTRACE_WARN_ON(ops == &global_ops))
|
||||
|
@ -4299,16 +4299,12 @@ int register_ftrace_function(struct ftrace_ops *ops)
|
|||
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
if (unlikely(ftrace_disabled))
|
||||
goto out_unlock;
|
||||
|
||||
ret = __register_ftrace_function(ops);
|
||||
if (!ret)
|
||||
ret = ftrace_startup(ops, 0);
|
||||
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ftrace_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_ftrace_function);
|
||||
|
|
|
@ -3239,6 +3239,10 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
if (cpu_buffer->commit_page == cpu_buffer->reader_page)
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -830,6 +830,8 @@ int register_tracer(struct tracer *type)
|
|||
current_trace = saved_tracer;
|
||||
if (ret) {
|
||||
printk(KERN_CONT "FAILED!\n");
|
||||
/* Add the warning after printing 'FAILED' */
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
/* 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)
|
||||
{
|
||||
struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, iter->cpu);
|
||||
|
||||
iter->idx++;
|
||||
if (iter->buffer_iter[iter->cpu])
|
||||
ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
|
||||
if (buf_iter)
|
||||
ring_buffer_read(buf_iter, NULL);
|
||||
}
|
||||
|
||||
static struct trace_entry *
|
||||
|
@ -1718,7 +1722,7 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
|
|||
unsigned long *lost_events)
|
||||
{
|
||||
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)
|
||||
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;
|
||||
|
||||
if (!iter->buffer_iter[cpu])
|
||||
buf_iter = trace_buffer_iter(iter, cpu);
|
||||
if (!buf_iter)
|
||||
return;
|
||||
|
||||
buf_iter = iter->buffer_iter[cpu];
|
||||
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)
|
||||
{
|
||||
struct ring_buffer_iter *buf_iter;
|
||||
int cpu;
|
||||
|
||||
/* If we are looking at one CPU buffer, only check that one */
|
||||
if (iter->cpu_file != TRACE_PIPE_ALL_CPU) {
|
||||
cpu = iter->cpu_file;
|
||||
if (iter->buffer_iter[cpu]) {
|
||||
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu]))
|
||||
buf_iter = trace_buffer_iter(iter, cpu);
|
||||
if (buf_iter) {
|
||||
if (!ring_buffer_iter_empty(buf_iter))
|
||||
return 0;
|
||||
} else {
|
||||
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) {
|
||||
if (iter->buffer_iter[cpu]) {
|
||||
if (!ring_buffer_iter_empty(iter->buffer_iter[cpu]))
|
||||
buf_iter = trace_buffer_iter(iter, cpu);
|
||||
if (buf_iter) {
|
||||
if (!ring_buffer_iter_empty(buf_iter))
|
||||
return 0;
|
||||
} else {
|
||||
if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu))
|
||||
|
@ -2381,6 +2388,11 @@ __tracing_open(struct inode *inode, struct file *file)
|
|||
if (!iter)
|
||||
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
|
||||
* changes on it while we are reading.
|
||||
|
@ -2441,6 +2453,8 @@ __tracing_open(struct inode *inode, struct file *file)
|
|||
fail:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
kfree(iter->trace);
|
||||
kfree(iter->buffer_iter);
|
||||
release:
|
||||
seq_release_private(inode, file);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
@ -2481,6 +2495,7 @@ static int tracing_release(struct inode *inode, struct file *file)
|
|||
mutex_destroy(&iter->mutex);
|
||||
free_cpumask_var(iter->started);
|
||||
kfree(iter->trace);
|
||||
kfree(iter->buffer_iter);
|
||||
seq_release_private(inode, file);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -317,6 +317,14 @@ struct tracer {
|
|||
|
||||
#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 tracing_is_enabled(void);
|
||||
void trace_wake_up(void);
|
||||
|
|
|
@ -538,7 +538,7 @@ get_return_for_leaf(struct trace_iterator *iter,
|
|||
next = &data->ret;
|
||||
} 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 */
|
||||
if (ring_iter)
|
||||
|
|
|
@ -1325,4 +1325,4 @@ __init static int init_events(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(init_events);
|
||||
early_initcall(init_events);
|
||||
|
|
|
@ -250,8 +250,12 @@ endef
|
|||
all_objs := $(sort $(ALL_OBJS))
|
||||
all_deps := $(all_objs:%.o=.%.d)
|
||||
|
||||
# let .d file also depends on the source and header files
|
||||
define check_deps
|
||||
$(CC) -M $(CFLAGS) $< > $@;
|
||||
@set -e; $(RM) $@; \
|
||||
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
$(RM) $@.$$$$
|
||||
endef
|
||||
|
||||
$(gui_deps): ks_version.h
|
||||
|
@ -270,11 +274,13 @@ endif
|
|||
|
||||
tags: force
|
||||
$(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
|
||||
$(RM) TAGS
|
||||
find . -name '*.[ch]' | xargs etags
|
||||
find . -name '*.[ch]' | xargs etags \
|
||||
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
|
||||
|
||||
define do_install
|
||||
$(print_install) \
|
||||
|
@ -290,7 +296,7 @@ install_lib: all_cmd install_plugins install_python
|
|||
install: install_lib
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d
|
||||
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d
|
||||
$(RM) tags TAGS
|
||||
|
||||
endif # skip-makefile
|
||||
|
|
|
@ -467,8 +467,10 @@ int pevent_register_function(struct pevent *pevent, char *func,
|
|||
item->mod = NULL;
|
||||
item->addr = addr;
|
||||
|
||||
pevent->funclist = item;
|
||||
if (!item->func || (mod && !item->mod))
|
||||
die("malloc func");
|
||||
|
||||
pevent->funclist = item;
|
||||
pevent->func_count++;
|
||||
|
||||
return 0;
|
||||
|
@ -511,12 +513,12 @@ struct printk_list {
|
|||
|
||||
static int printk_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct func_map *fa = a;
|
||||
const struct func_map *fb = b;
|
||||
const struct printk_map *pa = a;
|
||||
const struct printk_map *pb = b;
|
||||
|
||||
if (fa->addr < fb->addr)
|
||||
if (pa->addr < pb->addr)
|
||||
return -1;
|
||||
if (fa->addr > fb->addr)
|
||||
if (pa->addr > pb->addr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -583,10 +585,13 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt,
|
|||
item = malloc_or_die(sizeof(*item));
|
||||
|
||||
item->next = pevent->printklist;
|
||||
pevent->printklist = item;
|
||||
item->printk = strdup(fmt);
|
||||
item->addr = addr;
|
||||
|
||||
if (!item->printk)
|
||||
die("malloc fmt");
|
||||
|
||||
pevent->printklist = item;
|
||||
pevent->printk_count++;
|
||||
|
||||
return 0;
|
||||
|
@ -616,7 +621,9 @@ static struct event_format *alloc_event(void)
|
|||
{
|
||||
struct event_format *event;
|
||||
|
||||
event = malloc_or_die(sizeof(*event));
|
||||
event = malloc(sizeof(*event));
|
||||
if (!event)
|
||||
return NULL;
|
||||
memset(event, 0, sizeof(*event));
|
||||
|
||||
return event;
|
||||
|
@ -626,12 +633,8 @@ static void add_event(struct pevent *pevent, struct event_format *event)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!pevent->events)
|
||||
pevent->events = malloc_or_die(sizeof(event));
|
||||
else
|
||||
pevent->events =
|
||||
realloc(pevent->events, sizeof(event) *
|
||||
(pevent->nr_events + 1));
|
||||
pevent->events = realloc(pevent->events, sizeof(event) *
|
||||
(pevent->nr_events + 1));
|
||||
if (!pevent->events)
|
||||
die("Can not allocate events");
|
||||
|
||||
|
@ -697,6 +700,10 @@ static void free_arg(struct print_arg *arg)
|
|||
free_arg(arg->symbol.field);
|
||||
free_flag_sym(arg->symbol.symbols);
|
||||
break;
|
||||
case PRINT_HEX:
|
||||
free_arg(arg->hex.field);
|
||||
free_arg(arg->hex.size);
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
free(arg->typecast.type);
|
||||
free_arg(arg->typecast.item);
|
||||
|
@ -775,6 +782,25 @@ int pevent_peek_char(void)
|
|||
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 __read_token(char **tok)
|
||||
|
@ -859,17 +885,10 @@ static enum event_type __read_token(char **tok)
|
|||
do {
|
||||
if (i == (BUFSIZ - 1)) {
|
||||
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;
|
||||
|
||||
if (extend_token(tok, buf, tok_size) < 0)
|
||||
return EVENT_NONE;
|
||||
i = 0;
|
||||
}
|
||||
last_ch = ch;
|
||||
|
@ -908,17 +927,10 @@ static enum event_type __read_token(char **tok)
|
|||
while (get_type(__peek_char()) == type) {
|
||||
if (i == (BUFSIZ - 1)) {
|
||||
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;
|
||||
|
||||
if (extend_token(tok, buf, tok_size) < 0)
|
||||
return EVENT_NONE;
|
||||
i = 0;
|
||||
}
|
||||
ch = __read_char();
|
||||
|
@ -927,14 +939,7 @@ static enum event_type __read_token(char **tok)
|
|||
|
||||
out:
|
||||
buf[i] = 0;
|
||||
if (*tok) {
|
||||
*tok = realloc(*tok, tok_size + i);
|
||||
if (!*tok)
|
||||
return EVENT_NONE;
|
||||
strcat(*tok, buf);
|
||||
} else
|
||||
*tok = strdup(buf);
|
||||
if (!*tok)
|
||||
if (extend_token(tok, buf, tok_size + i + 1) < 0)
|
||||
return EVENT_NONE;
|
||||
|
||||
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;
|
||||
|
||||
if (field->type) {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(last_token) + 2);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
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, 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) {
|
||||
enum event_type last_type = type;
|
||||
char *brackets = token;
|
||||
char *new_brackets;
|
||||
int len;
|
||||
|
||||
field->flags |= FIELD_IS_ARRAY;
|
||||
|
@ -1301,9 +1313,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
len = 1;
|
||||
last_type = type;
|
||||
|
||||
brackets = realloc(brackets,
|
||||
strlen(brackets) +
|
||||
strlen(token) + len);
|
||||
new_brackets = realloc(brackets,
|
||||
strlen(brackets) +
|
||||
strlen(token) + len);
|
||||
if (!new_brackets) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
brackets = new_brackets;
|
||||
if (len == 2)
|
||||
strcat(brackets, " ");
|
||||
strcat(brackets, token);
|
||||
|
@ -1319,7 +1336,12 @@ static int event_read_fields(struct event_format *event, struct format_field **f
|
|||
|
||||
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, "]");
|
||||
|
||||
/* 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;
|
||||
*/
|
||||
if (type == EVENT_ITEM) {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(field->name) +
|
||||
strlen(brackets) + 2);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(field->name) +
|
||||
strlen(brackets) + 2);
|
||||
if (!new_type) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
field->type = new_type;
|
||||
strcat(field->type, " ");
|
||||
strcat(field->type, 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;
|
||||
type = read_token(&token);
|
||||
} else {
|
||||
field->type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(brackets) + 1);
|
||||
char *new_type;
|
||||
new_type = realloc(field->type,
|
||||
strlen(field->type) +
|
||||
strlen(brackets) + 1);
|
||||
if (!new_type) {
|
||||
free(brackets);
|
||||
goto fail;
|
||||
}
|
||||
field->type = new_type;
|
||||
strcat(field->type, 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 */
|
||||
if ((strcmp(arg->op.op, "*") == 0) &&
|
||||
type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
|
||||
char *new_atom;
|
||||
|
||||
if (left->type != PRINT_ATOM)
|
||||
die("bad pointer type");
|
||||
left->atom.atom = realloc(left->atom.atom,
|
||||
new_atom = realloc(left->atom.atom,
|
||||
strlen(left->atom.atom) + 3);
|
||||
if (!new_atom)
|
||||
goto out_free;
|
||||
|
||||
left->atom.atom = new_atom;
|
||||
strcat(left->atom.atom, " *");
|
||||
free(arg->op.op);
|
||||
*arg = *left;
|
||||
|
@ -2146,6 +2186,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
|
|||
if (value == NULL)
|
||||
goto out_free;
|
||||
field->value = strdup(value);
|
||||
if (field->value == NULL)
|
||||
goto out_free;
|
||||
|
||||
free_arg(arg);
|
||||
arg = alloc_arg();
|
||||
|
@ -2159,6 +2201,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
|
|||
if (value == NULL)
|
||||
goto out_free;
|
||||
field->str = strdup(value);
|
||||
if (field->str == NULL)
|
||||
goto out_free;
|
||||
free_arg(arg);
|
||||
arg = NULL;
|
||||
|
||||
|
@ -2259,6 +2303,45 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
|
|||
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
|
||||
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;
|
||||
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) {
|
||||
free_token(token);
|
||||
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 */
|
||||
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, token);
|
||||
free_token(token);
|
||||
|
@ -2835,7 +2931,7 @@ static int get_common_info(struct pevent *pevent,
|
|||
event = pevent->events[0];
|
||||
field = pevent_find_common_field(event, type);
|
||||
if (!field)
|
||||
die("field '%s' not found", type);
|
||||
return -1;
|
||||
|
||||
*offset = field->offset;
|
||||
*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)
|
||||
{
|
||||
int ret;
|
||||
return __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_lock_depth");
|
||||
}
|
||||
|
||||
ret = __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_lock_depth");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
static int parse_common_migrate_disable(struct pevent *pevent, void *data)
|
||||
{
|
||||
return __parse_common(pevent, data,
|
||||
&pevent->ld_size, &pevent->ld_offset,
|
||||
"common_migrate_disable");
|
||||
}
|
||||
|
||||
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;
|
||||
case PRINT_FLAGS:
|
||||
case PRINT_SYMBOL:
|
||||
case PRINT_HEX:
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
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 print_flag_sym *flag;
|
||||
struct format_field *field;
|
||||
unsigned long long val, fval;
|
||||
unsigned long addr;
|
||||
char *str;
|
||||
unsigned char *hex;
|
||||
int print;
|
||||
int len;
|
||||
int i, len;
|
||||
|
||||
switch (arg->type) {
|
||||
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);
|
||||
return;
|
||||
case PRINT_FIELD:
|
||||
if (!arg->field.field) {
|
||||
arg->field.field = pevent_find_any_field(event, arg->field.name);
|
||||
if (!arg->field.field)
|
||||
field = arg->field.field;
|
||||
if (!field) {
|
||||
field = pevent_find_any_field(event, arg->field.name);
|
||||
if (!field)
|
||||
die("field %s not found", arg->field.name);
|
||||
arg->field.field = field;
|
||||
}
|
||||
/* 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
|
||||
* and the size is the same as long_size, assume that it
|
||||
* is a pointer.
|
||||
*/
|
||||
if (!(arg->field.field->flags & FIELD_IS_ARRAY) &&
|
||||
arg->field.field->size == pevent->long_size) {
|
||||
addr = *(unsigned long *)(data + arg->field.field->offset);
|
||||
if (!(field->flags & FIELD_IS_ARRAY) &&
|
||||
field->size == pevent->long_size) {
|
||||
addr = *(unsigned long *)(data + field->offset);
|
||||
trace_seq_printf(s, "%lx", addr);
|
||||
break;
|
||||
}
|
||||
str = malloc_or_die(len + 1);
|
||||
memcpy(str, data + arg->field.field->offset, len);
|
||||
memcpy(str, data + field->offset, len);
|
||||
str[len] = 0;
|
||||
print_str_to_seq(s, format, len_arg, str);
|
||||
free(str);
|
||||
|
@ -3281,6 +3383,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
}
|
||||
}
|
||||
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:
|
||||
break;
|
||||
|
@ -3299,7 +3418,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|||
break;
|
||||
}
|
||||
case PRINT_BSTRING:
|
||||
trace_seq_printf(s, format, arg->string.string);
|
||||
print_str_to_seq(s, format, len_arg, arg->string.string);
|
||||
break;
|
||||
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->next = strings;
|
||||
string->str = strdup(str.buffer);
|
||||
if (!string->str)
|
||||
die("malloc str");
|
||||
|
||||
args[i] = (unsigned long long)string->str;
|
||||
strings = string;
|
||||
trace_seq_destroy(&str);
|
||||
break;
|
||||
|
@ -3400,6 +3523,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
unsigned long long ip, val;
|
||||
char *ptr;
|
||||
void *bptr;
|
||||
int vsize;
|
||||
|
||||
field = pevent->bprint_buf_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;
|
||||
case '0' ... '9':
|
||||
goto process_again;
|
||||
case '.':
|
||||
goto process_again;
|
||||
case 'p':
|
||||
ls = 1;
|
||||
/* fall through */
|
||||
|
@ -3455,23 +3581,30 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|||
case 'u':
|
||||
case 'x':
|
||||
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 */
|
||||
bptr = (void *)(((unsigned long)bptr + 3) &
|
||||
~3);
|
||||
switch (ls) {
|
||||
case 0:
|
||||
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;
|
||||
val = pevent_read_number(pevent, bptr, vsize);
|
||||
bptr += vsize;
|
||||
arg = alloc_arg();
|
||||
arg->next = NULL;
|
||||
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);
|
||||
*next = arg;
|
||||
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;
|
||||
case 's':
|
||||
arg = alloc_arg();
|
||||
arg->next = NULL;
|
||||
arg->type = PRINT_BSTRING;
|
||||
arg->string.string = strdup(bptr);
|
||||
if (!arg->string.string)
|
||||
break;
|
||||
bptr += strlen(bptr) + 1;
|
||||
*next = arg;
|
||||
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]);
|
||||
}
|
||||
|
||||
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,
|
||||
struct event_format *event)
|
||||
{
|
||||
|
@ -3608,7 +3760,8 @@ static void print_event_fields(struct trace_seq *s, void *data, int size,
|
|||
len = offset >> 16;
|
||||
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);
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
trace_seq_putc(s, ']');
|
||||
field->flags &= ~FIELD_IS_STRING;
|
||||
}
|
||||
} else {
|
||||
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') {
|
||||
print_mac_arg(s, *(ptr+1), data, size, event, arg);
|
||||
ptr++;
|
||||
arg = arg->next;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3794,14 +3949,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (pevent->long_size == 8 && ls) {
|
||||
if (pevent->long_size == 8 && ls &&
|
||||
sizeof(long) != 8) {
|
||||
char *p;
|
||||
|
||||
ls = 2;
|
||||
/* make %l into %ll */
|
||||
p = strchr(format, 'l');
|
||||
if (p)
|
||||
memmove(p, p+1, strlen(p)+1);
|
||||
memmove(p+1, p, strlen(p)+1);
|
||||
else if (strcmp(format, "%p") == 0)
|
||||
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: a handle to the pevent
|
||||
* @s: the trace_seq to write to
|
||||
* @data: the raw data to read from
|
||||
* @size: currently unused.
|
||||
* @record: the record to read from
|
||||
*
|
||||
* This parses out the Latency format (interrupts disabled,
|
||||
* 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)
|
||||
{
|
||||
static int check_lock_depth = 1;
|
||||
static int check_migrate_disable = 1;
|
||||
static int lock_depth_exists;
|
||||
static int migrate_disable_exists;
|
||||
unsigned int lat_flags;
|
||||
unsigned int pc;
|
||||
int lock_depth;
|
||||
int migrate_disable;
|
||||
int hardirq;
|
||||
int softirq;
|
||||
void *data = record->data;
|
||||
|
@ -3900,18 +4058,26 @@ void pevent_data_lat_fmt(struct pevent *pevent,
|
|||
lat_flags = parse_common_flags(pevent, data);
|
||||
pc = parse_common_pc(pevent, data);
|
||||
/* 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)
|
||||
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;
|
||||
softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
|
||||
|
@ -3930,6 +4096,13 @@ void pevent_data_lat_fmt(struct pevent *pevent,
|
|||
else
|
||||
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 < 0)
|
||||
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
|
||||
* @s: the trace_seq to write to
|
||||
* @event: the handle to the event
|
||||
* @cpu: the cpu the event was recorded on
|
||||
* @data: the raw data
|
||||
* @size: the size of the raw data
|
||||
* @nsecs: the timestamp of the event
|
||||
* @record: the record to read from
|
||||
*
|
||||
* This parses the raw @data using the given @event information and
|
||||
* writes the print format into the trace_seq.
|
||||
|
@ -4279,6 +4449,13 @@ static void print_args(struct print_arg *args)
|
|||
trace_seq_destroy(&s);
|
||||
printf(")");
|
||||
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_BSTRING:
|
||||
printf("__get_str(%s)", args->string.string);
|
||||
|
@ -4541,6 +4718,8 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
die("failed to read event id");
|
||||
|
||||
event->system = strdup(sys);
|
||||
if (!event->system)
|
||||
die("failed to allocate system");
|
||||
|
||||
/* Add pevent to event so that it can be referenced */
|
||||
event->pevent = pevent;
|
||||
|
@ -4582,6 +4761,11 @@ int pevent_parse_event(struct pevent *pevent,
|
|||
list = &arg->next;
|
||||
arg->type = PRINT_FIELD;
|
||||
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;
|
||||
}
|
||||
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.
|
||||
* @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,
|
||||
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: the handle to the pevent
|
||||
* @func: the function to process the helper function
|
||||
* @ret_type: the return type of the helper function
|
||||
* @name: the name of the helper function
|
||||
* @parameters: A list of enum pevent_func_arg_type
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* @id: the id of the event to register
|
||||
* @sys_name: the system name the event belongs to
|
||||
* @event_name: the name of the event
|
||||
* @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
|
||||
* 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)
|
||||
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->next = pevent->handlers;
|
||||
pevent->handlers = handle;
|
||||
|
|
|
@ -226,6 +226,11 @@ struct print_arg_symbol {
|
|||
struct print_flag_sym *symbols;
|
||||
};
|
||||
|
||||
struct print_arg_hex {
|
||||
struct print_arg *field;
|
||||
struct print_arg *size;
|
||||
};
|
||||
|
||||
struct print_arg_dynarray {
|
||||
struct format_field *field;
|
||||
struct print_arg *index;
|
||||
|
@ -253,6 +258,7 @@ enum print_arg_type {
|
|||
PRINT_FIELD,
|
||||
PRINT_FLAGS,
|
||||
PRINT_SYMBOL,
|
||||
PRINT_HEX,
|
||||
PRINT_TYPE,
|
||||
PRINT_STRING,
|
||||
PRINT_BSTRING,
|
||||
|
@ -270,6 +276,7 @@ struct print_arg {
|
|||
struct print_arg_typecast typecast;
|
||||
struct print_arg_flags flags;
|
||||
struct print_arg_symbol symbol;
|
||||
struct print_arg_hex hex;
|
||||
struct print_arg_func func;
|
||||
struct print_arg_string string;
|
||||
struct print_arg_op op;
|
||||
|
|
|
@ -96,7 +96,7 @@ static enum event_type read_token(char **tok)
|
|||
(strcmp(token, "=") == 0 || strcmp(token, "!") == 0) &&
|
||||
pevent_peek_char() == '~') {
|
||||
/* append it */
|
||||
*tok = malloc(3);
|
||||
*tok = malloc_or_die(3);
|
||||
sprintf(*tok, "%c%c", *token, '~');
|
||||
free_token(token);
|
||||
/* Now remove the '~' from the buffer */
|
||||
|
@ -148,17 +148,11 @@ add_filter_type(struct event_filter *filter, int id)
|
|||
if (filter_type)
|
||||
return filter_type;
|
||||
|
||||
if (!filter->filters)
|
||||
filter->event_filters =
|
||||
malloc_or_die(sizeof(*filter->event_filters));
|
||||
else {
|
||||
filter->event_filters =
|
||||
realloc(filter->event_filters,
|
||||
sizeof(*filter->event_filters) *
|
||||
(filter->filters + 1));
|
||||
if (!filter->event_filters)
|
||||
die("Could not allocate filter");
|
||||
}
|
||||
filter->event_filters = 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++) {
|
||||
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;
|
||||
int count = 0;
|
||||
int *ids;
|
||||
int *ids = NULL;
|
||||
int i;
|
||||
|
||||
if (!filter->filters)
|
||||
|
@ -1504,10 +1498,8 @@ void pevent_filter_clear_trivial(struct event_filter *filter,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (count)
|
||||
ids = realloc(ids, sizeof(*ids) * (count + 1));
|
||||
else
|
||||
ids = malloc(sizeof(*ids));
|
||||
|
||||
ids = realloc(ids, sizeof(*ids) * (count + 1));
|
||||
if (!ids)
|
||||
die("Can't allocate ids");
|
||||
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)
|
||||
{
|
||||
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];
|
||||
|
||||
/*
|
||||
* We need to copy the data since we can't be sure the field
|
||||
* is null terminated.
|
||||
*/
|
||||
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;
|
||||
/* If the field is not a string convert it */
|
||||
if (arg->str.field->flags & FIELD_IS_STRING) {
|
||||
val = record->data + arg->str.field->offset;
|
||||
|
||||
/*
|
||||
* We need to copy the data since we can't be sure the field
|
||||
* is null terminated.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2001,11 +2018,13 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
char *lstr;
|
||||
char *rstr;
|
||||
char *op;
|
||||
char *str;
|
||||
char *str = NULL;
|
||||
int len;
|
||||
|
||||
lstr = arg_to_str(filter, arg->exp.left);
|
||||
rstr = arg_to_str(filter, arg->exp.right);
|
||||
if (!lstr || !rstr)
|
||||
goto out;
|
||||
|
||||
switch (arg->exp.type) {
|
||||
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;
|
||||
str = malloc_or_die(len);
|
||||
snprintf(str, len, "%s %s %s", lstr, op, rstr);
|
||||
out:
|
||||
free(lstr);
|
||||
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);
|
||||
rstr = arg_to_str(filter, arg->num.right);
|
||||
if (!lstr || !rstr)
|
||||
goto out;
|
||||
|
||||
switch (arg->num.type) {
|
||||
case FILTER_CMP_EQ:
|
||||
|
@ -2097,6 +2119,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg)
|
|||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(lstr);
|
||||
free(rstr);
|
||||
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 */
|
||||
str1 = arg_to_str(filter1, filter_type1->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(str2);
|
||||
if (result)
|
||||
|
|
|
@ -12,7 +12,7 @@ SYNOPSIS
|
|||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This 'perf bench' command is general framework for benchmark suites.
|
||||
This 'perf bench' command is a general framework for benchmark suites.
|
||||
|
||||
COMMON OPTIONS
|
||||
--------------
|
||||
|
@ -45,14 +45,20 @@ SUBSYSTEM
|
|||
'sched'::
|
||||
Scheduler and IPC mechanisms.
|
||||
|
||||
'mem'::
|
||||
Memory access performance.
|
||||
|
||||
'all'::
|
||||
All benchmark subsystems.
|
||||
|
||||
SUITES FOR 'sched'
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
*messaging*::
|
||||
Suite for evaluating performance of scheduler and IPC mechanisms.
|
||||
Based on hackbench by Rusty Russell.
|
||||
|
||||
Options of *pipe*
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Options of *messaging*
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
-p::
|
||||
--pipe::
|
||||
Use pipe() instead of socketpair()
|
||||
|
@ -115,6 +121,72 @@ Example of *pipe*
|
|||
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
|
||||
--------
|
||||
linkperf:perf[1]
|
||||
|
|
|
@ -57,7 +57,7 @@ OPTIONS
|
|||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent.
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
|
||||
-p::
|
||||
--parent=<regex>::
|
||||
|
|
|
@ -112,7 +112,7 @@ Default is to monitor all CPUS.
|
|||
|
||||
-s::
|
||||
--sort::
|
||||
Sort by key(s): pid, comm, dso, symbol, parent
|
||||
Sort by key(s): pid, comm, dso, symbol, parent, srcline.
|
||||
|
||||
-n::
|
||||
--show-nr-samples::
|
||||
|
|
|
@ -155,7 +155,7 @@ endif
|
|||
|
||||
### --- 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 =
|
||||
|
||||
# Guard against environment variables
|
||||
|
@ -503,6 +503,7 @@ else
|
|||
LIB_OBJS += $(OUTPUT)ui/progress.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/tui/util.o
|
||||
LIB_H += ui/browser.h
|
||||
LIB_H += ui/browsers/map.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);
|
||||
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
|
||||
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)
|
||||
EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
|
||||
# Make sure that it'd be included only once.
|
||||
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
|
||||
LIB_OBJS += $(OUTPUT)ui/setup.o
|
||||
LIB_OBJS += $(OUTPUT)ui/util.o
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
|
|
@ -24,21 +24,21 @@
|
|||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_clock;
|
||||
static int clock_fd;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('l', "length", &length_str, "1MB",
|
||||
"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",
|
||||
"Specify routine to copy"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memcpy() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "clock", &use_clock,
|
||||
"Use CPU clock for measuring"),
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memcpy()"),
|
||||
OPT_BOOLEAN('n', "no-prefault", &no_prefault,
|
||||
|
@ -76,27 +76,27 @@ static const char * const bench_mem_memcpy_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr clock_attr = {
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.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");
|
||||
else
|
||||
BUG_ON(clock_fd < 0);
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(clock_fd, &clk, sizeof(u64));
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
|
@ -130,14 +130,14 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
|
|||
if (prefault)
|
||||
fn(dst, src, len);
|
||||
|
||||
clock_start = get_clock();
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, src, len);
|
||||
clock_end = get_clock();
|
||||
cycle_end = get_cycle();
|
||||
|
||||
free(src);
|
||||
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)
|
||||
|
@ -182,17 +182,17 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_clock[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memcpy_usage, 0);
|
||||
|
||||
if (use_clock)
|
||||
init_clock();
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
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;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
|
@ -223,11 +223,11 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_clock) {
|
||||
result_clock[0] =
|
||||
do_memcpy_clock(routines[i].fn, len, false);
|
||||
result_clock[1] =
|
||||
do_memcpy_clock(routines[i].fn, len, true);
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memcpy_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memcpy_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memcpy_gettimeofday(routines[i].fn,
|
||||
|
@ -237,9 +237,9 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
result_clock[pf] =
|
||||
do_memcpy_clock(routines[i].fn,
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memcpy_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
|
@ -251,12 +251,12 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte\n",
|
||||
(double)result_clock[0]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Clock/Byte (with prefault)\n",
|
||||
(double)result_clock[1]
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
|
@ -265,9 +265,9 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte",
|
||||
(double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
@ -277,17 +277,17 @@ int bench_mem_memcpy(int argc, const char **argv,
|
|||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_clock[0] / (double)len,
|
||||
(double)result_clock[1] / (double)len);
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf("%lf\n", (double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
|
|
|
@ -24,21 +24,21 @@
|
|||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int iterations = 1;
|
||||
static bool use_clock;
|
||||
static int clock_fd;
|
||||
static bool use_cycle;
|
||||
static int cycle_fd;
|
||||
static bool only_prefault;
|
||||
static bool no_prefault;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('l', "length", &length_str, "1MB",
|
||||
"Specify length of memory to copy. "
|
||||
"available unit: B, MB, GB (upper and lower)"),
|
||||
"Specify length of memory to set. "
|
||||
"Available units: B, KB, MB, GB and TB (upper and lower)"),
|
||||
OPT_STRING('r', "routine", &routine, "default",
|
||||
"Specify routine to copy"),
|
||||
"Specify routine to set"),
|
||||
OPT_INTEGER('i', "iterations", &iterations,
|
||||
"repeat memset() invocation this number of times"),
|
||||
OPT_BOOLEAN('c', "clock", &use_clock,
|
||||
"Use CPU clock for measuring"),
|
||||
OPT_BOOLEAN('c', "cycle", &use_cycle,
|
||||
"Use cycles event instead of gettimeofday() for measuring"),
|
||||
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
|
||||
"Show only the result with page faults before memset()"),
|
||||
OPT_BOOLEAN('n', "no-prefault", &no_prefault,
|
||||
|
@ -76,27 +76,27 @@ static const char * const bench_mem_memset_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static struct perf_event_attr clock_attr = {
|
||||
static struct perf_event_attr cycle_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.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");
|
||||
else
|
||||
BUG_ON(clock_fd < 0);
|
||||
BUG_ON(cycle_fd < 0);
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
static u64 get_cycle(void)
|
||||
{
|
||||
int ret;
|
||||
u64 clk;
|
||||
|
||||
ret = read(clock_fd, &clk, sizeof(u64));
|
||||
ret = read(cycle_fd, &clk, sizeof(u64));
|
||||
BUG_ON(ret != sizeof(u64));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
|
@ -126,13 +126,13 @@ static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)
|
|||
if (prefault)
|
||||
fn(dst, -1, len);
|
||||
|
||||
clock_start = get_clock();
|
||||
cycle_start = get_cycle();
|
||||
for (i = 0; i < iterations; ++i)
|
||||
fn(dst, i, len);
|
||||
clock_end = get_clock();
|
||||
cycle_end = get_cycle();
|
||||
|
||||
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)
|
||||
|
@ -176,17 +176,17 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
int i;
|
||||
size_t len;
|
||||
double result_bps[2];
|
||||
u64 result_clock[2];
|
||||
u64 result_cycle[2];
|
||||
|
||||
argc = parse_options(argc, argv, options,
|
||||
bench_mem_memset_usage, 0);
|
||||
|
||||
if (use_clock)
|
||||
init_clock();
|
||||
if (use_cycle)
|
||||
init_cycle();
|
||||
|
||||
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;
|
||||
|
||||
if ((s64)len <= 0) {
|
||||
|
@ -217,11 +217,11 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
|
||||
if (!only_prefault && !no_prefault) {
|
||||
/* show both of results */
|
||||
if (use_clock) {
|
||||
result_clock[0] =
|
||||
do_memset_clock(routines[i].fn, len, false);
|
||||
result_clock[1] =
|
||||
do_memset_clock(routines[i].fn, len, true);
|
||||
if (use_cycle) {
|
||||
result_cycle[0] =
|
||||
do_memset_cycle(routines[i].fn, len, false);
|
||||
result_cycle[1] =
|
||||
do_memset_cycle(routines[i].fn, len, true);
|
||||
} else {
|
||||
result_bps[0] =
|
||||
do_memset_gettimeofday(routines[i].fn,
|
||||
|
@ -231,9 +231,9 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
len, true);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
result_clock[pf] =
|
||||
do_memset_clock(routines[i].fn,
|
||||
if (use_cycle) {
|
||||
result_cycle[pf] =
|
||||
do_memset_cycle(routines[i].fn,
|
||||
len, only_prefault);
|
||||
} else {
|
||||
result_bps[pf] =
|
||||
|
@ -245,12 +245,12 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte\n",
|
||||
(double)result_clock[0]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte\n",
|
||||
(double)result_cycle[0]
|
||||
/ (double)len);
|
||||
printf(" %14lf Clock/Byte (with prefault)\n ",
|
||||
(double)result_clock[1]
|
||||
printf(" %14lf Cycle/Byte (with prefault)\n ",
|
||||
(double)result_cycle[1]
|
||||
/ (double)len);
|
||||
} else {
|
||||
print_bps(result_bps[0]);
|
||||
|
@ -259,9 +259,9 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
printf(" (with prefault)\n");
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf(" %14lf Clock/Byte",
|
||||
(double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf(" %14lf Cycle/Byte",
|
||||
(double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
print_bps(result_bps[pf]);
|
||||
|
@ -271,17 +271,17 @@ int bench_mem_memset(int argc, const char **argv,
|
|||
break;
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
if (!only_prefault && !no_prefault) {
|
||||
if (use_clock) {
|
||||
if (use_cycle) {
|
||||
printf("%lf %lf\n",
|
||||
(double)result_clock[0] / (double)len,
|
||||
(double)result_clock[1] / (double)len);
|
||||
(double)result_cycle[0] / (double)len,
|
||||
(double)result_cycle[1] / (double)len);
|
||||
} else {
|
||||
printf("%lf %lf\n",
|
||||
result_bps[0], result_bps[1]);
|
||||
}
|
||||
} else {
|
||||
if (use_clock) {
|
||||
printf("%lf\n", (double)result_clock[pf]
|
||||
if (use_cycle) {
|
||||
printf("%lf\n", (double)result_cycle[pf]
|
||||
/ (double)len);
|
||||
} else
|
||||
printf("%lf\n", result_bps[pf]);
|
||||
|
|
|
@ -33,7 +33,7 @@ struct bench_suite {
|
|||
};
|
||||
\
|
||||
/* 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[] = {
|
||||
{ "messaging",
|
||||
|
@ -75,7 +75,7 @@ static struct bench_subsys subsystems[] = {
|
|||
"memory access performance",
|
||||
mem_suites },
|
||||
{ "all", /* sentinel: easy for help */
|
||||
"test all subsystem (pseudo subsystem)",
|
||||
"all benchmark subsystem",
|
||||
NULL },
|
||||
{ NULL,
|
||||
NULL,
|
||||
|
|
|
@ -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) {
|
||||
bool first = true;
|
||||
|
||||
printf("%s", event_name(pos));
|
||||
printf("%s", perf_evsel__name(pos));
|
||||
|
||||
if (details->verbose || details->freq) {
|
||||
comma_printf(&first, " sample_freq=%" PRIu64,
|
||||
|
|
|
@ -57,6 +57,11 @@ static unsigned long nr_allocs, nr_cross_allocs;
|
|||
|
||||
#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)
|
||||
{
|
||||
FILE *fp;
|
||||
|
@ -278,14 +283,16 @@ static void process_free_event(void *data,
|
|||
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)
|
||||
{
|
||||
struct perf_kmem *kmem = container_of(tool, struct perf_kmem, tool);
|
||||
struct event_format *event;
|
||||
int type;
|
||||
|
||||
type = trace_parse_common_type(data);
|
||||
event = trace_find_event(type);
|
||||
type = trace_parse_common_type(kmem->session->pevent, data);
|
||||
event = pevent_find_event(kmem->session->pevent, type);
|
||||
|
||||
if (!strcmp(event->name, "kmalloc") ||
|
||||
!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,
|
||||
struct perf_sample *sample,
|
||||
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);
|
||||
|
||||
process_raw_event(event, sample->raw_data, sample->cpu,
|
||||
process_raw_event(tool, event, sample->raw_data, sample->cpu,
|
||||
sample->time, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool perf_kmem = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
static struct perf_kmem perf_kmem = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &perf_kmem);
|
||||
struct perf_session *session;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false,
|
||||
&perf_kmem.tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
perf_kmem.session = session;
|
||||
|
||||
if (perf_session__create_kernel_maps(session) < 0)
|
||||
goto out_delete;
|
||||
|
||||
|
@ -498,7 +511,7 @@ static int __cmd_kmem(void)
|
|||
goto out_delete;
|
||||
|
||||
setup_pager();
|
||||
err = perf_session__process_events(session, &perf_kmem);
|
||||
err = perf_session__process_events(session, &perf_kmem.tool);
|
||||
if (err != 0)
|
||||
goto out_delete;
|
||||
sort_result();
|
||||
|
|
|
@ -724,8 +724,8 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
|
|||
struct event_format *event;
|
||||
int type;
|
||||
|
||||
type = trace_parse_common_type(data);
|
||||
event = trace_find_event(type);
|
||||
type = trace_parse_common_type(session->pevent, data);
|
||||
event = pevent_find_event(session->pevent, type);
|
||||
|
||||
if (!strcmp(event->name, "lock_acquire"))
|
||||
process_lock_acquire_event(data, event, cpu, timestamp, thread);
|
||||
|
|
|
@ -265,7 +265,7 @@ static void perf_record__open(struct perf_record *rec)
|
|||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
event_name(pos));
|
||||
perf_evsel__name(pos));
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
|||
|
||||
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);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -140,7 +140,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
|||
struct hist_entry *he;
|
||||
|
||||
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);
|
||||
if (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);
|
||||
|
||||
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,
|
||||
event->read.pid, event->read.tid,
|
||||
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,
|
||||
evsel ? event_name(evsel) : "FAIL",
|
||||
evsel ? perf_evsel__name(evsel) : "FAIL",
|
||||
event->read.value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For pipe mode, sample_type is not currently set */
|
||||
static int perf_report__setup_sample_type(struct perf_report *rep)
|
||||
{
|
||||
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) {
|
||||
ui__error("Selected --sort parent, but no "
|
||||
"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 (!(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. "
|
||||
"Did you call perf record without -b?\n");
|
||||
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) {
|
||||
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(hists, NULL, false, true, 0, 0, stdout);
|
||||
|
|
|
@ -43,6 +43,11 @@ static u64 sleep_measurement_overhead;
|
|||
|
||||
static unsigned long nr_tasks;
|
||||
|
||||
struct perf_sched {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
struct sched_atom;
|
||||
|
||||
struct task_desc {
|
||||
|
@ -1597,11 +1602,13 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
|
|||
struct perf_evsel *evsel,
|
||||
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);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %s event, skipping it.\n",
|
||||
evsel->name);
|
||||
perf_evsel__name(evsel));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1612,7 +1619,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
|
|||
tracepoint_handler f = evsel->handler.func;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1620,12 +1628,14 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool perf_sched = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_task,
|
||||
.ordered_samples = true,
|
||||
static struct perf_sched perf_sched = {
|
||||
.tool = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_task,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
};
|
||||
|
||||
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_migrate_task", process_sched_migrate_task_event, },
|
||||
};
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &perf_sched);
|
||||
struct perf_session *session;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false,
|
||||
&perf_sched.tool);
|
||||
if (session == NULL)
|
||||
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);
|
||||
|
||||
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)
|
||||
die("Failed to process events, error %d", err);
|
||||
|
||||
|
|
|
@ -28,6 +28,11 @@ static bool system_wide;
|
|||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
|
||||
struct perf_script {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
enum perf_output_field {
|
||||
PERF_OUTPUT_COMM = 1U << 0,
|
||||
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)
|
||||
|
||||
static int perf_event_attr__check_stype(struct perf_event_attr *attr,
|
||||
u64 sample_type, const char *sample_msg,
|
||||
enum perf_output_field field)
|
||||
static int perf_evsel__check_stype(struct perf_evsel *evsel,
|
||||
u64 sample_type, const char *sample_msg,
|
||||
enum perf_output_field field)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int type = attr->type;
|
||||
const char *evname;
|
||||
|
||||
|
@ -148,7 +154,7 @@ static int perf_event_attr__check_stype(struct perf_event_attr *attr,
|
|||
return 0;
|
||||
|
||||
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. "
|
||||
"Cannot print '%s' field.\n",
|
||||
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 */
|
||||
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. "
|
||||
"Skipping '%s' field.\n",
|
||||
evname, sample_msg, output_field2str(field));
|
||||
|
@ -175,8 +181,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
|||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(IP)) {
|
||||
if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP",
|
||||
PERF_OUTPUT_IP))
|
||||
if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
|
||||
PERF_OUTPUT_IP))
|
||||
return -EINVAL;
|
||||
|
||||
if (!no_callchain &&
|
||||
|
@ -185,8 +191,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
|
|||
}
|
||||
|
||||
if (PRINT_FIELD(ADDR) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR",
|
||||
PERF_OUTPUT_ADDR))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
|
||||
PERF_OUTPUT_ADDR))
|
||||
return -EINVAL;
|
||||
|
||||
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)) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID",
|
||||
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
|
||||
PERF_OUTPUT_TID|PERF_OUTPUT_PID))
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(TIME) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME",
|
||||
PERF_OUTPUT_TIME))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
|
||||
PERF_OUTPUT_TIME))
|
||||
return -EINVAL;
|
||||
|
||||
if (PRINT_FIELD(CPU) &&
|
||||
perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU",
|
||||
PERF_OUTPUT_CPU))
|
||||
perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
|
||||
PERF_OUTPUT_CPU))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
@ -256,11 +262,13 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
|||
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 perf_event_attr *attr)
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int type;
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
struct event_format *event;
|
||||
const char *evname = NULL;
|
||||
unsigned long secs;
|
||||
|
@ -300,12 +308,18 @@ static void print_sample_start(struct perf_sample *sample,
|
|||
|
||||
if (PRINT_FIELD(EVNAME)) {
|
||||
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)
|
||||
evname = event->name;
|
||||
} else
|
||||
evname = __event_name(attr->type, attr->config);
|
||||
evname = perf_evsel__name(evsel);
|
||||
|
||||
printf("%s: ", evname ? evname : "[unknown]");
|
||||
}
|
||||
|
@ -387,7 +401,7 @@ static void print_sample_bts(union perf_event *event,
|
|||
printf(" ");
|
||||
else
|
||||
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(SYMOFFSET));
|
||||
}
|
||||
|
@ -402,6 +416,7 @@ static void print_sample_bts(union perf_event *event,
|
|||
}
|
||||
|
||||
static void process_event(union perf_event *event __unused,
|
||||
struct pevent *pevent,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine,
|
||||
|
@ -412,7 +427,7 @@ static void process_event(union perf_event *event __unused,
|
|||
if (output[attr->type].fields == 0)
|
||||
return;
|
||||
|
||||
print_sample_start(sample, thread, attr);
|
||||
print_sample_start(pevent, sample, thread, evsel);
|
||||
|
||||
if (is_bts_event(attr)) {
|
||||
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))
|
||||
print_trace_event(sample->cpu, sample->raw_data,
|
||||
print_trace_event(pevent, sample->cpu, sample->raw_data,
|
||||
sample->raw_size);
|
||||
|
||||
if (PRINT_FIELD(ADDR))
|
||||
|
@ -431,7 +446,7 @@ static void process_event(union perf_event *event __unused,
|
|||
printf(" ");
|
||||
else
|
||||
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(SYMOFFSET));
|
||||
}
|
||||
|
@ -451,7 +466,8 @@ static int default_stop_script(void)
|
|||
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;
|
||||
}
|
||||
|
@ -489,6 +505,7 @@ static int process_sample_event(struct perf_tool *tool __used,
|
|||
struct machine *machine)
|
||||
{
|
||||
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);
|
||||
|
||||
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))
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_tool perf_script = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
static struct perf_script perf_script = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
@ -553,7 +573,7 @@ static int __cmd_script(struct perf_session *session)
|
|||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
ret = perf_session__process_events(session, &perf_script);
|
||||
ret = perf_session__process_events(session, &perf_script.tool);
|
||||
|
||||
if (debug_mode)
|
||||
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)
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
perf_script.session = session;
|
||||
|
||||
if (cpu_list) {
|
||||
if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))
|
||||
return -1;
|
||||
|
@ -1384,7 +1407,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
|
|||
return -1;
|
||||
}
|
||||
|
||||
err = scripting_ops->generate_script("perf-script");
|
||||
err = scripting_ops->generate_script(session->pevent,
|
||||
"perf-script");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
|
@ -391,7 +391,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
|
|||
|
||||
if (verbose) {
|
||||
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) {
|
||||
if (verbose)
|
||||
ui__warning("%s event is not supported by the kernel.\n",
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
counter->supported = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -594,7 +594,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
|
|||
csv_output ? 0 : -4,
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
|
||||
if (counter->cgrp)
|
||||
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,
|
||||
csv_sep,
|
||||
csv_output ? 0 : -24,
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
|
||||
if (counter->cgrp)
|
||||
fprintf(output, "%s%s",
|
||||
|
|
|
@ -583,7 +583,7 @@ static int test__basic_mmap(void)
|
|||
if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
|
||||
pr_debug("expected %d %s events, got %d\n",
|
||||
expected_nr_events[evsel->idx],
|
||||
event_name(evsel), nr_events[evsel->idx]);
|
||||
perf_evsel__name(evsel), nr_events[evsel->idx]);
|
||||
goto out_munmap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,7 +245,7 @@ static void perf_top__show_details(struct perf_top *top)
|
|||
if (notes->src == NULL)
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -503,13 +503,13 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
|
|||
fprintf(stderr, "\nAvailable events:");
|
||||
|
||||
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");
|
||||
|
||||
if (counter >= top->evlist->nr_entries) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -774,7 +774,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
|||
|
||||
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);
|
||||
if (err)
|
||||
return;
|
||||
|
@ -960,7 +960,7 @@ static void perf_top__start_counters(struct perf_top *top)
|
|||
|
||||
if (err == ENOENT) {
|
||||
ui__error("The %s event is not supported.\n",
|
||||
event_name(counter));
|
||||
perf_evsel__name(counter));
|
||||
goto out_err;
|
||||
} else if (err == EMFILE) {
|
||||
ui__error("Too many events are opened.\n"
|
||||
|
|
|
@ -78,6 +78,19 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
}
|
||||
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
|
||||
|
||||
ifndef NO_LIBPERL
|
||||
|
|
|
@ -814,7 +814,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
{
|
||||
struct disasm_line *pos, *n;
|
||||
struct annotation *notes;
|
||||
const size_t size = symbol__size(sym);
|
||||
size_t size;
|
||||
struct map_symbol ms = {
|
||||
.map = map,
|
||||
.sym = sym,
|
||||
|
@ -834,6 +834,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
|||
if (sym == NULL)
|
||||
return -1;
|
||||
|
||||
size = symbol__size(sym);
|
||||
|
||||
if (map->dso->annotate_warned)
|
||||
return -1;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ struct hist_browser {
|
|||
struct hists *hists;
|
||||
struct hist_entry *he_selection;
|
||||
struct map_symbol *selection;
|
||||
int print_seq;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
continue;
|
||||
goto do_annotate;
|
||||
case 'P':
|
||||
hist_browser__dump(browser);
|
||||
continue;
|
||||
case 'd':
|
||||
goto zoom_dso;
|
||||
case 't':
|
||||
|
@ -969,6 +1163,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
"E Expand all callchains\n"
|
||||
"d Zoom into current DSO\n"
|
||||
"t Zoom into current Thread\n"
|
||||
"P Print histograms to perf.hist.N\n"
|
||||
"/ Filter symbol by name");
|
||||
continue;
|
||||
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);
|
||||
bool current_entry = ui_browser__is_current_entry(browser, row);
|
||||
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;
|
||||
const char *warn = " ";
|
||||
size_t printed;
|
||||
|
@ -1240,7 +1435,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
|
|||
*/
|
||||
if (timer)
|
||||
timer(arg);
|
||||
ev_name = event_name(pos);
|
||||
ev_name = perf_evsel__name(pos);
|
||||
key = perf_evsel__hists_browse(pos, nr_events, help,
|
||||
ev_name, true, timer,
|
||||
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");
|
||||
|
||||
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;
|
||||
|
||||
if (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,
|
||||
|
@ -1330,11 +1519,10 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
|||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
{
|
||||
|
||||
if (evlist->nr_entries == 1) {
|
||||
struct perf_evsel *first = list_entry(evlist->entries.next,
|
||||
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,
|
||||
ev_name, false, timer, arg,
|
||||
delay_secs);
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
static void perf_gtk__signal(int sig)
|
||||
{
|
||||
perf_gtk__exit(false);
|
||||
psignal(sig, "perf");
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#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,
|
||||
const char *help __used,
|
||||
void (*timer) (void *arg)__used,
|
||||
void *arg __used, int delay_secs __used)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *notebook;
|
||||
GtkWidget *info_bar;
|
||||
GtkWidget *statbar;
|
||||
GtkWidget *window;
|
||||
|
||||
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);
|
||||
|
||||
pgctx = perf_gtk__activate_context(window);
|
||||
if (!pgctx)
|
||||
return -1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
|
||||
notebook = gtk_notebook_new();
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct hists *hists = &pos->hists;
|
||||
const char *evname = event_name(pos);
|
||||
const char *evname = perf_evsel__name(pos);
|
||||
GtkWidget *scrolled_window;
|
||||
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_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);
|
||||
|
||||
|
@ -174,5 +235,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
|
|||
|
||||
gtk_main();
|
||||
|
||||
perf_gtk__deactivate_context(&pgctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,39 @@
|
|||
#ifndef _PERF_GTK_H_
|
||||
#define _PERF_GTK_H_ 1
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include <gtk/gtk.h>
|
||||
#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_ */
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
#include "gtk.h"
|
||||
#include "../../util/cache.h"
|
||||
#include "../../util/debug.h"
|
||||
|
||||
extern struct perf_error_ops perf_gtk_eops;
|
||||
|
||||
int perf_gtk__init(void)
|
||||
{
|
||||
perf_error__register(&perf_gtk_eops);
|
||||
return gtk_init_check(NULL, NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
void perf_gtk__exit(bool wait_for_ok __used)
|
||||
{
|
||||
perf_error__unregister(&perf_gtk_eops);
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -15,6 +15,8 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
|
|||
|
||||
static volatile int ui__need_resize;
|
||||
|
||||
extern struct perf_error_ops perf_tui_eops;
|
||||
|
||||
void ui__refresh_dimensions(bool force)
|
||||
{
|
||||
if (force || ui__need_resize) {
|
||||
|
@ -122,6 +124,8 @@ int ui__init(void)
|
|||
signal(SIGINT, ui__signal);
|
||||
signal(SIGQUIT, ui__signal);
|
||||
signal(SIGTERM, ui__signal);
|
||||
|
||||
perf_error__register(&perf_tui_eops);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -137,4 +141,6 @@ void ui__exit(bool wait_for_ok)
|
|||
SLsmg_refresh();
|
||||
SLsmg_reset_smg();
|
||||
SLang_reset_tty();
|
||||
|
||||
perf_error__unregister(&perf_tui_eops);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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 "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;
|
||||
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);
|
||||
fprintf(stderr, "Error:\n");
|
||||
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 key;
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
key = __ui__warning("Warning", format, args);
|
||||
ret = perf_eops->warning(format, 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;
|
||||
va_list args;
|
||||
if (perf_eops != &default_eops)
|
||||
return -1;
|
||||
|
||||
va_start(args, format);
|
||||
key = __ui__warning("Error", format, args);
|
||||
va_end(args);
|
||||
return key;
|
||||
perf_eops = eops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,13 @@ int ui__help_window(const char *text);
|
|||
int ui__dialog_yesno(const char *msg);
|
||||
int ui__question_window(const char *title, const char *text,
|
||||
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_ */
|
||||
|
|
|
@ -47,7 +47,7 @@ int dump_printf(const char *fmt, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
|
||||
int ui__warning(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
|
|
@ -12,8 +12,9 @@ int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
|||
void trace_event(union perf_event *event);
|
||||
|
||||
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)
|
||||
{
|
||||
return 0;
|
||||
|
@ -23,12 +24,28 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used,
|
|||
const char *title __used) {}
|
||||
|
||||
#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[];
|
||||
int ui_helpline__show_help(const char *format, va_list ap);
|
||||
#include "../ui/progress.h"
|
||||
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__error_paranoid(void);
|
||||
|
|
|
@ -224,8 +224,8 @@ int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
|
||||
struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
|||
#define perf_evlist__set_tracepoints_handlers_array(evlist, 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,
|
||||
int cpu, int thread, u64 id);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "cpumap.h"
|
||||
#include "thread_map.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 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",
|
||||
};
|
||||
|
||||
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])
|
||||
return perf_evsel__hw_names[config];
|
||||
|
@ -86,16 +86,15 @@ const char *__perf_evsel__hw_name(u64 config)
|
|||
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;
|
||||
int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(attr->config));
|
||||
bool exclude_guest_default = false;
|
||||
|
||||
#define MOD_PRINT(context, mod) do { \
|
||||
if (!attr->exclude_##context) { \
|
||||
if (!colon) colon = r++; \
|
||||
if (!colon) colon = ++r; \
|
||||
r += scnprintf(bf + r, size - r, "%c", mod); \
|
||||
} } 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 (!colon)
|
||||
colon = r++;
|
||||
colon = ++r;
|
||||
r += scnprintf(bf + r, size - r, "%.*s", attr->precise_ip, "ppp");
|
||||
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
|
||||
if (colon)
|
||||
bf[colon] = ':';
|
||||
bf[colon - 1] = ':';
|
||||
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) {
|
||||
case PERF_TYPE_RAW:
|
||||
ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
|
||||
perf_evsel__raw_name(evsel, bf, sizeof(bf));
|
||||
break;
|
||||
|
||||
case PERF_TYPE_HARDWARE:
|
||||
ret = perf_evsel__hw_name(evsel, bf, size);
|
||||
perf_evsel__hw_name(evsel, bf, sizeof(bf));
|
||||
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:
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* 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;
|
||||
scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
evsel->name = strdup(bf);
|
||||
|
||||
return evsel->name ?: "unknown";
|
||||
}
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
|
||||
|
|
|
@ -83,8 +83,19 @@ void perf_evsel__config(struct perf_evsel *evsel,
|
|||
struct perf_record_opts *opts,
|
||||
struct perf_evsel *first);
|
||||
|
||||
const char* __perf_evsel__hw_name(u64 config);
|
||||
int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size);
|
||||
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
#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_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
|
|
|
@ -641,7 +641,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
|
|||
/*
|
||||
* 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)
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
static int process_build_id(struct perf_file_section *section,
|
||||
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))
|
||||
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);
|
||||
void (*print)(struct perf_header *h, int fd, FILE *fp);
|
||||
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;
|
||||
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,
|
||||
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) {
|
||||
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)
|
||||
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,
|
||||
|
@ -2093,9 +2093,11 @@ static int read_attr(int fd, struct perf_header *ph,
|
|||
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];
|
||||
|
||||
if (event == NULL)
|
||||
|
@ -2109,13 +2111,14 @@ static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel)
|
|||
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;
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (pos->attr.type == PERF_TYPE_TRACEPOINT &&
|
||||
perf_evsel__set_tracepoint_name(pos))
|
||||
perf_evsel__set_tracepoint_name(pos, pevent))
|
||||
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);
|
||||
}
|
||||
|
||||
perf_header__process_sections(header, fd, NULL,
|
||||
perf_header__process_sections(header, fd, &session->pevent,
|
||||
perf_file_section__process);
|
||||
|
||||
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;
|
||||
|
||||
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),
|
||||
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;
|
||||
|
||||
if (read(session->fd, buf, padding) < 0)
|
||||
|
|
|
@ -47,6 +47,7 @@ enum hist_column {
|
|||
HISTC_SYMBOL_TO,
|
||||
HISTC_DSO_FROM,
|
||||
HISTC_DSO_TO,
|
||||
HISTC_SRCLINE,
|
||||
HISTC_NR_COLS, /* Last entry */
|
||||
};
|
||||
|
||||
|
|
|
@ -108,4 +108,14 @@ int eprintf(int level,
|
|||
#define pr_debug3(fmt, ...) pr_debugN(3, 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
|
||||
|
|
|
@ -157,7 +157,7 @@ void machine__exit(struct machine *self);
|
|||
void machine__delete(struct machine *self);
|
||||
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel, struct thread *thread,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
|
||||
|
|
|
@ -181,6 +181,22 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
|
|||
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)
|
||||
{
|
||||
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_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:u"));
|
||||
|
||||
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_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:x:k"));
|
||||
|
||||
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_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:r:hp"));
|
||||
|
||||
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_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:w:up"));
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
|
@ -413,19 +452,63 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist)
|
|||
{
|
||||
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);
|
||||
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 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);
|
||||
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 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;
|
||||
}
|
||||
|
@ -541,10 +624,16 @@ static struct test__event_st test__events[] = {
|
|||
.name = "instructions:H",
|
||||
.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[] = {
|
||||
[0] = {
|
||||
.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) / \
|
||||
sizeof(struct test__event_st))
|
||||
struct test__term {
|
||||
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;
|
||||
int ret;
|
||||
|
@ -590,7 +692,48 @@ static int test_events(struct test__event_st *events, unsigned cnt)
|
|||
struct test__event_st *e = &events[i];
|
||||
|
||||
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)
|
||||
break;
|
||||
}
|
||||
|
@ -617,9 +760,17 @@ int parse_events__test(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = test_events(test__events, TEST__EVENTS_CNT);
|
||||
if (!ret && test_pmu())
|
||||
ret = test_events(test__events_pmu, TEST__EVENTS_PMU_CNT);
|
||||
#define TEST_EVENTS(tests) \
|
||||
do { \
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
#include "cache.h"
|
||||
#include "header.h"
|
||||
#include "debugfs.h"
|
||||
#include "parse-events-bison.h"
|
||||
#define YY_EXTRA_TYPE int
|
||||
#include "parse-events-flex.h"
|
||||
#include "pmu.h"
|
||||
|
||||
#define MAX_NAME_LEN 100
|
||||
|
||||
struct event_symbol {
|
||||
u8 type;
|
||||
u64 config;
|
||||
const char *symbol;
|
||||
const char *alias;
|
||||
};
|
||||
|
@ -26,32 +26,88 @@ struct event_symbol {
|
|||
#ifdef PARSER_DEBUG
|
||||
extern int parse_events_debug;
|
||||
#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
|
||||
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
|
||||
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
[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[] = {
|
||||
{ CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
|
||||
{ CHW(STALLED_CYCLES_FRONTEND), "stalled-cycles-frontend", "idle-cycles-frontend" },
|
||||
{ CHW(STALLED_CYCLES_BACKEND), "stalled-cycles-backend", "idle-cycles-backend" },
|
||||
{ CHW(INSTRUCTIONS), "instructions", "" },
|
||||
{ CHW(CACHE_REFERENCES), "cache-references", "" },
|
||||
{ CHW(CACHE_MISSES), "cache-misses", "" },
|
||||
{ CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
|
||||
{ CHW(BRANCH_MISSES), "branch-misses", "" },
|
||||
{ CHW(BUS_CYCLES), "bus-cycles", "" },
|
||||
{ CHW(REF_CPU_CYCLES), "ref-cycles", "" },
|
||||
|
||||
{ CSW(CPU_CLOCK), "cpu-clock", "" },
|
||||
{ CSW(TASK_CLOCK), "task-clock", "" },
|
||||
{ CSW(PAGE_FAULTS), "page-faults", "faults" },
|
||||
{ CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
|
||||
{ CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
|
||||
{ CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
|
||||
{ CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
|
||||
{ CSW(ALIGNMENT_FAULTS), "alignment-faults", "" },
|
||||
{ CSW(EMULATION_FAULTS), "emulation-faults", "" },
|
||||
static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
||||
[PERF_COUNT_SW_CPU_CLOCK] = {
|
||||
.symbol = "cpu-clock",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_TASK_CLOCK] = {
|
||||
.symbol = "task-clock",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_PAGE_FAULTS] = {
|
||||
.symbol = "page-faults",
|
||||
.alias = "faults",
|
||||
},
|
||||
[PERF_COUNT_SW_CONTEXT_SWITCHES] = {
|
||||
.symbol = "context-switches",
|
||||
.alias = "cs",
|
||||
},
|
||||
[PERF_COUNT_SW_CPU_MIGRATIONS] = {
|
||||
.symbol = "cpu-migrations",
|
||||
.alias = "migrations",
|
||||
},
|
||||
[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) \
|
||||
|
@ -62,63 +118,6 @@ static struct event_symbol event_symbols[] = {
|
|||
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
|
||||
#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) \
|
||||
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
|
||||
if (sys_dirent.d_type == DT_DIR && \
|
||||
|
@ -218,48 +217,6 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
|
|||
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)
|
||||
{
|
||||
switch (type) {
|
||||
|
@ -282,76 +239,6 @@ const char *event_type(int type)
|
|||
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,
|
||||
struct perf_event_attr *attr, char *name)
|
||||
{
|
||||
|
@ -373,19 +260,20 @@ static int add_event(struct list_head **_list, int *idx,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
evsel->name = strdup(name);
|
||||
if (name)
|
||||
evsel->name = strdup(name);
|
||||
list_add_tail(&evsel->node, list);
|
||||
*_list = list;
|
||||
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 n, longest = -1;
|
||||
|
||||
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]);
|
||||
if (n > longest && !strncasecmp(str, names[i][j], 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
|
||||
* then bail out:
|
||||
*/
|
||||
cache_type = parse_aliases(type, hw_cache,
|
||||
cache_type = parse_aliases(type, perf_evsel__hw_cache,
|
||||
PERF_COUNT_HW_CACHE_MAX);
|
||||
if (cache_type == -1)
|
||||
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);
|
||||
|
||||
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);
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache_result == -1) {
|
||||
cache_result = parse_aliases(str, hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
cache_result = parse_aliases(str, perf_evsel__hw_cache_result,
|
||||
PERF_COUNT_HW_CACHE_RESULT_MAX);
|
||||
if (cache_result >= 0)
|
||||
continue;
|
||||
}
|
||||
|
@ -549,21 +437,31 @@ parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
|
|||
if (!type || !type[i])
|
||||
break;
|
||||
|
||||
#define CHECK_SET_TYPE(bit) \
|
||||
do { \
|
||||
if (attr->bp_type & bit) \
|
||||
return -EINVAL; \
|
||||
else \
|
||||
attr->bp_type |= bit; \
|
||||
} while (0)
|
||||
|
||||
switch (type[i]) {
|
||||
case 'r':
|
||||
attr->bp_type |= HW_BREAKPOINT_R;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_R);
|
||||
break;
|
||||
case 'w':
|
||||
attr->bp_type |= HW_BREAKPOINT_W;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_W);
|
||||
break;
|
||||
case 'x':
|
||||
attr->bp_type |= HW_BREAKPOINT_X;
|
||||
CHECK_SET_TYPE(HW_BREAKPOINT_X);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef CHECK_SET_TYPE
|
||||
|
||||
if (!attr->bp_type) /* Default */
|
||||
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)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
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;
|
||||
|
||||
snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
|
||||
return add_event(list, idx, &attr, name);
|
||||
return add_event(list, idx, &attr, NULL);
|
||||
}
|
||||
|
||||
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))
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr,
|
||||
(char *) __event_name(type, config));
|
||||
return add_event(list, idx, &attr, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static char *pmu_event_name(struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
static char *pmu_event_name(struct list_head *head_terms)
|
||||
{
|
||||
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))
|
||||
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,
|
||||
|
@ -699,6 +593,9 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
|
|||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
if (perf_pmu__check_alias(pmu, head_config))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Configure hardcoded terms first, no need to check
|
||||
* 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 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,
|
||||
|
@ -787,27 +684,62 @@ int parse_events_modifier(struct list_head *list, char *str)
|
|||
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;
|
||||
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
|
||||
parse_events_debug = 1;
|
||||
#endif
|
||||
ret = parse_events_parse(&list, &idx);
|
||||
ret = parse_events_parse(data, scanner);
|
||||
|
||||
parse_events__flush_buffer(buffer);
|
||||
parse_events__delete_buffer(buffer);
|
||||
parse_events_lex_destroy();
|
||||
parse_events__flush_buffer(buffer, scanner);
|
||||
parse_events__delete_buffer(buffer, scanner);
|
||||
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) {
|
||||
int entries = idx - evlist->nr_entries;
|
||||
perf_evlist__splice_list_tail(evlist, &list, entries);
|
||||
list_splice(data.terms, terms);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -946,16 +878,13 @@ int is_valid_tracepoint(const char *event_string)
|
|||
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];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) {
|
||||
if (type != syms->type)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < max ; i++, syms++) {
|
||||
if (strlen(syms->alias))
|
||||
snprintf(name, sizeof(name), "%s OR %s",
|
||||
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)
|
||||
{
|
||||
unsigned int type, op, i, printed = 0;
|
||||
char name[64];
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
/* skip invalid cache type */
|
||||
if (!is_cache_op_valid(type, op))
|
||||
if (!perf_evsel__is_cache_op_valid(type, op))
|
||||
continue;
|
||||
|
||||
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))
|
||||
continue;
|
||||
|
||||
|
@ -993,26 +931,13 @@ int print_hwcache_events(const char *event_glob)
|
|||
return printed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the help text for the event symbols:
|
||||
*/
|
||||
void print_events(const char *event_glob)
|
||||
static void print_symbol_events(const char *event_glob, unsigned type,
|
||||
struct event_symbol *syms, unsigned max)
|
||||
{
|
||||
unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0;
|
||||
struct event_symbol *syms = event_symbols;
|
||||
unsigned i, printed = 0;
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
printf("\n");
|
||||
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++;
|
||||
}
|
||||
for (i = 0; i < max; i++, syms++) {
|
||||
|
||||
if (event_glob != NULL &&
|
||||
!(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);
|
||||
else
|
||||
strncpy(name, syms->symbol, MAX_NAME_LEN);
|
||||
printf(" %-50s [%s]\n", name,
|
||||
event_type_descriptors[type]);
|
||||
|
||||
prev_type = type;
|
||||
++printed;
|
||||
printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
|
||||
|
||||
printed++;
|
||||
}
|
||||
|
||||
if (ntypes_printed) {
|
||||
printed = 0;
|
||||
if (printed)
|
||||
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);
|
||||
|
||||
if (event_glob != NULL)
|
||||
|
@ -1106,6 +1045,13 @@ int parse_events__term_str(struct parse_events__term **term,
|
|||
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)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
|
|
|
@ -26,13 +26,12 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
|
|||
extern bool have_tracepoints(struct list_head *evlist);
|
||||
|
||||
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,
|
||||
int unset);
|
||||
extern int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
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);
|
||||
|
||||
#define EVENTS_HELP_MAX (128*1024)
|
||||
|
@ -63,11 +62,22 @@ struct parse_events__term {
|
|||
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__term_num(struct parse_events__term **_term,
|
||||
int type_term, char *config, long num);
|
||||
int parse_events__term_str(struct parse_events__term **_term,
|
||||
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);
|
||||
int parse_events_modifier(struct list_head *list, char *str);
|
||||
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);
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all);
|
||||
void parse_events_error(struct list_head *list_all,
|
||||
int *idx, char const *msg);
|
||||
void parse_events_error(void *data, void *scanner, char const *msg);
|
||||
int parse_events__test(void);
|
||||
|
||||
void print_events(const char *event_glob);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
%option reentrant
|
||||
%option bison-bridge
|
||||
%option prefix="parse_events_"
|
||||
%option stack
|
||||
|
||||
|
@ -8,7 +10,10 @@
|
|||
#include "parse-events-bison.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;
|
||||
|
||||
|
@ -17,35 +22,48 @@ static int __value(char *str, int base, int token)
|
|||
if (errno)
|
||||
return PE_ERROR;
|
||||
|
||||
parse_events_lval.num = num;
|
||||
yylval->num = num;
|
||||
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;
|
||||
}
|
||||
|
||||
static int sym(int type, int config)
|
||||
static int sym(yyscan_t scanner, int type, int config)
|
||||
{
|
||||
parse_events_lval.num = (type << 16) + config;
|
||||
return PE_VALUE_SYM;
|
||||
YYSTYPE *yylval = parse_events_get_lval(scanner);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -58,28 +76,41 @@ num_hex 0x[a-fA-F0-9]+
|
|||
num_raw_hex [a-fA-F0-9]+
|
||||
name [a-zA-Z_*?][a-zA-Z0-9_*?]*
|
||||
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); }
|
||||
cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
|
||||
cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
|
||||
branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
|
||||
branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
|
||||
bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
|
||||
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); }
|
||||
major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
|
||||
context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
|
||||
cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
|
||||
alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
|
||||
emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
|
||||
|
||||
%{
|
||||
{
|
||||
int start_token;
|
||||
|
||||
start_token = (int) parse_events_get_extra(yyscanner);
|
||||
if (start_token) {
|
||||
parse_events_set_extra(NULL, yyscanner);
|
||||
return start_token;
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
||||
cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
|
||||
stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
|
||||
stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
|
||||
instructions { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
|
||||
cache-references { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
|
||||
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-icache|l1-i|l1i|L1-instruction |
|
||||
|
@ -87,14 +118,14 @@ LLC|L2 |
|
|||
dTLB|d-tlb|Data-TLB |
|
||||
iTLB|i-tlb|Instruction-TLB |
|
||||
branch|branches|bpu|btb|bpc |
|
||||
node { return str(PE_NAME_CACHE_TYPE); }
|
||||
node { return str(yyscanner, PE_NAME_CACHE_TYPE); }
|
||||
|
||||
load|loads|read |
|
||||
store|stores|write |
|
||||
prefetch|prefetches |
|
||||
speculative-read|speculative-load |
|
||||
refs|Reference|ops|access |
|
||||
misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
|
||||
misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
|
||||
|
||||
/*
|
||||
* 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
|
||||
* in future, this needs to go into '//' condition block.
|
||||
*/
|
||||
config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
|
||||
config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
name { return term(PARSE_EVENTS__TERM_TYPE_NAME); }
|
||||
period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
|
||||
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
|
||||
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
|
||||
mem: { BEGIN(mem); return PE_PREFIX_MEM; }
|
||||
r{num_raw_hex} { return raw(); }
|
||||
{num_dec} { return value(10); }
|
||||
{num_hex} { return value(16); }
|
||||
r{num_raw_hex} { return raw(yyscanner); }
|
||||
{num_dec} { return value(yyscanner, 10); }
|
||||
{num_hex} { return value(yyscanner, 16); }
|
||||
|
||||
{modifier_event} { return str(PE_MODIFIER_EVENT); }
|
||||
{name} { return str(PE_NAME); }
|
||||
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
|
||||
{name} { return str(yyscanner, PE_NAME); }
|
||||
"/" { return '/'; }
|
||||
- { return '-'; }
|
||||
, { return ','; }
|
||||
: { return ':'; }
|
||||
= { return '='; }
|
||||
\n { }
|
||||
|
||||
<mem>{
|
||||
{modifier_bp} { return str(PE_MODIFIER_BP); }
|
||||
{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
|
||||
: { return ':'; }
|
||||
{num_dec} { return value(10); }
|
||||
{num_hex} { return value(16); }
|
||||
{num_dec} { return value(yyscanner, 10); }
|
||||
{num_hex} { return value(yyscanner, 16); }
|
||||
/*
|
||||
* We need to separate 'mem:' scanner part, in order to get specific
|
||||
* 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>
|
||||
* 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,
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
%pure-parser
|
||||
%name-prefix "parse_events_"
|
||||
%parse-param {struct list_head *list_all}
|
||||
%parse-param {int *idx}
|
||||
%parse-param {void *_data}
|
||||
%parse-param {void *scanner}
|
||||
%lex-param {void* scanner}
|
||||
|
||||
%{
|
||||
|
||||
|
@ -12,8 +13,9 @@
|
|||
#include "types.h"
|
||||
#include "util.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) \
|
||||
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_MODIFIER_EVENT PE_MODIFIER_BP
|
||||
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
|
||||
%token PE_PREFIX_MEM PE_PREFIX_RAW
|
||||
%token PE_ERROR
|
||||
%type <num> PE_VALUE
|
||||
%type <num> PE_VALUE_SYM
|
||||
%type <num> PE_VALUE_SYM_HW
|
||||
%type <num> PE_VALUE_SYM_SW
|
||||
%type <num> PE_RAW
|
||||
%type <num> PE_TERM
|
||||
%type <str> PE_NAME
|
||||
|
@ -38,6 +42,7 @@ do { \
|
|||
%type <str> PE_NAME_CACHE_OP_RESULT
|
||||
%type <str> PE_MODIFIER_EVENT
|
||||
%type <str> PE_MODIFIER_BP
|
||||
%type <num> value_sym
|
||||
%type <head> event_config
|
||||
%type <term> event_term
|
||||
%type <head> event_pmu
|
||||
|
@ -58,24 +63,33 @@ do { \
|
|||
}
|
||||
%%
|
||||
|
||||
start:
|
||||
PE_START_EVENTS events
|
||||
|
|
||||
PE_START_TERMS terms
|
||||
|
||||
events:
|
||||
events ',' event | event
|
||||
|
||||
event:
|
||||
event_def PE_MODIFIER_EVENT
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
|
||||
/*
|
||||
* Apply modifier on all events added by single event definition
|
||||
* (there could be more events added for multiple tracepoint
|
||||
* definitions via '*?'.
|
||||
*/
|
||||
ABORT_ON(parse_events_modifier($1, $2));
|
||||
parse_events_update_lists($1, list_all);
|
||||
parse_events_update_lists($1, &data->list);
|
||||
}
|
||||
|
|
||||
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 |
|
||||
|
@ -89,104 +103,131 @@ event_def: event_pmu |
|
|||
event_pmu:
|
||||
PE_NAME '/' event_config '/'
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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);
|
||||
$$ = list;
|
||||
}
|
||||
|
||||
value_sym:
|
||||
PE_VALUE_SYM_HW
|
||||
|
|
||||
PE_VALUE_SYM_SW
|
||||
|
||||
event_legacy_symbol:
|
||||
PE_VALUE_SYM '/' event_config '/'
|
||||
value_sym '/' event_config '/'
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
int type = $1 >> 16;
|
||||
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);
|
||||
$$ = list;
|
||||
}
|
||||
|
|
||||
PE_VALUE_SYM sep_slash_dc
|
||||
value_sym sep_slash_dc
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
int type = $1 >> 16;
|
||||
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;
|
||||
}
|
||||
|
||||
event_legacy_cache:
|
||||
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;
|
||||
|
||||
ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, $5));
|
||||
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5));
|
||||
$$ = list;
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
||||
event_legacy_mem:
|
||||
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
|
||||
PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
||||
event_legacy_tracepoint:
|
||||
PE_NAME ':' PE_NAME
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
||||
event_legacy_numeric:
|
||||
PE_VALUE ':' PE_VALUE
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
||||
event_legacy_raw:
|
||||
PE_RAW
|
||||
{
|
||||
struct parse_events_data__events *data = _data;
|
||||
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;
|
||||
}
|
||||
|
||||
terms: event_config
|
||||
{
|
||||
struct parse_events_data__terms *data = _data;
|
||||
data->terms = $1;
|
||||
}
|
||||
|
||||
event_config:
|
||||
event_config ',' event_term
|
||||
{
|
||||
|
@ -267,8 +308,7 @@ sep_slash_dc: '/' | ':' |
|
|||
|
||||
%%
|
||||
|
||||
void parse_events_error(struct list_head *list_all __used,
|
||||
int *idx __used,
|
||||
void parse_events_error(void *data __used, void *scanner __used,
|
||||
char const *msg __used)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format)
|
|||
"%s/bus/event_source/devices/%s/format", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
return 0; /* no error if format does not exist */
|
||||
|
||||
if (pmu_format_parse(path, format))
|
||||
return -1;
|
||||
|
@ -80,6 +80,114 @@ static int pmu_format(char *name, struct list_head *format)
|
|||
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
|
||||
* located at:
|
||||
|
@ -118,6 +226,7 @@ static struct perf_pmu *pmu_lookup(char *name)
|
|||
{
|
||||
struct perf_pmu *pmu;
|
||||
LIST_HEAD(format);
|
||||
LIST_HEAD(aliases);
|
||||
__u32 type;
|
||||
|
||||
/*
|
||||
|
@ -135,10 +244,15 @@ static struct perf_pmu *pmu_lookup(char *name)
|
|||
if (!pmu)
|
||||
return NULL;
|
||||
|
||||
pmu_aliases(name, &aliases);
|
||||
|
||||
INIT_LIST_HEAD(&pmu->format);
|
||||
INIT_LIST_HEAD(&pmu->aliases);
|
||||
list_splice(&format, &pmu->format);
|
||||
list_splice(&aliases, &pmu->aliases);
|
||||
pmu->name = strdup(name);
|
||||
pmu->type = type;
|
||||
list_add_tail(&pmu->list, &pmus);
|
||||
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);
|
||||
}
|
||||
|
||||
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 config, unsigned long *bits)
|
||||
{
|
||||
|
|
|
@ -19,17 +19,26 @@ struct perf_pmu__format {
|
|||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu__alias {
|
||||
char *name;
|
||||
struct list_head terms;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu {
|
||||
char *name;
|
||||
__u32 type;
|
||||
struct list_head format;
|
||||
struct list_head aliases;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
|
||||
int perf_pmu__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);
|
||||
void perf_pmu_error(struct list_head *list, char *name, char const *msg);
|
||||
|
||||
|
|
|
@ -209,6 +209,10 @@ static void define_event_symbols(struct event_format *event,
|
|||
define_symbolic_values(args->symbol.symbols, ev_name,
|
||||
cur_field_name);
|
||||
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_DYNAMIC_ARRAY:
|
||||
case PRINT_STRING:
|
||||
|
@ -233,7 +237,8 @@ static void define_event_symbols(struct event_format *event,
|
|||
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];
|
||||
struct event_format *event;
|
||||
|
@ -241,7 +246,7 @@ static inline struct event_format *find_cache_event(int type)
|
|||
if (events[type])
|
||||
return events[type];
|
||||
|
||||
events[type] = event = trace_find_event(type);
|
||||
events[type] = event = pevent_find_event(pevent, type);
|
||||
if (!event)
|
||||
return NULL;
|
||||
|
||||
|
@ -252,7 +257,8 @@ static inline struct event_format *find_cache_event(int type)
|
|||
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_evsel *evsel,
|
||||
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)
|
||||
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)
|
||||
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);
|
||||
|
||||
|
@ -314,7 +320,8 @@ static void perl_process_tracepoint(union perf_event *pevent __unused,
|
|||
offset = field->offset;
|
||||
XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
|
||||
} 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) {
|
||||
XPUSHs(sv_2mortal(newSViv(val)));
|
||||
} else {
|
||||
|
@ -368,14 +375,15 @@ static void perl_process_event_generic(union perf_event *pevent __unused,
|
|||
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_evsel *evsel,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
perl_process_tracepoint(pevent, sample, evsel, machine, thread);
|
||||
perl_process_event_generic(pevent, sample, evsel, machine, thread);
|
||||
perl_process_tracepoint(event, pevent, sample, evsel, machine, thread);
|
||||
perl_process_event_generic(event, sample, evsel, machine, thread);
|
||||
}
|
||||
|
||||
static void run_start_sub(void)
|
||||
|
@ -448,7 +456,7 @@ static int perl_stop_script(void)
|
|||
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 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_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, "\tmy (");
|
||||
|
||||
|
|
|
@ -166,6 +166,10 @@ static void define_event_symbols(struct event_format *event,
|
|||
define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name,
|
||||
cur_field_name);
|
||||
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:
|
||||
break;
|
||||
case PRINT_TYPE:
|
||||
|
@ -190,7 +194,8 @@ static void define_event_symbols(struct event_format *event,
|
|||
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];
|
||||
struct event_format *event;
|
||||
|
@ -198,7 +203,7 @@ static inline struct event_format *find_cache_event(int type)
|
|||
if (events[type])
|
||||
return events[type];
|
||||
|
||||
events[type] = event = trace_find_event(type);
|
||||
events[type] = event = pevent_find_event(pevent, type);
|
||||
if (!event)
|
||||
return NULL;
|
||||
|
||||
|
@ -209,7 +214,8 @@ static inline struct event_format *find_cache_event(int type)
|
|||
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_evsel *evsel __unused,
|
||||
struct machine *machine __unused,
|
||||
|
@ -233,13 +239,13 @@ static void python_process_event(union perf_event *pevent __unused,
|
|||
if (!t)
|
||||
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)
|
||||
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);
|
||||
|
||||
|
@ -284,7 +290,8 @@ static void python_process_event(union perf_event *pevent __unused,
|
|||
offset = field->offset;
|
||||
obj = PyString_FromString((char *)data + offset);
|
||||
} 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 ((long long)val >= LONG_MIN &&
|
||||
(long long)val <= LONG_MAX)
|
||||
|
@ -438,7 +445,7 @@ static int python_stop_script(void)
|
|||
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 format_field *f;
|
||||
|
@ -487,7 +494,7 @@ static int python_generate_script(const char *outfile)
|
|||
fprintf(ofp, "def trace_end():\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, "event_name, ");
|
||||
fprintf(ofp, "context, ");
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "sort.h"
|
||||
#include "util.h"
|
||||
#include "cpumap.h"
|
||||
#include "event-parse.h"
|
||||
|
||||
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,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso, int print_symoffset)
|
||||
struct machine *machine, int print_sym,
|
||||
int print_dso, int print_symoffset)
|
||||
{
|
||||
struct addr_location al;
|
||||
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 (machine__resolve_callchain(machine, evsel, al.thread,
|
||||
if (machine__resolve_callchain(machine, al.thread,
|
||||
sample->callchain, NULL) != 0) {
|
||||
if (verbose)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ struct perf_session {
|
|||
struct machine host_machine;
|
||||
struct rb_root machines;
|
||||
struct perf_evlist *evlist;
|
||||
struct pevent *pevent;
|
||||
/*
|
||||
* FIXME: Need to split this up further, we need global
|
||||
* 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);
|
||||
|
||||
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso, int print_symoffset);
|
||||
struct machine *machine, int print_sym,
|
||||
int print_dso, int print_symoffset);
|
||||
|
||||
int perf_session__cpu_bitmap(struct perf_session *session,
|
||||
const char *cpu_list, unsigned long *cpu_bitmap);
|
||||
|
||||
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 */
|
||||
|
|
|
@ -241,6 +241,54 @@ struct sort_entry sort_sym = {
|
|||
.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 */
|
||||
|
||||
static int64_t
|
||||
|
@ -439,6 +487,7 @@ static struct sort_dimension sort_dimensions[] = {
|
|||
DIM(SORT_PARENT, "parent", sort_parent),
|
||||
DIM(SORT_CPU, "cpu", sort_cpu),
|
||||
DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
|
||||
DIM(SORT_SRCLINE, "srcline", sort_srcline),
|
||||
};
|
||||
|
||||
int sort_dimension__add(const char *tok)
|
||||
|
|
|
@ -71,6 +71,7 @@ struct hist_entry {
|
|||
char level;
|
||||
bool used;
|
||||
u8 filtered;
|
||||
char *srcline;
|
||||
struct symbol *parent;
|
||||
union {
|
||||
unsigned long position;
|
||||
|
@ -93,6 +94,7 @@ enum sort_type {
|
|||
SORT_SYM_FROM,
|
||||
SORT_SYM_TO,
|
||||
SORT_MISPREDICT,
|
||||
SORT_SRCLINE,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue