mirror of
https://github.com/t2linux/kernel.git
synced 2026-04-30 13:48:59 -07:00
Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (50 commits) perf python scripting: Add futex-contention script perf python scripting: Fixup cut'n'paste error in sctop script perf scripting: Shut up 'perf record' final status perf record: Remove newline character from perror() argument perf python scripting: Support fedora 11 (audit 1.7.17) perf python scripting: Improve the syscalls-by-pid script perf python scripting: print the syscall name on sctop perf python scripting: Improve the syscalls-counts script perf python scripting: Improve the failed-syscalls-by-pid script kprobes: Remove redundant text_mutex lock in optimize x86/oprofile: Fix uninitialized variable use in debug printk tracing: Fix 'faild' -> 'failed' typo perf probe: Fix format specified for Dwarf_Off parameter perf trace: Fix detection of script extension perf trace: Use $PERF_EXEC_PATH in canned report scripts perf tools: Document event modifiers perf tools: Remove direct slang.h include perf_events: Fix for transaction recovery in group_sched_in() perf_events: Revert: Fix transaction recovery in group_sched_in() perf, x86: Use NUMA aware allocations for PEBS/BTS/DS allocations ...
This commit is contained in:
@@ -121,6 +121,7 @@
|
||||
#define MSR_AMD64_IBSDCLINAD 0xc0011038
|
||||
#define MSR_AMD64_IBSDCPHYSAD 0xc0011039
|
||||
#define MSR_AMD64_IBSCTL 0xc001103a
|
||||
#define MSR_AMD64_IBSBRTARGET 0xc001103b
|
||||
|
||||
/* Fam 10h MSRs */
|
||||
#define MSR_FAM10H_MMIO_CONF_BASE 0xc0010058
|
||||
|
||||
@@ -111,17 +111,18 @@ union cpuid10_edx {
|
||||
#define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16)
|
||||
|
||||
/* IbsFetchCtl bits/masks */
|
||||
#define IBS_FETCH_RAND_EN (1ULL<<57)
|
||||
#define IBS_FETCH_VAL (1ULL<<49)
|
||||
#define IBS_FETCH_ENABLE (1ULL<<48)
|
||||
#define IBS_FETCH_CNT 0xFFFF0000ULL
|
||||
#define IBS_FETCH_MAX_CNT 0x0000FFFFULL
|
||||
#define IBS_FETCH_RAND_EN (1ULL<<57)
|
||||
#define IBS_FETCH_VAL (1ULL<<49)
|
||||
#define IBS_FETCH_ENABLE (1ULL<<48)
|
||||
#define IBS_FETCH_CNT 0xFFFF0000ULL
|
||||
#define IBS_FETCH_MAX_CNT 0x0000FFFFULL
|
||||
|
||||
/* IbsOpCtl bits */
|
||||
#define IBS_OP_CNT_CTL (1ULL<<19)
|
||||
#define IBS_OP_VAL (1ULL<<18)
|
||||
#define IBS_OP_ENABLE (1ULL<<17)
|
||||
#define IBS_OP_MAX_CNT 0x0000FFFFULL
|
||||
#define IBS_OP_CNT_CTL (1ULL<<19)
|
||||
#define IBS_OP_VAL (1ULL<<18)
|
||||
#define IBS_OP_ENABLE (1ULL<<17)
|
||||
#define IBS_OP_MAX_CNT 0x0000FFFFULL
|
||||
#define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
extern void init_hw_perf_events(void);
|
||||
|
||||
@@ -237,6 +237,7 @@ struct x86_pmu {
|
||||
* Intel DebugStore bits
|
||||
*/
|
||||
int bts, pebs;
|
||||
int bts_active, pebs_active;
|
||||
int pebs_record_size;
|
||||
void (*drain_pebs)(struct pt_regs *regs);
|
||||
struct event_constraint *pebs_constraints;
|
||||
@@ -380,7 +381,7 @@ static void release_pmc_hardware(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
static int reserve_ds_buffers(void);
|
||||
static void reserve_ds_buffers(void);
|
||||
static void release_ds_buffers(void);
|
||||
|
||||
static void hw_perf_event_destroy(struct perf_event *event)
|
||||
@@ -477,7 +478,7 @@ static int x86_setup_perfctr(struct perf_event *event)
|
||||
if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
|
||||
(hwc->sample_period == 1)) {
|
||||
/* BTS is not supported by this architecture. */
|
||||
if (!x86_pmu.bts)
|
||||
if (!x86_pmu.bts_active)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* BTS is currently only allowed for user-mode. */
|
||||
@@ -496,12 +497,13 @@ static int x86_pmu_hw_config(struct perf_event *event)
|
||||
int precise = 0;
|
||||
|
||||
/* Support for constant skid */
|
||||
if (x86_pmu.pebs)
|
||||
if (x86_pmu.pebs_active) {
|
||||
precise++;
|
||||
|
||||
/* Support for IP fixup */
|
||||
if (x86_pmu.lbr_nr)
|
||||
precise++;
|
||||
/* Support for IP fixup */
|
||||
if (x86_pmu.lbr_nr)
|
||||
precise++;
|
||||
}
|
||||
|
||||
if (event->attr.precise_ip > precise)
|
||||
return -EOPNOTSUPP;
|
||||
@@ -543,11 +545,8 @@ static int __x86_pmu_event_init(struct perf_event *event)
|
||||
if (atomic_read(&active_events) == 0) {
|
||||
if (!reserve_pmc_hardware())
|
||||
err = -EBUSY;
|
||||
else {
|
||||
err = reserve_ds_buffers();
|
||||
if (err)
|
||||
release_pmc_hardware();
|
||||
}
|
||||
else
|
||||
reserve_ds_buffers();
|
||||
}
|
||||
if (!err)
|
||||
atomic_inc(&active_events);
|
||||
|
||||
@@ -74,6 +74,107 @@ static void fini_debug_store_on_cpu(int cpu)
|
||||
wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0);
|
||||
}
|
||||
|
||||
static int alloc_pebs_buffer(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
int node = cpu_to_node(cpu);
|
||||
int max, thresh = 1; /* always use a single PEBS record */
|
||||
void *buffer;
|
||||
|
||||
if (!x86_pmu.pebs)
|
||||
return 0;
|
||||
|
||||
buffer = kmalloc_node(PEBS_BUFFER_SIZE, GFP_KERNEL | __GFP_ZERO, node);
|
||||
if (unlikely(!buffer))
|
||||
return -ENOMEM;
|
||||
|
||||
max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size;
|
||||
|
||||
ds->pebs_buffer_base = (u64)(unsigned long)buffer;
|
||||
ds->pebs_index = ds->pebs_buffer_base;
|
||||
ds->pebs_absolute_maximum = ds->pebs_buffer_base +
|
||||
max * x86_pmu.pebs_record_size;
|
||||
|
||||
ds->pebs_interrupt_threshold = ds->pebs_buffer_base +
|
||||
thresh * x86_pmu.pebs_record_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_pebs_buffer(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
|
||||
if (!ds || !x86_pmu.pebs)
|
||||
return;
|
||||
|
||||
kfree((void *)(unsigned long)ds->pebs_buffer_base);
|
||||
ds->pebs_buffer_base = 0;
|
||||
}
|
||||
|
||||
static int alloc_bts_buffer(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
int node = cpu_to_node(cpu);
|
||||
int max, thresh;
|
||||
void *buffer;
|
||||
|
||||
if (!x86_pmu.bts)
|
||||
return 0;
|
||||
|
||||
buffer = kmalloc_node(BTS_BUFFER_SIZE, GFP_KERNEL | __GFP_ZERO, node);
|
||||
if (unlikely(!buffer))
|
||||
return -ENOMEM;
|
||||
|
||||
max = BTS_BUFFER_SIZE / BTS_RECORD_SIZE;
|
||||
thresh = max / 16;
|
||||
|
||||
ds->bts_buffer_base = (u64)(unsigned long)buffer;
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
ds->bts_absolute_maximum = ds->bts_buffer_base +
|
||||
max * BTS_RECORD_SIZE;
|
||||
ds->bts_interrupt_threshold = ds->bts_absolute_maximum -
|
||||
thresh * BTS_RECORD_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_bts_buffer(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
|
||||
if (!ds || !x86_pmu.bts)
|
||||
return;
|
||||
|
||||
kfree((void *)(unsigned long)ds->bts_buffer_base);
|
||||
ds->bts_buffer_base = 0;
|
||||
}
|
||||
|
||||
static int alloc_ds_buffer(int cpu)
|
||||
{
|
||||
int node = cpu_to_node(cpu);
|
||||
struct debug_store *ds;
|
||||
|
||||
ds = kmalloc_node(sizeof(*ds), GFP_KERNEL | __GFP_ZERO, node);
|
||||
if (unlikely(!ds))
|
||||
return -ENOMEM;
|
||||
|
||||
per_cpu(cpu_hw_events, cpu).ds = ds;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_ds_buffer(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
per_cpu(cpu_hw_events, cpu).ds = NULL;
|
||||
kfree(ds);
|
||||
}
|
||||
|
||||
static void release_ds_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
@@ -82,93 +183,77 @@ static void release_ds_buffers(void)
|
||||
return;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
fini_debug_store_on_cpu(cpu);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
|
||||
if (!ds)
|
||||
continue;
|
||||
|
||||
per_cpu(cpu_hw_events, cpu).ds = NULL;
|
||||
|
||||
kfree((void *)(unsigned long)ds->pebs_buffer_base);
|
||||
kfree((void *)(unsigned long)ds->bts_buffer_base);
|
||||
kfree(ds);
|
||||
release_pebs_buffer(cpu);
|
||||
release_bts_buffer(cpu);
|
||||
release_ds_buffer(cpu);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int reserve_ds_buffers(void)
|
||||
static void reserve_ds_buffers(void)
|
||||
{
|
||||
int cpu, err = 0;
|
||||
int bts_err = 0, pebs_err = 0;
|
||||
int cpu;
|
||||
|
||||
x86_pmu.bts_active = 0;
|
||||
x86_pmu.pebs_active = 0;
|
||||
|
||||
if (!x86_pmu.bts && !x86_pmu.pebs)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (!x86_pmu.bts)
|
||||
bts_err = 1;
|
||||
|
||||
if (!x86_pmu.pebs)
|
||||
pebs_err = 1;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct debug_store *ds;
|
||||
void *buffer;
|
||||
int max, thresh;
|
||||
if (alloc_ds_buffer(cpu)) {
|
||||
bts_err = 1;
|
||||
pebs_err = 1;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
ds = kzalloc(sizeof(*ds), GFP_KERNEL);
|
||||
if (unlikely(!ds))
|
||||
if (!bts_err && alloc_bts_buffer(cpu))
|
||||
bts_err = 1;
|
||||
|
||||
if (!pebs_err && alloc_pebs_buffer(cpu))
|
||||
pebs_err = 1;
|
||||
|
||||
if (bts_err && pebs_err)
|
||||
break;
|
||||
per_cpu(cpu_hw_events, cpu).ds = ds;
|
||||
|
||||
if (x86_pmu.bts) {
|
||||
buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (unlikely(!buffer))
|
||||
break;
|
||||
|
||||
max = BTS_BUFFER_SIZE / BTS_RECORD_SIZE;
|
||||
thresh = max / 16;
|
||||
|
||||
ds->bts_buffer_base = (u64)(unsigned long)buffer;
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
ds->bts_absolute_maximum = ds->bts_buffer_base +
|
||||
max * BTS_RECORD_SIZE;
|
||||
ds->bts_interrupt_threshold = ds->bts_absolute_maximum -
|
||||
thresh * BTS_RECORD_SIZE;
|
||||
}
|
||||
|
||||
if (x86_pmu.pebs) {
|
||||
buffer = kzalloc(PEBS_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (unlikely(!buffer))
|
||||
break;
|
||||
|
||||
max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size;
|
||||
|
||||
ds->pebs_buffer_base = (u64)(unsigned long)buffer;
|
||||
ds->pebs_index = ds->pebs_buffer_base;
|
||||
ds->pebs_absolute_maximum = ds->pebs_buffer_base +
|
||||
max * x86_pmu.pebs_record_size;
|
||||
/*
|
||||
* Always use single record PEBS
|
||||
*/
|
||||
ds->pebs_interrupt_threshold = ds->pebs_buffer_base +
|
||||
x86_pmu.pebs_record_size;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
}
|
||||
|
||||
if (err)
|
||||
release_ds_buffers();
|
||||
else {
|
||||
if (bts_err) {
|
||||
for_each_possible_cpu(cpu)
|
||||
release_bts_buffer(cpu);
|
||||
}
|
||||
|
||||
if (pebs_err) {
|
||||
for_each_possible_cpu(cpu)
|
||||
release_pebs_buffer(cpu);
|
||||
}
|
||||
|
||||
if (bts_err && pebs_err) {
|
||||
for_each_possible_cpu(cpu)
|
||||
release_ds_buffer(cpu);
|
||||
} else {
|
||||
if (x86_pmu.bts && !bts_err)
|
||||
x86_pmu.bts_active = 1;
|
||||
|
||||
if (x86_pmu.pebs && !pebs_err)
|
||||
x86_pmu.pebs_active = 1;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
init_debug_store_on_cpu(cpu);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -233,7 +318,7 @@ static int intel_pmu_drain_bts_buffer(void)
|
||||
if (!event)
|
||||
return 0;
|
||||
|
||||
if (!ds)
|
||||
if (!x86_pmu.bts_active)
|
||||
return 0;
|
||||
|
||||
at = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
|
||||
@@ -503,7 +588,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
|
||||
struct pebs_record_core *at, *top;
|
||||
int n;
|
||||
|
||||
if (!ds || !x86_pmu.pebs)
|
||||
if (!x86_pmu.pebs_active)
|
||||
return;
|
||||
|
||||
at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base;
|
||||
@@ -545,7 +630,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
|
||||
u64 status = 0;
|
||||
int bit, n;
|
||||
|
||||
if (!ds || !x86_pmu.pebs)
|
||||
if (!x86_pmu.pebs_active)
|
||||
return;
|
||||
|
||||
at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base;
|
||||
@@ -630,9 +715,8 @@ static void intel_ds_init(void)
|
||||
|
||||
#else /* CONFIG_CPU_SUP_INTEL */
|
||||
|
||||
static int reserve_ds_buffers(void)
|
||||
static void reserve_ds_buffers(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_ds_buffers(void)
|
||||
|
||||
@@ -726,6 +726,12 @@ int __init op_nmi_init(struct oprofile_operations *ops)
|
||||
case 0x11:
|
||||
cpu_type = "x86-64/family11h";
|
||||
break;
|
||||
case 0x12:
|
||||
cpu_type = "x86-64/family12h";
|
||||
break;
|
||||
case 0x14:
|
||||
cpu_type = "x86-64/family14h";
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -48,17 +48,24 @@ static unsigned long reset_value[NUM_VIRT_COUNTERS];
|
||||
|
||||
static u32 ibs_caps;
|
||||
|
||||
struct op_ibs_config {
|
||||
struct ibs_config {
|
||||
unsigned long op_enabled;
|
||||
unsigned long fetch_enabled;
|
||||
unsigned long max_cnt_fetch;
|
||||
unsigned long max_cnt_op;
|
||||
unsigned long rand_en;
|
||||
unsigned long dispatched_ops;
|
||||
unsigned long branch_target;
|
||||
};
|
||||
|
||||
static struct op_ibs_config ibs_config;
|
||||
static u64 ibs_op_ctl;
|
||||
struct ibs_state {
|
||||
u64 ibs_op_ctl;
|
||||
int branch_target;
|
||||
unsigned long sample_size;
|
||||
};
|
||||
|
||||
static struct ibs_config ibs_config;
|
||||
static struct ibs_state ibs_state;
|
||||
|
||||
/*
|
||||
* IBS cpuid feature detection
|
||||
@@ -71,8 +78,16 @@ static u64 ibs_op_ctl;
|
||||
* bit 0 is used to indicate the existence of IBS.
|
||||
*/
|
||||
#define IBS_CAPS_AVAIL (1U<<0)
|
||||
#define IBS_CAPS_FETCHSAM (1U<<1)
|
||||
#define IBS_CAPS_OPSAM (1U<<2)
|
||||
#define IBS_CAPS_RDWROPCNT (1U<<3)
|
||||
#define IBS_CAPS_OPCNT (1U<<4)
|
||||
#define IBS_CAPS_BRNTRGT (1U<<5)
|
||||
#define IBS_CAPS_OPCNTEXT (1U<<6)
|
||||
|
||||
#define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \
|
||||
| IBS_CAPS_FETCHSAM \
|
||||
| IBS_CAPS_OPSAM)
|
||||
|
||||
/*
|
||||
* IBS APIC setup
|
||||
@@ -99,12 +114,12 @@ static u32 get_ibs_caps(void)
|
||||
/* check IBS cpuid feature flags */
|
||||
max_level = cpuid_eax(0x80000000);
|
||||
if (max_level < IBS_CPUID_FEATURES)
|
||||
return IBS_CAPS_AVAIL;
|
||||
return IBS_CAPS_DEFAULT;
|
||||
|
||||
ibs_caps = cpuid_eax(IBS_CPUID_FEATURES);
|
||||
if (!(ibs_caps & IBS_CAPS_AVAIL))
|
||||
/* cpuid flags not valid */
|
||||
return IBS_CAPS_AVAIL;
|
||||
return IBS_CAPS_DEFAULT;
|
||||
|
||||
return ibs_caps;
|
||||
}
|
||||
@@ -197,8 +212,8 @@ op_amd_handle_ibs(struct pt_regs * const regs,
|
||||
rdmsrl(MSR_AMD64_IBSOPCTL, ctl);
|
||||
if (ctl & IBS_OP_VAL) {
|
||||
rdmsrl(MSR_AMD64_IBSOPRIP, val);
|
||||
oprofile_write_reserve(&entry, regs, val,
|
||||
IBS_OP_CODE, IBS_OP_SIZE);
|
||||
oprofile_write_reserve(&entry, regs, val, IBS_OP_CODE,
|
||||
ibs_state.sample_size);
|
||||
oprofile_add_data64(&entry, val);
|
||||
rdmsrl(MSR_AMD64_IBSOPDATA, val);
|
||||
oprofile_add_data64(&entry, val);
|
||||
@@ -210,10 +225,14 @@ op_amd_handle_ibs(struct pt_regs * const regs,
|
||||
oprofile_add_data64(&entry, val);
|
||||
rdmsrl(MSR_AMD64_IBSDCPHYSAD, val);
|
||||
oprofile_add_data64(&entry, val);
|
||||
if (ibs_state.branch_target) {
|
||||
rdmsrl(MSR_AMD64_IBSBRTARGET, val);
|
||||
oprofile_add_data(&entry, (unsigned long)val);
|
||||
}
|
||||
oprofile_write_commit(&entry);
|
||||
|
||||
/* reenable the IRQ */
|
||||
ctl = op_amd_randomize_ibs_op(ibs_op_ctl);
|
||||
ctl = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
|
||||
wrmsrl(MSR_AMD64_IBSOPCTL, ctl);
|
||||
}
|
||||
}
|
||||
@@ -226,21 +245,32 @@ static inline void op_amd_start_ibs(void)
|
||||
if (!ibs_caps)
|
||||
return;
|
||||
|
||||
memset(&ibs_state, 0, sizeof(ibs_state));
|
||||
|
||||
/*
|
||||
* Note: Since the max count settings may out of range we
|
||||
* write back the actual used values so that userland can read
|
||||
* it.
|
||||
*/
|
||||
|
||||
if (ibs_config.fetch_enabled) {
|
||||
val = (ibs_config.max_cnt_fetch >> 4) & IBS_FETCH_MAX_CNT;
|
||||
val = ibs_config.max_cnt_fetch >> 4;
|
||||
val = min(val, IBS_FETCH_MAX_CNT);
|
||||
ibs_config.max_cnt_fetch = val << 4;
|
||||
val |= ibs_config.rand_en ? IBS_FETCH_RAND_EN : 0;
|
||||
val |= IBS_FETCH_ENABLE;
|
||||
wrmsrl(MSR_AMD64_IBSFETCHCTL, val);
|
||||
}
|
||||
|
||||
if (ibs_config.op_enabled) {
|
||||
ibs_op_ctl = ibs_config.max_cnt_op >> 4;
|
||||
val = ibs_config.max_cnt_op >> 4;
|
||||
if (!(ibs_caps & IBS_CAPS_RDWROPCNT)) {
|
||||
/*
|
||||
* IbsOpCurCnt not supported. See
|
||||
* op_amd_randomize_ibs_op() for details.
|
||||
*/
|
||||
ibs_op_ctl = clamp(ibs_op_ctl, 0x0081ULL, 0xFF80ULL);
|
||||
val = clamp(val, 0x0081ULL, 0xFF80ULL);
|
||||
ibs_config.max_cnt_op = val << 4;
|
||||
} else {
|
||||
/*
|
||||
* The start value is randomized with a
|
||||
@@ -248,13 +278,24 @@ static inline void op_amd_start_ibs(void)
|
||||
* with the half of the randomized range. Also
|
||||
* avoid underflows.
|
||||
*/
|
||||
ibs_op_ctl = min(ibs_op_ctl + IBS_RANDOM_MAXCNT_OFFSET,
|
||||
IBS_OP_MAX_CNT);
|
||||
val += IBS_RANDOM_MAXCNT_OFFSET;
|
||||
if (ibs_caps & IBS_CAPS_OPCNTEXT)
|
||||
val = min(val, IBS_OP_MAX_CNT_EXT);
|
||||
else
|
||||
val = min(val, IBS_OP_MAX_CNT);
|
||||
ibs_config.max_cnt_op =
|
||||
(val - IBS_RANDOM_MAXCNT_OFFSET) << 4;
|
||||
}
|
||||
if (ibs_caps & IBS_CAPS_OPCNT && ibs_config.dispatched_ops)
|
||||
ibs_op_ctl |= IBS_OP_CNT_CTL;
|
||||
ibs_op_ctl |= IBS_OP_ENABLE;
|
||||
val = op_amd_randomize_ibs_op(ibs_op_ctl);
|
||||
val = ((val & ~IBS_OP_MAX_CNT) << 4) | (val & IBS_OP_MAX_CNT);
|
||||
val |= ibs_config.dispatched_ops ? IBS_OP_CNT_CTL : 0;
|
||||
val |= IBS_OP_ENABLE;
|
||||
ibs_state.ibs_op_ctl = val;
|
||||
ibs_state.sample_size = IBS_OP_SIZE;
|
||||
if (ibs_config.branch_target) {
|
||||
ibs_state.branch_target = 1;
|
||||
ibs_state.sample_size++;
|
||||
}
|
||||
val = op_amd_randomize_ibs_op(ibs_state.ibs_op_ctl);
|
||||
wrmsrl(MSR_AMD64_IBSOPCTL, val);
|
||||
}
|
||||
}
|
||||
@@ -281,29 +322,25 @@ static inline int eilvt_is_available(int offset)
|
||||
|
||||
static inline int ibs_eilvt_valid(void)
|
||||
{
|
||||
u64 val;
|
||||
int offset;
|
||||
u64 val;
|
||||
|
||||
rdmsrl(MSR_AMD64_IBSCTL, val);
|
||||
offset = val & IBSCTL_LVT_OFFSET_MASK;
|
||||
|
||||
if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
|
||||
pr_err(FW_BUG "cpu %d, invalid IBS "
|
||||
"interrupt offset %d (MSR%08X=0x%016llx)",
|
||||
smp_processor_id(), offset,
|
||||
MSR_AMD64_IBSCTL, val);
|
||||
pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n",
|
||||
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = val & IBSCTL_LVT_OFFSET_MASK;
|
||||
if (!eilvt_is_available(offset)) {
|
||||
pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n",
|
||||
smp_processor_id(), offset, MSR_AMD64_IBSCTL, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (eilvt_is_available(offset))
|
||||
return !0;
|
||||
|
||||
pr_err(FW_BUG "cpu %d, IBS interrupt offset %d "
|
||||
"not available (MSR%08X=0x%016llx)",
|
||||
smp_processor_id(), offset,
|
||||
MSR_AMD64_IBSCTL, val);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int get_ibs_offset(void)
|
||||
@@ -630,28 +667,33 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
|
||||
/* model specific files */
|
||||
|
||||
/* setup some reasonable defaults */
|
||||
memset(&ibs_config, 0, sizeof(ibs_config));
|
||||
ibs_config.max_cnt_fetch = 250000;
|
||||
ibs_config.fetch_enabled = 0;
|
||||
ibs_config.max_cnt_op = 250000;
|
||||
ibs_config.op_enabled = 0;
|
||||
ibs_config.dispatched_ops = 0;
|
||||
|
||||
dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
|
||||
oprofilefs_create_ulong(sb, dir, "enable",
|
||||
&ibs_config.fetch_enabled);
|
||||
oprofilefs_create_ulong(sb, dir, "max_count",
|
||||
&ibs_config.max_cnt_fetch);
|
||||
oprofilefs_create_ulong(sb, dir, "rand_enable",
|
||||
&ibs_config.rand_en);
|
||||
if (ibs_caps & IBS_CAPS_FETCHSAM) {
|
||||
dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
|
||||
oprofilefs_create_ulong(sb, dir, "enable",
|
||||
&ibs_config.fetch_enabled);
|
||||
oprofilefs_create_ulong(sb, dir, "max_count",
|
||||
&ibs_config.max_cnt_fetch);
|
||||
oprofilefs_create_ulong(sb, dir, "rand_enable",
|
||||
&ibs_config.rand_en);
|
||||
}
|
||||
|
||||
dir = oprofilefs_mkdir(sb, root, "ibs_op");
|
||||
oprofilefs_create_ulong(sb, dir, "enable",
|
||||
&ibs_config.op_enabled);
|
||||
oprofilefs_create_ulong(sb, dir, "max_count",
|
||||
&ibs_config.max_cnt_op);
|
||||
if (ibs_caps & IBS_CAPS_OPCNT)
|
||||
oprofilefs_create_ulong(sb, dir, "dispatched_ops",
|
||||
&ibs_config.dispatched_ops);
|
||||
if (ibs_caps & IBS_CAPS_OPSAM) {
|
||||
dir = oprofilefs_mkdir(sb, root, "ibs_op");
|
||||
oprofilefs_create_ulong(sb, dir, "enable",
|
||||
&ibs_config.op_enabled);
|
||||
oprofilefs_create_ulong(sb, dir, "max_count",
|
||||
&ibs_config.max_cnt_op);
|
||||
if (ibs_caps & IBS_CAPS_OPCNT)
|
||||
oprofilefs_create_ulong(sb, dir, "dispatched_ops",
|
||||
&ibs_config.dispatched_ops);
|
||||
if (ibs_caps & IBS_CAPS_BRNTRGT)
|
||||
oprofilefs_create_ulong(sb, dir, "branch_target",
|
||||
&ibs_config.branch_target);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ extern void open_softirq(int nr, void (*action)(struct softirq_action *));
|
||||
extern void softirq_init(void);
|
||||
static inline void __raise_softirq_irqoff(unsigned int nr)
|
||||
{
|
||||
trace_softirq_raise((struct softirq_action *)(unsigned long)nr, NULL);
|
||||
trace_softirq_raise(nr);
|
||||
or_softirq_pending(1UL << nr);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,18 +62,6 @@ enum ring_buffer_type {
|
||||
unsigned ring_buffer_event_length(struct ring_buffer_event *event);
|
||||
void *ring_buffer_event_data(struct ring_buffer_event *event);
|
||||
|
||||
/**
|
||||
* ring_buffer_event_time_delta - return the delta timestamp of the event
|
||||
* @event: the event to get the delta timestamp of
|
||||
*
|
||||
* The delta timestamp is the 27 bit timestamp since the last event.
|
||||
*/
|
||||
static inline unsigned
|
||||
ring_buffer_event_time_delta(struct ring_buffer_event *event)
|
||||
{
|
||||
return event->time_delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* ring_buffer_discard_commit will remove an event that has not
|
||||
* ben committed yet. If this is used, then ring_buffer_unlock_commit
|
||||
|
||||
+20
-34
@@ -86,76 +86,62 @@ TRACE_EVENT(irq_handler_exit,
|
||||
|
||||
DECLARE_EVENT_CLASS(softirq,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec),
|
||||
TP_ARGS(vec_nr),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( int, vec )
|
||||
__field( unsigned int, vec )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
if (vec)
|
||||
__entry->vec = (int)(h - vec);
|
||||
else
|
||||
__entry->vec = (int)(long)h;
|
||||
__entry->vec = vec_nr;
|
||||
),
|
||||
|
||||
TP_printk("vec=%d [action=%s]", __entry->vec,
|
||||
TP_printk("vec=%u [action=%s]", __entry->vec,
|
||||
show_softirq_name(__entry->vec))
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_entry - called immediately before the softirq handler
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter, contains a pointer to the struct softirq_action
|
||||
* which has a pointer to the action handler that is called. By subtracting
|
||||
* the @vec pointer from the @h pointer, we can determine the softirq
|
||||
* number. Also, when used in combination with the softirq_exit tracepoint
|
||||
* we can determine the softirq latency.
|
||||
* When used in combination with the softirq_exit tracepoint
|
||||
* we can determine the softirq handler runtine.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_entry,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_exit - called immediately after the softirq handler returns
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter contains a pointer to the struct softirq_action
|
||||
* that has handled the softirq. By subtracting the @vec pointer from
|
||||
* the @h pointer, we can determine the softirq number. Also, when used in
|
||||
* combination with the softirq_entry tracepoint we can determine the softirq
|
||||
* latency.
|
||||
* When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq handler runtine.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_exit,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_raise - called immediately when a softirq is raised
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter contains a pointer to the softirq vector number which is
|
||||
* raised. @vec is NULL and it means @h includes vector number not
|
||||
* softirq_action. When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq raise latency.
|
||||
* When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq raise to run latency.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_raise,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_IRQ_H */
|
||||
|
||||
+4
-3
@@ -74,7 +74,8 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
|
||||
/* NOTE: change this value only with kprobe_mutex held */
|
||||
static bool kprobes_all_disarmed;
|
||||
|
||||
static DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */
|
||||
/* This protects kprobe_table and optimizing_list */
|
||||
static DEFINE_MUTEX(kprobe_mutex);
|
||||
static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
|
||||
static struct {
|
||||
spinlock_t lock ____cacheline_aligned_in_smp;
|
||||
@@ -595,6 +596,7 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
/* This should be called with kprobe_mutex locked */
|
||||
static void __kprobes optimize_all_kprobes(void)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
@@ -607,17 +609,16 @@ static void __kprobes optimize_all_kprobes(void)
|
||||
return;
|
||||
|
||||
kprobes_allow_optimization = true;
|
||||
mutex_lock(&text_mutex);
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist)
|
||||
if (!kprobe_disabled(p))
|
||||
optimize_kprobe(p);
|
||||
}
|
||||
mutex_unlock(&text_mutex);
|
||||
printk(KERN_INFO "Kprobes globally optimized\n");
|
||||
}
|
||||
|
||||
/* This should be called with kprobe_mutex locked */
|
||||
static void __kprobes unoptimize_all_kprobes(void)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
|
||||
+31
-63
@@ -417,8 +417,8 @@ event_filter_match(struct perf_event *event)
|
||||
return event->cpu == -1 || event->cpu == smp_processor_id();
|
||||
}
|
||||
|
||||
static int
|
||||
__event_sched_out(struct perf_event *event,
|
||||
static void
|
||||
event_sched_out(struct perf_event *event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
@@ -437,13 +437,14 @@ __event_sched_out(struct perf_event *event,
|
||||
}
|
||||
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
if (event->pending_disable) {
|
||||
event->pending_disable = 0;
|
||||
event->state = PERF_EVENT_STATE_OFF;
|
||||
}
|
||||
event->tstamp_stopped = ctx->time;
|
||||
event->pmu->del(event, 0);
|
||||
event->oncpu = -1;
|
||||
|
||||
@@ -452,19 +453,6 @@ __event_sched_out(struct perf_event *event,
|
||||
ctx->nr_active--;
|
||||
if (event->attr.exclusive || !cpuctx->active_oncpu)
|
||||
cpuctx->exclusive = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
event_sched_out(struct perf_event *event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __event_sched_out(event, cpuctx, ctx);
|
||||
if (ret)
|
||||
event->tstamp_stopped = ctx->time;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -664,7 +652,7 @@ retry:
|
||||
}
|
||||
|
||||
static int
|
||||
__event_sched_in(struct perf_event *event,
|
||||
event_sched_in(struct perf_event *event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
@@ -684,6 +672,8 @@ __event_sched_in(struct perf_event *event,
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
event->tstamp_running += ctx->time - event->tstamp_stopped;
|
||||
|
||||
if (!is_software_event(event))
|
||||
cpuctx->active_oncpu++;
|
||||
ctx->nr_active++;
|
||||
@@ -694,35 +684,6 @@ __event_sched_in(struct perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
event_sched_in(struct perf_event *event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
int ret = __event_sched_in(event, cpuctx, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
event->tstamp_running += ctx->time - event->tstamp_stopped;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
group_commit_event_sched_in(struct perf_event *group_event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
struct perf_event *event;
|
||||
u64 now = ctx->time;
|
||||
|
||||
group_event->tstamp_running += now - group_event->tstamp_stopped;
|
||||
/*
|
||||
* Schedule in siblings as one group (if any):
|
||||
*/
|
||||
list_for_each_entry(event, &group_event->sibling_list, group_entry) {
|
||||
event->tstamp_running += now - event->tstamp_stopped;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
group_sched_in(struct perf_event *group_event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
@@ -730,19 +691,15 @@ group_sched_in(struct perf_event *group_event,
|
||||
{
|
||||
struct perf_event *event, *partial_group = NULL;
|
||||
struct pmu *pmu = group_event->pmu;
|
||||
u64 now = ctx->time;
|
||||
bool simulate = false;
|
||||
|
||||
if (group_event->state == PERF_EVENT_STATE_OFF)
|
||||
return 0;
|
||||
|
||||
pmu->start_txn(pmu);
|
||||
|
||||
/*
|
||||
* use __event_sched_in() to delay updating tstamp_running
|
||||
* until the transaction is committed. In case of failure
|
||||
* we will keep an unmodified tstamp_running which is a
|
||||
* requirement to get correct timing information
|
||||
*/
|
||||
if (__event_sched_in(group_event, cpuctx, ctx)) {
|
||||
if (event_sched_in(group_event, cpuctx, ctx)) {
|
||||
pmu->cancel_txn(pmu);
|
||||
return -EAGAIN;
|
||||
}
|
||||
@@ -751,31 +708,42 @@ group_sched_in(struct perf_event *group_event,
|
||||
* Schedule in siblings as one group (if any):
|
||||
*/
|
||||
list_for_each_entry(event, &group_event->sibling_list, group_entry) {
|
||||
if (__event_sched_in(event, cpuctx, ctx)) {
|
||||
if (event_sched_in(event, cpuctx, ctx)) {
|
||||
partial_group = event;
|
||||
goto group_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pmu->commit_txn(pmu)) {
|
||||
/* commit tstamp_running */
|
||||
group_commit_event_sched_in(group_event, cpuctx, ctx);
|
||||
if (!pmu->commit_txn(pmu))
|
||||
return 0;
|
||||
}
|
||||
|
||||
group_error:
|
||||
/*
|
||||
* Groups can be scheduled in as one unit only, so undo any
|
||||
* partial group before returning:
|
||||
* The events up to the failed event are scheduled out normally,
|
||||
* tstamp_stopped will be updated.
|
||||
*
|
||||
* use __event_sched_out() to avoid updating tstamp_stopped
|
||||
* because the event never actually ran
|
||||
* The failed events and the remaining siblings need to have
|
||||
* their timings updated as if they had gone thru event_sched_in()
|
||||
* and event_sched_out(). This is required to get consistent timings
|
||||
* across the group. This also takes care of the case where the group
|
||||
* could never be scheduled by ensuring tstamp_stopped is set to mark
|
||||
* the time the event was actually stopped, such that time delta
|
||||
* calculation in update_event_times() is correct.
|
||||
*/
|
||||
list_for_each_entry(event, &group_event->sibling_list, group_entry) {
|
||||
if (event == partial_group)
|
||||
break;
|
||||
__event_sched_out(event, cpuctx, ctx);
|
||||
simulate = true;
|
||||
|
||||
if (simulate) {
|
||||
event->tstamp_running += now - event->tstamp_stopped;
|
||||
event->tstamp_stopped = now;
|
||||
} else {
|
||||
event_sched_out(event, cpuctx, ctx);
|
||||
}
|
||||
}
|
||||
__event_sched_out(group_event, cpuctx, ctx);
|
||||
event_sched_out(group_event, cpuctx, ctx);
|
||||
|
||||
pmu->cancel_txn(pmu);
|
||||
|
||||
|
||||
+9
-7
@@ -229,18 +229,20 @@ restart:
|
||||
|
||||
do {
|
||||
if (pending & 1) {
|
||||
unsigned int vec_nr = h - softirq_vec;
|
||||
int prev_count = preempt_count();
|
||||
kstat_incr_softirqs_this_cpu(h - softirq_vec);
|
||||
|
||||
trace_softirq_entry(h, softirq_vec);
|
||||
kstat_incr_softirqs_this_cpu(vec_nr);
|
||||
|
||||
trace_softirq_entry(vec_nr);
|
||||
h->action(h);
|
||||
trace_softirq_exit(h, softirq_vec);
|
||||
trace_softirq_exit(vec_nr);
|
||||
if (unlikely(prev_count != preempt_count())) {
|
||||
printk(KERN_ERR "huh, entered softirq %td %s %p"
|
||||
printk(KERN_ERR "huh, entered softirq %u %s %p"
|
||||
"with preempt_count %08x,"
|
||||
" exited with %08x?\n", h - softirq_vec,
|
||||
softirq_to_name[h - softirq_vec],
|
||||
h->action, prev_count, preempt_count());
|
||||
" exited with %08x?\n", vec_nr,
|
||||
softirq_to_name[vec_nr], h->action,
|
||||
prev_count, preempt_count());
|
||||
preempt_count() = prev_count;
|
||||
}
|
||||
|
||||
|
||||
+172
-163
File diff suppressed because it is too large
Load Diff
@@ -3996,13 +3996,9 @@ static void tracing_init_debugfs_percpu(long cpu)
|
||||
{
|
||||
struct dentry *d_percpu = tracing_dentry_percpu();
|
||||
struct dentry *d_cpu;
|
||||
/* strlen(cpu) + MAX(log10(cpu)) + '\0' */
|
||||
char cpu_dir[7];
|
||||
char cpu_dir[30]; /* 30 characters should be more than enough */
|
||||
|
||||
if (cpu > 999 || cpu < 0)
|
||||
return;
|
||||
|
||||
sprintf(cpu_dir, "cpu%ld", cpu);
|
||||
snprintf(cpu_dir, 30, "cpu%ld", cpu);
|
||||
d_cpu = debugfs_create_dir(cpu_dir, d_percpu);
|
||||
if (!d_cpu) {
|
||||
pr_warning("Could not create debugfs '%s' entry\n", cpu_dir);
|
||||
|
||||
@@ -15,6 +15,23 @@ DESCRIPTION
|
||||
This command displays the symbolic event types which can be selected in the
|
||||
various perf commands with the -e option.
|
||||
|
||||
EVENT MODIFIERS
|
||||
---------------
|
||||
|
||||
Events can optionally have a modifer by appending a colon and one or
|
||||
more modifiers. Modifiers allow the user to restrict when events are
|
||||
counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor.
|
||||
|
||||
The 'p' modifier can be used for specifying how precise the instruction
|
||||
address should be. The 'p' modifier is currently only implemented for
|
||||
Intel PEBS and can be specified multiple times:
|
||||
0 - SAMPLE_IP can have arbitrary skid
|
||||
1 - SAMPLE_IP must have constant skid
|
||||
2 - SAMPLE_IP requested to have 0 skid
|
||||
3 - SAMPLE_IP must have 0 skid
|
||||
|
||||
The PEBS implementation now supports up to 2.
|
||||
|
||||
RAW HARDWARE EVENT DESCRIPTOR
|
||||
-----------------------------
|
||||
Even when an event is not available in a symbolic form within perf right now,
|
||||
|
||||
@@ -16,7 +16,9 @@ or
|
||||
or
|
||||
'perf probe' --list
|
||||
or
|
||||
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
||||
'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
||||
or
|
||||
'perf probe' [options] --vars='PROBEPOINT'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -31,6 +33,11 @@ OPTIONS
|
||||
--vmlinux=PATH::
|
||||
Specify vmlinux path which has debuginfo (Dwarf binary).
|
||||
|
||||
-m::
|
||||
--module=MODNAME::
|
||||
Specify module name in which perf-probe searches probe points
|
||||
or lines.
|
||||
|
||||
-s::
|
||||
--source=PATH::
|
||||
Specify path to kernel source.
|
||||
@@ -57,6 +64,15 @@ OPTIONS
|
||||
Show source code lines which can be probed. This needs an argument
|
||||
which specifies a range of the source code. (see LINE SYNTAX for detail)
|
||||
|
||||
-V::
|
||||
--vars=::
|
||||
Show available local variables at given probe point. The argument
|
||||
syntax is same as PROBE SYNTAX, but NO ARGs.
|
||||
|
||||
--externs::
|
||||
(Only for --vars) Show external defined variables in addition to local
|
||||
variables.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Forcibly add events with existing name.
|
||||
|
||||
@@ -83,6 +83,10 @@ OPTIONS
|
||||
--call-graph::
|
||||
Do call-graph (stack chain/backtrace) recording.
|
||||
|
||||
-q::
|
||||
--quiet::
|
||||
Don't print any message, useful for scripting.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc).
|
||||
|
||||
+67
-11
@@ -50,14 +50,17 @@ static struct {
|
||||
bool list_events;
|
||||
bool force_add;
|
||||
bool show_lines;
|
||||
bool show_vars;
|
||||
bool show_ext_vars;
|
||||
bool mod_events;
|
||||
int nevents;
|
||||
struct perf_probe_event events[MAX_PROBES];
|
||||
struct strlist *dellist;
|
||||
struct line_range line_range;
|
||||
const char *target_module;
|
||||
int max_probe_points;
|
||||
} params;
|
||||
|
||||
|
||||
/* Parse an event definition. Note that any error must die. */
|
||||
static int parse_probe_event(const char *str)
|
||||
{
|
||||
@@ -92,6 +95,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
||||
len = 0;
|
||||
for (i = 0; i < argc; i++)
|
||||
len += sprintf(&buf[len], "%s ", argv[i]);
|
||||
params.mod_events = true;
|
||||
ret = parse_probe_event(buf);
|
||||
free(buf);
|
||||
return ret;
|
||||
@@ -100,9 +104,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
||||
static int opt_add_probe_event(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
if (str)
|
||||
if (str) {
|
||||
params.mod_events = true;
|
||||
return parse_probe_event(str);
|
||||
else
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -110,6 +115,7 @@ static int opt_del_probe_event(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
if (str) {
|
||||
params.mod_events = true;
|
||||
if (!params.dellist)
|
||||
params.dellist = strlist__new(true, NULL);
|
||||
strlist__add(params.dellist, str);
|
||||
@@ -130,6 +136,25 @@ static int opt_show_lines(const struct option *opt __used,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int opt_show_vars(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
struct perf_probe_event *pev = ¶ms.events[params.nevents];
|
||||
int ret;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
ret = parse_probe_event(str);
|
||||
if (!ret && pev->nargs != 0) {
|
||||
pr_err(" Error: '--vars' doesn't accept arguments.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
params.show_vars = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char * const probe_usage[] = {
|
||||
@@ -138,7 +163,8 @@ static const char * const probe_usage[] = {
|
||||
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
|
||||
"perf probe --list",
|
||||
#ifdef DWARF_SUPPORT
|
||||
"perf probe --line 'LINEDESC'",
|
||||
"perf probe [<options>] --line 'LINEDESC'",
|
||||
"perf probe [<options>] --vars 'PROBEPOINT'",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
@@ -180,10 +206,17 @@ static const struct option options[] = {
|
||||
OPT_CALLBACK('L', "line", NULL,
|
||||
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
|
||||
"Show source code lines.", opt_show_lines),
|
||||
OPT_CALLBACK('V', "vars", NULL,
|
||||
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
|
||||
"Show accessible variables on PROBEDEF", opt_show_vars),
|
||||
OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars,
|
||||
"Show external variables too (with --vars only)"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
||||
"directory", "path to kernel source"),
|
||||
OPT_STRING('m', "module", ¶ms.target_module,
|
||||
"modname", "target module name"),
|
||||
#endif
|
||||
OPT__DRY_RUN(&probe_event_dry_run),
|
||||
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
|
||||
@@ -217,7 +250,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||
usage_with_options(probe_usage, options);
|
||||
|
||||
if (params.list_events) {
|
||||
if (params.nevents != 0 || params.dellist) {
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --list with --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
@@ -225,6 +258,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||
pr_err(" Error: Don't use --list with --line.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
if (params.show_vars) {
|
||||
pr_err(" Error: Don't use --list with --vars.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
ret = show_perf_probe_events();
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show event list. (%d)\n",
|
||||
@@ -234,17 +271,35 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
#ifdef DWARF_SUPPORT
|
||||
if (params.show_lines) {
|
||||
if (params.nevents != 0 || params.dellist) {
|
||||
pr_warning(" Error: Don't use --line with"
|
||||
" --add/--del.\n");
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --line with"
|
||||
" --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
if (params.show_vars) {
|
||||
pr_err(" Error: Don't use --line with --vars.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
|
||||
ret = show_line_range(¶ms.line_range);
|
||||
ret = show_line_range(¶ms.line_range, params.target_module);
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show lines. (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (params.show_vars) {
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --vars with"
|
||||
" --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
ret = show_available_vars(params.events, params.nevents,
|
||||
params.max_probe_points,
|
||||
params.target_module,
|
||||
params.show_ext_vars);
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (params.dellist) {
|
||||
@@ -258,8 +313,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
if (params.nevents) {
|
||||
ret = add_perf_probe_events(params.events, params.nevents,
|
||||
params.force_add,
|
||||
params.max_probe_points);
|
||||
params.max_probe_points,
|
||||
params.target_module,
|
||||
params.force_add);
|
||||
if (ret < 0) {
|
||||
pr_err(" Error: Failed to add events. (%d)\n", ret);
|
||||
return ret;
|
||||
|
||||
@@ -353,7 +353,7 @@ try_again:
|
||||
}
|
||||
|
||||
if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) {
|
||||
perror("Unable to read perf file descriptor\n");
|
||||
perror("Unable to read perf file descriptor");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||
|
||||
nr_cpus = read_cpu_map(cpu_list);
|
||||
if (nr_cpus < 1) {
|
||||
perror("failed to collect number of CPUs\n");
|
||||
perror("failed to collect number of CPUs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -761,6 +761,9 @@ static int __cmd_record(int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (quiet)
|
||||
return 0;
|
||||
|
||||
fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
|
||||
|
||||
/*
|
||||
@@ -820,6 +823,7 @@ static const struct option options[] = {
|
||||
"do call-graph (stack chain/backtrace) recording"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
|
||||
OPT_BOOLEAN('s', "stat", &inherit_stat,
|
||||
"per thread counts"),
|
||||
OPT_BOOLEAN('d', "data", &sample_address,
|
||||
|
||||
@@ -46,9 +46,6 @@ static struct scripting_ops *scripting_ops;
|
||||
|
||||
static void setup_scripting(void)
|
||||
{
|
||||
/* make sure PERF_EXEC_PATH is set for scripts */
|
||||
perf_set_argv_exec_path(perf_exec_path());
|
||||
|
||||
setup_perl_scripting();
|
||||
setup_python_scripting();
|
||||
|
||||
@@ -285,7 +282,7 @@ static int parse_scriptname(const struct option *opt __used,
|
||||
script++;
|
||||
} else {
|
||||
script = str;
|
||||
ext = strchr(script, '.');
|
||||
ext = strrchr(script, '.');
|
||||
if (!ext) {
|
||||
fprintf(stderr, "invalid script extension");
|
||||
return -1;
|
||||
@@ -593,6 +590,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
||||
suffix = REPORT_SUFFIX;
|
||||
}
|
||||
|
||||
/* make sure PERF_EXEC_PATH is set for scripts */
|
||||
perf_set_argv_exec_path(perf_exec_path());
|
||||
|
||||
if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
|
||||
char *record_script_path, *report_script_path;
|
||||
int live_pipe[2];
|
||||
@@ -625,12 +625,13 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
||||
dup2(live_pipe[1], 1);
|
||||
close(live_pipe[0]);
|
||||
|
||||
__argv = malloc(5 * sizeof(const char *));
|
||||
__argv = malloc(6 * sizeof(const char *));
|
||||
__argv[0] = "/bin/sh";
|
||||
__argv[1] = record_script_path;
|
||||
__argv[2] = "-o";
|
||||
__argv[3] = "-";
|
||||
__argv[4] = NULL;
|
||||
__argv[2] = "-q";
|
||||
__argv[3] = "-o";
|
||||
__argv[4] = "-";
|
||||
__argv[5] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
exit(-1);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user