mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 20:54:10 +08:00
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (311 commits) perf tools: Add mode to build without newt support perf symbols: symbol inconsistency message should be done only at verbose=1 perf tui: Add explicit -lslang option perf options: Type check all the remaining OPT_ variants perf options: Type check OPT_BOOLEAN and fix the offenders perf options: Check v type in OPT_U?INTEGER perf options: Introduce OPT_UINTEGER perf tui: Add workaround for slang < 2.1.4 perf record: Fix bug mismatch with -c option definition perf options: Introduce OPT_U64 perf tui: Add help window to show key associations perf tui: Make <- exit menus too perf newt: Add single key shortcuts for zoom into DSO and threads perf newt: Exit browser unconditionally when CTRL+C, q or Q is pressed perf newt: Fix the 'A'/'a' shortcut for annotate perf newt: Make <- exit the ui_browser x86, perf: P4 PMU - fix counters management logic perf newt: Make <- zoom out filters perf report: Report number of events, not samples perf hist: Clarify events_stats fields usage ... Fix up trivial conflicts in kernel/fork.c and tools/perf/builtin-record.c
This commit is contained in:
commit
4d7b4ac22f
@ -165,8 +165,8 @@ the user entry_handler invocation is also skipped.
|
||||
|
||||
1.4 How Does Jump Optimization Work?
|
||||
|
||||
If you configured your kernel with CONFIG_OPTPROBES=y (currently
|
||||
this option is supported on x86/x86-64, non-preemptive kernel) and
|
||||
If your kernel is built with CONFIG_OPTPROBES=y (currently this flag
|
||||
is automatically set 'y' on x86/x86-64, non-preemptive kernel) and
|
||||
the "debug.kprobes_optimization" kernel parameter is set to 1 (see
|
||||
sysctl(8)), Kprobes tries to reduce probe-hit overhead by using a jump
|
||||
instruction instead of a breakpoint instruction at each probepoint.
|
||||
@ -271,8 +271,6 @@ tweak the kernel's execution path, you need to suppress optimization,
|
||||
using one of the following techniques:
|
||||
- Specify an empty function for the kprobe's post_handler or break_handler.
|
||||
or
|
||||
- Config CONFIG_OPTPROBES=n.
|
||||
or
|
||||
- Execute 'sysctl -w debug.kprobes_optimization=n'
|
||||
|
||||
2. Architectures Supported
|
||||
@ -307,10 +305,6 @@ it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO),
|
||||
so you can use "objdump -d -l vmlinux" to see the source-to-object
|
||||
code mapping.
|
||||
|
||||
If you want to reduce probing overhead, set "Kprobes jump optimization
|
||||
support" (CONFIG_OPTPROBES) to "y". You can find this option under the
|
||||
"Kprobes" line.
|
||||
|
||||
4. API Reference
|
||||
|
||||
The Kprobes API includes a "register" function and an "unregister"
|
||||
|
@ -40,7 +40,9 @@ Synopsis of kprobe_events
|
||||
$stack : Fetch stack address.
|
||||
$retval : Fetch return value.(*)
|
||||
+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
|
||||
NAME=FETCHARG: Set NAME as the argument name of FETCHARG.
|
||||
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
|
||||
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
|
||||
(u8/u16/u32/u64/s8/s16/s32/s64) are supported.
|
||||
|
||||
(*) only for return probe.
|
||||
(**) this is useful for fetching a field of data structures.
|
||||
|
10
MAINTAINERS
10
MAINTAINERS
@ -4354,13 +4354,13 @@ M: Paul Mackerras <paulus@samba.org>
|
||||
M: Ingo Molnar <mingo@elte.hu>
|
||||
M: Arnaldo Carvalho de Melo <acme@redhat.com>
|
||||
S: Supported
|
||||
F: kernel/perf_event.c
|
||||
F: kernel/perf_event*.c
|
||||
F: include/linux/perf_event.h
|
||||
F: arch/*/kernel/perf_event.c
|
||||
F: arch/*/kernel/*/perf_event.c
|
||||
F: arch/*/kernel/*/*/perf_event.c
|
||||
F: arch/*/kernel/perf_event*.c
|
||||
F: arch/*/kernel/*/perf_event*.c
|
||||
F: arch/*/kernel/*/*/perf_event*.c
|
||||
F: arch/*/include/asm/perf_event.h
|
||||
F: arch/*/lib/perf_event.c
|
||||
F: arch/*/lib/perf_event*.c
|
||||
F: arch/*/kernel/perf_callchain.c
|
||||
F: tools/perf/
|
||||
|
||||
|
20
arch/Kconfig
20
arch/Kconfig
@ -42,15 +42,10 @@ config KPROBES
|
||||
If in doubt, say "N".
|
||||
|
||||
config OPTPROBES
|
||||
bool "Kprobes jump optimization support (EXPERIMENTAL)"
|
||||
default y
|
||||
depends on KPROBES
|
||||
def_bool y
|
||||
depends on KPROBES && HAVE_OPTPROBES
|
||||
depends on !PREEMPT
|
||||
depends on HAVE_OPTPROBES
|
||||
select KALLSYMS_ALL
|
||||
help
|
||||
This option will allow kprobes to optimize breakpoint to
|
||||
a jump for reducing its overhead.
|
||||
|
||||
config HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
bool
|
||||
@ -142,6 +137,17 @@ config HAVE_HW_BREAKPOINT
|
||||
bool
|
||||
depends on PERF_EVENTS
|
||||
|
||||
config HAVE_MIXED_BREAKPOINTS_REGS
|
||||
bool
|
||||
depends on HAVE_HW_BREAKPOINT
|
||||
help
|
||||
Depending on the arch implementation of hardware breakpoints,
|
||||
some of them have separate registers for data and instruction
|
||||
breakpoints addresses, others have mixed registers to store
|
||||
them but define the access type in a control register.
|
||||
Select this option if your arch implements breakpoints under the
|
||||
latter fashion.
|
||||
|
||||
config HAVE_USER_RETURN_NOTIFIER
|
||||
bool
|
||||
|
||||
|
@ -35,6 +35,9 @@ struct cpu_hw_events {
|
||||
u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES];
|
||||
unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES];
|
||||
unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES];
|
||||
|
||||
unsigned int group_flag;
|
||||
int n_txn_start;
|
||||
};
|
||||
DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
|
||||
|
||||
@ -718,66 +721,6 @@ static int collect_events(struct perf_event *group, int max_count,
|
||||
return n;
|
||||
}
|
||||
|
||||
static void event_sched_in(struct perf_event *event)
|
||||
{
|
||||
event->state = PERF_EVENT_STATE_ACTIVE;
|
||||
event->oncpu = smp_processor_id();
|
||||
event->tstamp_running += event->ctx->time - event->tstamp_stopped;
|
||||
if (is_software_event(event))
|
||||
event->pmu->enable(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to enable a whole group of events.
|
||||
* Returns 1 if the group was enabled, or -EAGAIN if it could not be.
|
||||
* Assumes the caller has disabled interrupts and has
|
||||
* frozen the PMU with hw_perf_save_disable.
|
||||
*/
|
||||
int hw_perf_group_sched_in(struct perf_event *group_leader,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
struct cpu_hw_events *cpuhw;
|
||||
long i, n, n0;
|
||||
struct perf_event *sub;
|
||||
|
||||
if (!ppmu)
|
||||
return 0;
|
||||
cpuhw = &__get_cpu_var(cpu_hw_events);
|
||||
n0 = cpuhw->n_events;
|
||||
n = collect_events(group_leader, ppmu->n_counter - n0,
|
||||
&cpuhw->event[n0], &cpuhw->events[n0],
|
||||
&cpuhw->flags[n0]);
|
||||
if (n < 0)
|
||||
return -EAGAIN;
|
||||
if (check_excludes(cpuhw->event, cpuhw->flags, n0, n))
|
||||
return -EAGAIN;
|
||||
i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n + n0);
|
||||
if (i < 0)
|
||||
return -EAGAIN;
|
||||
cpuhw->n_events = n0 + n;
|
||||
cpuhw->n_added += n;
|
||||
|
||||
/*
|
||||
* OK, this group can go on; update event states etc.,
|
||||
* and enable any software events
|
||||
*/
|
||||
for (i = n0; i < n0 + n; ++i)
|
||||
cpuhw->event[i]->hw.config = cpuhw->events[i];
|
||||
cpuctx->active_oncpu += n;
|
||||
n = 1;
|
||||
event_sched_in(group_leader);
|
||||
list_for_each_entry(sub, &group_leader->sibling_list, group_entry) {
|
||||
if (sub->state != PERF_EVENT_STATE_OFF) {
|
||||
event_sched_in(sub);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
ctx->nr_active += n;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a event to the PMU.
|
||||
* If all events are not already frozen, then we disable and
|
||||
@ -805,12 +748,22 @@ static int power_pmu_enable(struct perf_event *event)
|
||||
cpuhw->event[n0] = event;
|
||||
cpuhw->events[n0] = event->hw.config;
|
||||
cpuhw->flags[n0] = event->hw.event_base;
|
||||
|
||||
/*
|
||||
* If group events scheduling transaction was started,
|
||||
* skip the schedulability test here, it will be peformed
|
||||
* at commit time(->commit_txn) as a whole
|
||||
*/
|
||||
if (cpuhw->group_flag & PERF_EVENT_TXN_STARTED)
|
||||
goto nocheck;
|
||||
|
||||
if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1))
|
||||
goto out;
|
||||
if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1))
|
||||
goto out;
|
||||
|
||||
event->hw.config = cpuhw->events[n0];
|
||||
|
||||
nocheck:
|
||||
++cpuhw->n_events;
|
||||
++cpuhw->n_added;
|
||||
|
||||
@ -896,11 +849,65 @@ static void power_pmu_unthrottle(struct perf_event *event)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start group events scheduling transaction
|
||||
* Set the flag to make pmu::enable() not perform the
|
||||
* schedulability test, it will be performed at commit time
|
||||
*/
|
||||
void power_pmu_start_txn(const struct pmu *pmu)
|
||||
{
|
||||
struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
cpuhw->group_flag |= PERF_EVENT_TXN_STARTED;
|
||||
cpuhw->n_txn_start = cpuhw->n_events;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop group events scheduling transaction
|
||||
* Clear the flag and pmu::enable() will perform the
|
||||
* schedulability test.
|
||||
*/
|
||||
void power_pmu_cancel_txn(const struct pmu *pmu)
|
||||
{
|
||||
struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
cpuhw->group_flag &= ~PERF_EVENT_TXN_STARTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Commit group events scheduling transaction
|
||||
* Perform the group schedulability test as a whole
|
||||
* Return 0 if success
|
||||
*/
|
||||
int power_pmu_commit_txn(const struct pmu *pmu)
|
||||
{
|
||||
struct cpu_hw_events *cpuhw;
|
||||
long i, n;
|
||||
|
||||
if (!ppmu)
|
||||
return -EAGAIN;
|
||||
cpuhw = &__get_cpu_var(cpu_hw_events);
|
||||
n = cpuhw->n_events;
|
||||
if (check_excludes(cpuhw->event, cpuhw->flags, 0, n))
|
||||
return -EAGAIN;
|
||||
i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n);
|
||||
if (i < 0)
|
||||
return -EAGAIN;
|
||||
|
||||
for (i = cpuhw->n_txn_start; i < n; ++i)
|
||||
cpuhw->event[i]->hw.config = cpuhw->events[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pmu power_pmu = {
|
||||
.enable = power_pmu_enable,
|
||||
.disable = power_pmu_disable,
|
||||
.read = power_pmu_read,
|
||||
.unthrottle = power_pmu_unthrottle,
|
||||
.start_txn = power_pmu_start_txn,
|
||||
.cancel_txn = power_pmu_cancel_txn,
|
||||
.commit_txn = power_pmu_commit_txn,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -44,6 +44,7 @@ config SUPERH32
|
||||
select HAVE_FUNCTION_GRAPH_TRACER
|
||||
select HAVE_ARCH_KGDB
|
||||
select HAVE_HW_BREAKPOINT
|
||||
select HAVE_MIXED_BREAKPOINTS_REGS
|
||||
select PERF_EVENTS if HAVE_HW_BREAKPOINT
|
||||
select ARCH_HIBERNATION_POSSIBLE if MMU
|
||||
|
||||
|
@ -46,10 +46,14 @@ struct pmu;
|
||||
/* Maximum number of UBC channels */
|
||||
#define HBP_NUM 2
|
||||
|
||||
static inline int hw_breakpoint_slots(int type)
|
||||
{
|
||||
return HBP_NUM;
|
||||
}
|
||||
|
||||
/* arch/sh/kernel/hw_breakpoint.c */
|
||||
extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len);
|
||||
extern int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
struct task_struct *tsk);
|
||||
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
|
||||
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
|
||||
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
|
||||
unsigned long val, void *data);
|
||||
|
||||
|
@ -119,26 +119,17 @@ static int get_hbp_len(u16 hbp_len)
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for virtual address in user space.
|
||||
*/
|
||||
int arch_check_va_in_userspace(unsigned long va, u16 hbp_len)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = get_hbp_len(hbp_len);
|
||||
|
||||
return (va <= TASK_SIZE - len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for virtual address in kernel space.
|
||||
*/
|
||||
static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len)
|
||||
int arch_check_bp_in_kernelspace(struct perf_event *bp)
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned long va;
|
||||
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
|
||||
|
||||
len = get_hbp_len(hbp_len);
|
||||
va = info->address;
|
||||
len = get_hbp_len(info->len);
|
||||
|
||||
return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
|
||||
}
|
||||
@ -226,8 +217,7 @@ static int arch_build_bp_info(struct perf_event *bp)
|
||||
/*
|
||||
* Validate the arch-specific HW Breakpoint register settings
|
||||
*/
|
||||
int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
struct task_struct *tsk)
|
||||
int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
{
|
||||
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
|
||||
unsigned int align;
|
||||
@ -270,15 +260,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
if (info->address & align)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that the virtual address is in the proper range */
|
||||
if (tsk) {
|
||||
if (!arch_check_va_in_userspace(info->address, info->len))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (!arch_check_va_in_kernelspace(info->address, info->len))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -363,8 +344,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
|
||||
perf_bp_event(bp, args->regs);
|
||||
|
||||
/* Deliver the signal to userspace */
|
||||
if (arch_check_va_in_userspace(bp->attr.bp_addr,
|
||||
bp->attr.bp_len)) {
|
||||
if (!arch_check_bp_in_kernelspace(bp)) {
|
||||
siginfo_t info;
|
||||
|
||||
info.si_signo = args->signr;
|
||||
|
@ -85,7 +85,7 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr)
|
||||
|
||||
bp = thread->ptrace_bps[0];
|
||||
if (!bp) {
|
||||
hw_breakpoint_init(&attr);
|
||||
ptrace_breakpoint_init(&attr);
|
||||
|
||||
attr.bp_addr = addr;
|
||||
attr.bp_len = HW_BREAKPOINT_LEN_2;
|
||||
|
@ -53,11 +53,15 @@ config X86
|
||||
select HAVE_KERNEL_LZMA
|
||||
select HAVE_KERNEL_LZO
|
||||
select HAVE_HW_BREAKPOINT
|
||||
select HAVE_MIXED_BREAKPOINTS_REGS
|
||||
select PERF_EVENTS
|
||||
select ANON_INODES
|
||||
select HAVE_ARCH_KMEMCHECK
|
||||
select HAVE_USER_RETURN_NOTIFIER
|
||||
|
||||
config INSTRUCTION_DECODER
|
||||
def_bool (KPROBES || PERF_EVENTS)
|
||||
|
||||
config OUTPUT_FORMAT
|
||||
string
|
||||
default "elf32-i386" if X86_32
|
||||
|
@ -502,23 +502,3 @@ config CPU_SUP_UMC_32
|
||||
CPU might render the kernel unbootable.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config X86_DS
|
||||
def_bool X86_PTRACE_BTS
|
||||
depends on X86_DEBUGCTLMSR
|
||||
select HAVE_HW_BRANCH_TRACER
|
||||
|
||||
config X86_PTRACE_BTS
|
||||
bool "Branch Trace Store"
|
||||
default y
|
||||
depends on X86_DEBUGCTLMSR
|
||||
depends on BROKEN
|
||||
---help---
|
||||
This adds a ptrace interface to the hardware's branch trace store.
|
||||
|
||||
Debuggers may use it to collect an execution trace of the debugged
|
||||
application in order to answer the question 'how did I get here?'.
|
||||
Debuggers may trace user mode as well as kernel mode.
|
||||
|
||||
Say Y unless there is no application development on this machine
|
||||
and you want to save a small amount of code size.
|
||||
|
@ -174,15 +174,6 @@ config IOMMU_LEAK
|
||||
Add a simple leak tracer to the IOMMU code. This is useful when you
|
||||
are debugging a buggy device driver that leaks IOMMU mappings.
|
||||
|
||||
config X86_DS_SELFTEST
|
||||
bool "DS selftest"
|
||||
default y
|
||||
depends on DEBUG_KERNEL
|
||||
depends on X86_DS
|
||||
---help---
|
||||
Perform Debug Store selftests at boot time.
|
||||
If in doubt, say "N".
|
||||
|
||||
config HAVE_MMIOTRACE_SUPPORT
|
||||
def_bool y
|
||||
|
||||
|
@ -373,6 +373,7 @@ extern atomic_t init_deasserted;
|
||||
extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
static inline u32 apic_read(u32 reg)
|
||||
{
|
||||
return apic->read(reg);
|
||||
@ -403,10 +404,19 @@ static inline u32 safe_apic_wait_icr_idle(void)
|
||||
return apic->safe_wait_icr_idle();
|
||||
}
|
||||
|
||||
#else /* CONFIG_X86_LOCAL_APIC */
|
||||
|
||||
static inline u32 apic_read(u32 reg) { return 0; }
|
||||
static inline void apic_write(u32 reg, u32 val) { }
|
||||
static inline u64 apic_icr_read(void) { return 0; }
|
||||
static inline void apic_icr_write(u32 low, u32 high) { }
|
||||
static inline void apic_wait_icr_idle(void) { }
|
||||
static inline u32 safe_apic_wait_icr_idle(void) { return 0; }
|
||||
|
||||
#endif /* CONFIG_X86_LOCAL_APIC */
|
||||
|
||||
static inline void ack_APIC_irq(void)
|
||||
{
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
/*
|
||||
* ack_APIC_irq() actually gets compiled as a single instruction
|
||||
* ... yummie.
|
||||
@ -414,7 +424,6 @@ static inline void ack_APIC_irq(void)
|
||||
|
||||
/* Docs say use 0 for future compatibility */
|
||||
apic_write(APIC_EOI, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned default_get_apic_id(unsigned long x)
|
||||
|
@ -1,302 +0,0 @@
|
||||
/*
|
||||
* Debug Store (DS) support
|
||||
*
|
||||
* This provides a low-level interface to the hardware's Debug Store
|
||||
* feature that is used for branch trace store (BTS) and
|
||||
* precise-event based sampling (PEBS).
|
||||
*
|
||||
* It manages:
|
||||
* - DS and BTS hardware configuration
|
||||
* - buffer overflow handling (to be done)
|
||||
* - buffer access
|
||||
*
|
||||
* It does not do:
|
||||
* - security checking (is the caller allowed to trace the task)
|
||||
* - buffer allocation (memory accounting)
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2007-2009
|
||||
*/
|
||||
|
||||
#ifndef _ASM_X86_DS_H
|
||||
#define _ASM_X86_DS_H
|
||||
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
|
||||
#ifdef CONFIG_X86_DS
|
||||
|
||||
struct task_struct;
|
||||
struct ds_context;
|
||||
struct ds_tracer;
|
||||
struct bts_tracer;
|
||||
struct pebs_tracer;
|
||||
|
||||
typedef void (*bts_ovfl_callback_t)(struct bts_tracer *);
|
||||
typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *);
|
||||
|
||||
|
||||
/*
|
||||
* A list of features plus corresponding macros to talk about them in
|
||||
* the ds_request function's flags parameter.
|
||||
*
|
||||
* We use the enum to index an array of corresponding control bits;
|
||||
* we use the macro to index a flags bit-vector.
|
||||
*/
|
||||
enum ds_feature {
|
||||
dsf_bts = 0,
|
||||
dsf_bts_kernel,
|
||||
#define BTS_KERNEL (1 << dsf_bts_kernel)
|
||||
/* trace kernel-mode branches */
|
||||
|
||||
dsf_bts_user,
|
||||
#define BTS_USER (1 << dsf_bts_user)
|
||||
/* trace user-mode branches */
|
||||
|
||||
dsf_bts_overflow,
|
||||
dsf_bts_max,
|
||||
dsf_pebs = dsf_bts_max,
|
||||
|
||||
dsf_pebs_max,
|
||||
dsf_ctl_max = dsf_pebs_max,
|
||||
dsf_bts_timestamps = dsf_ctl_max,
|
||||
#define BTS_TIMESTAMPS (1 << dsf_bts_timestamps)
|
||||
/* add timestamps into BTS trace */
|
||||
|
||||
#define BTS_USER_FLAGS (BTS_KERNEL | BTS_USER | BTS_TIMESTAMPS)
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Request BTS or PEBS
|
||||
*
|
||||
* Due to alignement constraints, the actual buffer may be slightly
|
||||
* smaller than the requested or provided buffer.
|
||||
*
|
||||
* Returns a pointer to a tracer structure on success, or
|
||||
* ERR_PTR(errcode) on failure.
|
||||
*
|
||||
* The interrupt threshold is independent from the overflow callback
|
||||
* to allow users to use their own overflow interrupt handling mechanism.
|
||||
*
|
||||
* The function might sleep.
|
||||
*
|
||||
* task: the task to request recording for
|
||||
* cpu: the cpu to request recording for
|
||||
* base: the base pointer for the (non-pageable) buffer;
|
||||
* size: the size of the provided buffer in bytes
|
||||
* ovfl: pointer to a function to be called on buffer overflow;
|
||||
* NULL if cyclic buffer requested
|
||||
* th: the interrupt threshold in records from the end of the buffer;
|
||||
* -1 if no interrupt threshold is requested.
|
||||
* flags: a bit-mask of the above flags
|
||||
*/
|
||||
extern struct bts_tracer *ds_request_bts_task(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
bts_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size,
|
||||
bts_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct pebs_tracer *ds_request_pebs_task(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
pebs_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct pebs_tracer *ds_request_pebs_cpu(int cpu,
|
||||
void *base, size_t size,
|
||||
pebs_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
|
||||
/*
|
||||
* Release BTS or PEBS resources
|
||||
* Suspend and resume BTS or PEBS tracing
|
||||
*
|
||||
* Must be called with irq's enabled.
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern void ds_release_bts(struct bts_tracer *tracer);
|
||||
extern void ds_suspend_bts(struct bts_tracer *tracer);
|
||||
extern void ds_resume_bts(struct bts_tracer *tracer);
|
||||
extern void ds_release_pebs(struct pebs_tracer *tracer);
|
||||
extern void ds_suspend_pebs(struct pebs_tracer *tracer);
|
||||
extern void ds_resume_pebs(struct pebs_tracer *tracer);
|
||||
|
||||
/*
|
||||
* Release BTS or PEBS resources
|
||||
* Suspend and resume BTS or PEBS tracing
|
||||
*
|
||||
* Cpu tracers must call this on the traced cpu.
|
||||
* Task tracers must call ds_release_~_noirq() for themselves.
|
||||
*
|
||||
* May be called with irq's disabled.
|
||||
*
|
||||
* Returns 0 if successful;
|
||||
* -EPERM if the cpu tracer does not trace the current cpu.
|
||||
* -EPERM if the task tracer does not trace itself.
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern int ds_release_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_suspend_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_resume_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_release_pebs_noirq(struct pebs_tracer *tracer);
|
||||
extern int ds_suspend_pebs_noirq(struct pebs_tracer *tracer);
|
||||
extern int ds_resume_pebs_noirq(struct pebs_tracer *tracer);
|
||||
|
||||
|
||||
/*
|
||||
* The raw DS buffer state as it is used for BTS and PEBS recording.
|
||||
*
|
||||
* This is the low-level, arch-dependent interface for working
|
||||
* directly on the raw trace data.
|
||||
*/
|
||||
struct ds_trace {
|
||||
/* the number of bts/pebs records */
|
||||
size_t n;
|
||||
/* the size of a bts/pebs record in bytes */
|
||||
size_t size;
|
||||
/* pointers into the raw buffer:
|
||||
- to the first entry */
|
||||
void *begin;
|
||||
/* - one beyond the last entry */
|
||||
void *end;
|
||||
/* - one beyond the newest entry */
|
||||
void *top;
|
||||
/* - the interrupt threshold */
|
||||
void *ith;
|
||||
/* flags given on ds_request() */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* An arch-independent view on branch trace data.
|
||||
*/
|
||||
enum bts_qualifier {
|
||||
bts_invalid,
|
||||
#define BTS_INVALID bts_invalid
|
||||
|
||||
bts_branch,
|
||||
#define BTS_BRANCH bts_branch
|
||||
|
||||
bts_task_arrives,
|
||||
#define BTS_TASK_ARRIVES bts_task_arrives
|
||||
|
||||
bts_task_departs,
|
||||
#define BTS_TASK_DEPARTS bts_task_departs
|
||||
|
||||
bts_qual_bit_size = 4,
|
||||
bts_qual_max = (1 << bts_qual_bit_size),
|
||||
};
|
||||
|
||||
struct bts_struct {
|
||||
__u64 qualifier;
|
||||
union {
|
||||
/* BTS_BRANCH */
|
||||
struct {
|
||||
__u64 from;
|
||||
__u64 to;
|
||||
} lbr;
|
||||
/* BTS_TASK_ARRIVES or BTS_TASK_DEPARTS */
|
||||
struct {
|
||||
__u64 clock;
|
||||
pid_t pid;
|
||||
} event;
|
||||
} variant;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The BTS state.
|
||||
*
|
||||
* This gives access to the raw DS state and adds functions to provide
|
||||
* an arch-independent view of the BTS data.
|
||||
*/
|
||||
struct bts_trace {
|
||||
struct ds_trace ds;
|
||||
|
||||
int (*read)(struct bts_tracer *tracer, const void *at,
|
||||
struct bts_struct *out);
|
||||
int (*write)(struct bts_tracer *tracer, const struct bts_struct *in);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The PEBS state.
|
||||
*
|
||||
* This gives access to the raw DS state and the PEBS-specific counter
|
||||
* reset value.
|
||||
*/
|
||||
struct pebs_trace {
|
||||
struct ds_trace ds;
|
||||
|
||||
/* the number of valid counters in the below array */
|
||||
unsigned int counters;
|
||||
|
||||
#define MAX_PEBS_COUNTERS 4
|
||||
/* the counter reset value */
|
||||
unsigned long long counter_reset[MAX_PEBS_COUNTERS];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Read the BTS or PEBS trace.
|
||||
*
|
||||
* Returns a view on the trace collected for the parameter tracer.
|
||||
*
|
||||
* The view remains valid as long as the traced task is not running or
|
||||
* the tracer is suspended.
|
||||
* Writes into the trace buffer are not reflected.
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern const struct bts_trace *ds_read_bts(struct bts_tracer *tracer);
|
||||
extern const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer);
|
||||
|
||||
|
||||
/*
|
||||
* Reset the write pointer of the BTS/PEBS buffer.
|
||||
*
|
||||
* Returns 0 on success; -Eerrno on error
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern int ds_reset_bts(struct bts_tracer *tracer);
|
||||
extern int ds_reset_pebs(struct pebs_tracer *tracer);
|
||||
|
||||
/*
|
||||
* Set the PEBS counter reset value.
|
||||
*
|
||||
* Returns 0 on success; -Eerrno on error
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_pebs()
|
||||
* counter: the index of the counter
|
||||
* value: the new counter reset value
|
||||
*/
|
||||
extern int ds_set_pebs_reset(struct pebs_tracer *tracer,
|
||||
unsigned int counter, u64 value);
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
*/
|
||||
struct cpuinfo_x86;
|
||||
extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
|
||||
|
||||
/*
|
||||
* Context switch work
|
||||
*/
|
||||
extern void ds_switch_to(struct task_struct *prev, struct task_struct *next);
|
||||
|
||||
#else /* CONFIG_X86_DS */
|
||||
|
||||
struct cpuinfo_x86;
|
||||
static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {}
|
||||
static inline void ds_switch_to(struct task_struct *prev,
|
||||
struct task_struct *next) {}
|
||||
|
||||
#endif /* CONFIG_X86_DS */
|
||||
#endif /* _ASM_X86_DS_H */
|
@ -41,12 +41,16 @@ struct arch_hw_breakpoint {
|
||||
/* Total number of available HW breakpoint registers */
|
||||
#define HBP_NUM 4
|
||||
|
||||
static inline int hw_breakpoint_slots(int type)
|
||||
{
|
||||
return HBP_NUM;
|
||||
}
|
||||
|
||||
struct perf_event;
|
||||
struct pmu;
|
||||
|
||||
extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len);
|
||||
extern int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
struct task_struct *tsk);
|
||||
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
|
||||
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
|
||||
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
|
||||
unsigned long val, void *data);
|
||||
|
||||
|
@ -68,6 +68,8 @@ struct insn {
|
||||
const insn_byte_t *next_byte;
|
||||
};
|
||||
|
||||
#define MAX_INSN_SIZE 16
|
||||
|
||||
#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6)
|
||||
#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3)
|
||||
#define X86_MODRM_RM(modrm) ((modrm) & 0x07)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
#define __ARCH_WANT_KPROBES_INSN_SLOT
|
||||
|
||||
@ -36,7 +37,6 @@ typedef u8 kprobe_opcode_t;
|
||||
#define RELATIVEJUMP_SIZE 5
|
||||
#define RELATIVECALL_OPCODE 0xe8
|
||||
#define RELATIVE_ADDR_SIZE 4
|
||||
#define MAX_INSN_SIZE 16
|
||||
#define MAX_STACK_SIZE 64
|
||||
#define MIN_STACK_SIZE(ADDR) \
|
||||
(((MAX_STACK_SIZE) < (((unsigned long)current_thread_info()) + \
|
||||
|
@ -71,11 +71,14 @@
|
||||
#define MSR_IA32_LASTINTTOIP 0x000001de
|
||||
|
||||
/* DEBUGCTLMSR bits (others vary by model): */
|
||||
#define _DEBUGCTLMSR_LBR 0 /* last branch recording */
|
||||
#define _DEBUGCTLMSR_BTF 1 /* single-step on branches */
|
||||
|
||||
#define DEBUGCTLMSR_LBR (1UL << _DEBUGCTLMSR_LBR)
|
||||
#define DEBUGCTLMSR_BTF (1UL << _DEBUGCTLMSR_BTF)
|
||||
#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */
|
||||
#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */
|
||||
#define DEBUGCTLMSR_TR (1UL << 6)
|
||||
#define DEBUGCTLMSR_BTS (1UL << 7)
|
||||
#define DEBUGCTLMSR_BTINT (1UL << 8)
|
||||
#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9)
|
||||
#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10)
|
||||
#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
|
||||
|
||||
#define MSR_IA32_MC0_CTL 0x00000400
|
||||
#define MSR_IA32_MC0_STATUS 0x00000401
|
||||
@ -359,6 +362,8 @@
|
||||
#define MSR_P4_U2L_ESCR0 0x000003b0
|
||||
#define MSR_P4_U2L_ESCR1 0x000003b1
|
||||
|
||||
#define MSR_P4_PEBS_MATRIX_VERT 0x000003f2
|
||||
|
||||
/* Intel Core-based CPU performance counters */
|
||||
#define MSR_CORE_PERF_FIXED_CTR0 0x00000309
|
||||
#define MSR_CORE_PERF_FIXED_CTR1 0x0000030a
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Performance event hw details:
|
||||
*/
|
||||
|
||||
#define X86_PMC_MAX_GENERIC 8
|
||||
#define X86_PMC_MAX_GENERIC 32
|
||||
#define X86_PMC_MAX_FIXED 3
|
||||
|
||||
#define X86_PMC_IDX_GENERIC 0
|
||||
@ -18,39 +18,31 @@
|
||||
#define MSR_ARCH_PERFMON_EVENTSEL0 0x186
|
||||
#define MSR_ARCH_PERFMON_EVENTSEL1 0x187
|
||||
|
||||
#define ARCH_PERFMON_EVENTSEL_ENABLE (1 << 22)
|
||||
#define ARCH_PERFMON_EVENTSEL_ANY (1 << 21)
|
||||
#define ARCH_PERFMON_EVENTSEL_INT (1 << 20)
|
||||
#define ARCH_PERFMON_EVENTSEL_OS (1 << 17)
|
||||
#define ARCH_PERFMON_EVENTSEL_USR (1 << 16)
|
||||
#define ARCH_PERFMON_EVENTSEL_EVENT 0x000000FFULL
|
||||
#define ARCH_PERFMON_EVENTSEL_UMASK 0x0000FF00ULL
|
||||
#define ARCH_PERFMON_EVENTSEL_USR (1ULL << 16)
|
||||
#define ARCH_PERFMON_EVENTSEL_OS (1ULL << 17)
|
||||
#define ARCH_PERFMON_EVENTSEL_EDGE (1ULL << 18)
|
||||
#define ARCH_PERFMON_EVENTSEL_INT (1ULL << 20)
|
||||
#define ARCH_PERFMON_EVENTSEL_ANY (1ULL << 21)
|
||||
#define ARCH_PERFMON_EVENTSEL_ENABLE (1ULL << 22)
|
||||
#define ARCH_PERFMON_EVENTSEL_INV (1ULL << 23)
|
||||
#define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL
|
||||
|
||||
/*
|
||||
* Includes eventsel and unit mask as well:
|
||||
*/
|
||||
#define AMD64_EVENTSEL_EVENT \
|
||||
(ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32))
|
||||
#define INTEL_ARCH_EVENT_MASK \
|
||||
(ARCH_PERFMON_EVENTSEL_UMASK | ARCH_PERFMON_EVENTSEL_EVENT)
|
||||
|
||||
|
||||
#define INTEL_ARCH_EVTSEL_MASK 0x000000FFULL
|
||||
#define INTEL_ARCH_UNIT_MASK 0x0000FF00ULL
|
||||
#define INTEL_ARCH_EDGE_MASK 0x00040000ULL
|
||||
#define INTEL_ARCH_INV_MASK 0x00800000ULL
|
||||
#define INTEL_ARCH_CNT_MASK 0xFF000000ULL
|
||||
#define INTEL_ARCH_EVENT_MASK (INTEL_ARCH_UNIT_MASK|INTEL_ARCH_EVTSEL_MASK)
|
||||
|
||||
/*
|
||||
* filter mask to validate fixed counter events.
|
||||
* the following filters disqualify for fixed counters:
|
||||
* - inv
|
||||
* - edge
|
||||
* - cnt-mask
|
||||
* The other filters are supported by fixed counters.
|
||||
* The any-thread option is supported starting with v3.
|
||||
*/
|
||||
#define INTEL_ARCH_FIXED_MASK \
|
||||
(INTEL_ARCH_CNT_MASK| \
|
||||
INTEL_ARCH_INV_MASK| \
|
||||
INTEL_ARCH_EDGE_MASK|\
|
||||
INTEL_ARCH_UNIT_MASK|\
|
||||
INTEL_ARCH_EVENT_MASK)
|
||||
#define X86_RAW_EVENT_MASK \
|
||||
(ARCH_PERFMON_EVENTSEL_EVENT | \
|
||||
ARCH_PERFMON_EVENTSEL_UMASK | \
|
||||
ARCH_PERFMON_EVENTSEL_EDGE | \
|
||||
ARCH_PERFMON_EVENTSEL_INV | \
|
||||
ARCH_PERFMON_EVENTSEL_CMASK)
|
||||
#define AMD64_RAW_EVENT_MASK \
|
||||
(X86_RAW_EVENT_MASK | \
|
||||
AMD64_EVENTSEL_EVENT)
|
||||
|
||||
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c
|
||||
#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8)
|
||||
@ -67,7 +59,7 @@
|
||||
union cpuid10_eax {
|
||||
struct {
|
||||
unsigned int version_id:8;
|
||||
unsigned int num_events:8;
|
||||
unsigned int num_counters:8;
|
||||
unsigned int bit_width:8;
|
||||
unsigned int mask_length:8;
|
||||
} split;
|
||||
@ -76,7 +68,7 @@ union cpuid10_eax {
|
||||
|
||||
union cpuid10_edx {
|
||||
struct {
|
||||
unsigned int num_events_fixed:4;
|
||||
unsigned int num_counters_fixed:4;
|
||||
unsigned int reserved:28;
|
||||
} split;
|
||||
unsigned int full;
|
||||
@ -136,6 +128,18 @@ extern void perf_events_lapic_init(void);
|
||||
|
||||
#define PERF_EVENT_INDEX_OFFSET 0
|
||||
|
||||
/*
|
||||
* Abuse bit 3 of the cpu eflags register to indicate proper PEBS IP fixups.
|
||||
* This flag is otherwise unused and ABI specified to be 0, so nobody should
|
||||
* care what we do with it.
|
||||
*/
|
||||
#define PERF_EFLAGS_EXACT (1UL << 3)
|
||||
|
||||
struct pt_regs;
|
||||
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
|
||||
extern unsigned long perf_misc_flags(struct pt_regs *regs);
|
||||
#define perf_misc_flags(regs) perf_misc_flags(regs)
|
||||
|
||||
#else
|
||||
static inline void init_hw_perf_events(void) { }
|
||||
static inline void perf_events_lapic_init(void) { }
|
||||
|
794
arch/x86/include/asm/perf_event_p4.h
Normal file
794
arch/x86/include/asm/perf_event_p4.h
Normal file
@ -0,0 +1,794 @@
|
||||
/*
|
||||
* Netburst Perfomance Events (P4, old Xeon)
|
||||
*/
|
||||
|
||||
#ifndef PERF_EVENT_P4_H
|
||||
#define PERF_EVENT_P4_H
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
* NetBurst has perfomance MSRs shared between
|
||||
* threads if HT is turned on, ie for both logical
|
||||
* processors (mem: in turn in Atom with HT support
|
||||
* perf-MSRs are not shared and every thread has its
|
||||
* own perf-MSRs set)
|
||||
*/
|
||||
#define ARCH_P4_TOTAL_ESCR (46)
|
||||
#define ARCH_P4_RESERVED_ESCR (2) /* IQ_ESCR(0,1) not always present */
|
||||
#define ARCH_P4_MAX_ESCR (ARCH_P4_TOTAL_ESCR - ARCH_P4_RESERVED_ESCR)
|
||||
#define ARCH_P4_MAX_CCCR (18)
|
||||
#define ARCH_P4_MAX_COUNTER (ARCH_P4_MAX_CCCR / 2)
|
||||
|
||||
#define P4_ESCR_EVENT_MASK 0x7e000000U
|
||||
#define P4_ESCR_EVENT_SHIFT 25
|
||||
#define P4_ESCR_EVENTMASK_MASK 0x01fffe00U
|
||||
#define P4_ESCR_EVENTMASK_SHIFT 9
|
||||
#define P4_ESCR_TAG_MASK 0x000001e0U
|
||||
#define P4_ESCR_TAG_SHIFT 5
|
||||
#define P4_ESCR_TAG_ENABLE 0x00000010U
|
||||
#define P4_ESCR_T0_OS 0x00000008U
|
||||
#define P4_ESCR_T0_USR 0x00000004U
|
||||
#define P4_ESCR_T1_OS 0x00000002U
|
||||
#define P4_ESCR_T1_USR 0x00000001U
|
||||
|
||||
#define P4_ESCR_EVENT(v) ((v) << P4_ESCR_EVENT_SHIFT)
|
||||
#define P4_ESCR_EMASK(v) ((v) << P4_ESCR_EVENTMASK_SHIFT)
|
||||
#define P4_ESCR_TAG(v) ((v) << P4_ESCR_TAG_SHIFT)
|
||||
|
||||
/* Non HT mask */
|
||||
#define P4_ESCR_MASK \
|
||||
(P4_ESCR_EVENT_MASK | \
|
||||
P4_ESCR_EVENTMASK_MASK | \
|
||||
P4_ESCR_TAG_MASK | \
|
||||
P4_ESCR_TAG_ENABLE | \
|
||||
P4_ESCR_T0_OS | \
|
||||
P4_ESCR_T0_USR)
|
||||
|
||||
/* HT mask */
|
||||
#define P4_ESCR_MASK_HT \
|
||||
(P4_ESCR_MASK | P4_ESCR_T1_OS | P4_ESCR_T1_USR)
|
||||
|
||||
#define P4_CCCR_OVF 0x80000000U
|
||||
#define P4_CCCR_CASCADE 0x40000000U
|
||||
#define P4_CCCR_OVF_PMI_T0 0x04000000U
|
||||
#define P4_CCCR_OVF_PMI_T1 0x08000000U
|
||||
#define P4_CCCR_FORCE_OVF 0x02000000U
|
||||
#define P4_CCCR_EDGE 0x01000000U
|
||||
#define P4_CCCR_THRESHOLD_MASK 0x00f00000U
|
||||
#define P4_CCCR_THRESHOLD_SHIFT 20
|
||||
#define P4_CCCR_COMPLEMENT 0x00080000U
|
||||
#define P4_CCCR_COMPARE 0x00040000U
|
||||
#define P4_CCCR_ESCR_SELECT_MASK 0x0000e000U
|
||||
#define P4_CCCR_ESCR_SELECT_SHIFT 13
|
||||
#define P4_CCCR_ENABLE 0x00001000U
|
||||
#define P4_CCCR_THREAD_SINGLE 0x00010000U
|
||||
#define P4_CCCR_THREAD_BOTH 0x00020000U
|
||||
#define P4_CCCR_THREAD_ANY 0x00030000U
|
||||
#define P4_CCCR_RESERVED 0x00000fffU
|
||||
|
||||
#define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT)
|
||||
#define P4_CCCR_ESEL(v) ((v) << P4_CCCR_ESCR_SELECT_SHIFT)
|
||||
|
||||
/* Custom bits in reerved CCCR area */
|
||||
#define P4_CCCR_CACHE_OPS_MASK 0x0000003fU
|
||||
|
||||
|
||||
/* Non HT mask */
|
||||
#define P4_CCCR_MASK \
|
||||
(P4_CCCR_OVF | \
|
||||
P4_CCCR_CASCADE | \
|
||||
P4_CCCR_OVF_PMI_T0 | \
|
||||
P4_CCCR_FORCE_OVF | \
|
||||
P4_CCCR_EDGE | \
|
||||
P4_CCCR_THRESHOLD_MASK | \
|
||||
P4_CCCR_COMPLEMENT | \
|
||||
P4_CCCR_COMPARE | \
|
||||
P4_CCCR_ESCR_SELECT_MASK | \
|
||||
P4_CCCR_ENABLE)
|
||||
|
||||
/* HT mask */
|
||||
#define P4_CCCR_MASK_HT (P4_CCCR_MASK | P4_CCCR_THREAD_ANY)
|
||||
|
||||
#define P4_GEN_ESCR_EMASK(class, name, bit) \
|
||||
class##__##name = ((1 << bit) << P4_ESCR_EVENTMASK_SHIFT)
|
||||
#define P4_ESCR_EMASK_BIT(class, name) class##__##name
|
||||
|
||||
/*
|
||||
* config field is 64bit width and consists of
|
||||
* HT << 63 | ESCR << 32 | CCCR
|
||||
* where HT is HyperThreading bit (since ESCR
|
||||
* has it reserved we may use it for own purpose)
|
||||
*
|
||||
* note that this is NOT the addresses of respective
|
||||
* ESCR and CCCR but rather an only packed value should
|
||||
* be unpacked and written to a proper addresses
|
||||
*
|
||||
* the base idea is to pack as much info as
|
||||
* possible
|
||||
*/
|
||||
#define p4_config_pack_escr(v) (((u64)(v)) << 32)
|
||||
#define p4_config_pack_cccr(v) (((u64)(v)) & 0xffffffffULL)
|
||||
#define p4_config_unpack_escr(v) (((u64)(v)) >> 32)
|
||||
#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xffffffffULL)
|
||||
|
||||
#define p4_config_unpack_emask(v) \
|
||||
({ \
|
||||
u32 t = p4_config_unpack_escr((v)); \
|
||||
t = t & P4_ESCR_EVENTMASK_MASK; \
|
||||
t = t >> P4_ESCR_EVENTMASK_SHIFT; \
|
||||
t; \
|
||||
})
|
||||
|
||||
#define p4_config_unpack_event(v) \
|
||||
({ \
|
||||
u32 t = p4_config_unpack_escr((v)); \
|
||||
t = t & P4_ESCR_EVENT_MASK; \
|
||||
t = t >> P4_ESCR_EVENT_SHIFT; \
|
||||
t; \
|
||||
})
|
||||
|
||||
#define p4_config_unpack_cache_event(v) (((u64)(v)) & P4_CCCR_CACHE_OPS_MASK)
|
||||
|
||||
#define P4_CONFIG_HT_SHIFT 63
|
||||
#define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT)
|
||||
|
||||
static inline bool p4_is_event_cascaded(u64 config)
|
||||
{
|
||||
u32 cccr = p4_config_unpack_cccr(config);
|
||||
return !!(cccr & P4_CCCR_CASCADE);
|
||||
}
|
||||
|
||||
static inline int p4_ht_config_thread(u64 config)
|
||||
{
|
||||
return !!(config & P4_CONFIG_HT);
|
||||
}
|
||||
|
||||
static inline u64 p4_set_ht_bit(u64 config)
|
||||
{
|
||||
return config | P4_CONFIG_HT;
|
||||
}
|
||||
|
||||
static inline u64 p4_clear_ht_bit(u64 config)
|
||||
{
|
||||
return config & ~P4_CONFIG_HT;
|
||||
}
|
||||
|
||||
static inline int p4_ht_active(void)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
return smp_num_siblings > 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int p4_ht_thread(int cpu)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
if (smp_num_siblings == 2)
|
||||
return cpu != cpumask_first(__get_cpu_var(cpu_sibling_map));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int p4_should_swap_ts(u64 config, int cpu)
|
||||
{
|
||||
return p4_ht_config_thread(config) ^ p4_ht_thread(cpu);
|
||||
}
|
||||
|
||||
static inline u32 p4_default_cccr_conf(int cpu)
|
||||
{
|
||||
/*
|
||||
* Note that P4_CCCR_THREAD_ANY is "required" on
|
||||
* non-HT machines (on HT machines we count TS events
|
||||
* regardless the state of second logical processor
|
||||
*/
|
||||
u32 cccr = P4_CCCR_THREAD_ANY;
|
||||
|
||||
if (!p4_ht_thread(cpu))
|
||||
cccr |= P4_CCCR_OVF_PMI_T0;
|
||||
else
|
||||
cccr |= P4_CCCR_OVF_PMI_T1;
|
||||
|
||||
return cccr;
|
||||
}
|
||||
|
||||
static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr)
|
||||
{
|
||||
u32 escr = 0;
|
||||
|
||||
if (!p4_ht_thread(cpu)) {
|
||||
if (!exclude_os)
|
||||
escr |= P4_ESCR_T0_OS;
|
||||
if (!exclude_usr)
|
||||
escr |= P4_ESCR_T0_USR;
|
||||
} else {
|
||||
if (!exclude_os)
|
||||
escr |= P4_ESCR_T1_OS;
|
||||
if (!exclude_usr)
|
||||
escr |= P4_ESCR_T1_USR;
|
||||
}
|
||||
|
||||
return escr;
|
||||
}
|
||||
|
||||
enum P4_EVENTS {
|
||||
P4_EVENT_TC_DELIVER_MODE,
|
||||
P4_EVENT_BPU_FETCH_REQUEST,
|
||||
P4_EVENT_ITLB_REFERENCE,
|
||||
P4_EVENT_MEMORY_CANCEL,
|
||||
P4_EVENT_MEMORY_COMPLETE,
|
||||
P4_EVENT_LOAD_PORT_REPLAY,
|
||||
P4_EVENT_STORE_PORT_REPLAY,
|
||||
P4_EVENT_MOB_LOAD_REPLAY,
|
||||
P4_EVENT_PAGE_WALK_TYPE,
|
||||
P4_EVENT_BSQ_CACHE_REFERENCE,
|
||||
P4_EVENT_IOQ_ALLOCATION,
|
||||
P4_EVENT_IOQ_ACTIVE_ENTRIES,
|
||||
P4_EVENT_FSB_DATA_ACTIVITY,
|
||||
P4_EVENT_BSQ_ALLOCATION,
|
||||
P4_EVENT_BSQ_ACTIVE_ENTRIES,
|
||||
P4_EVENT_SSE_INPUT_ASSIST,
|
||||
P4_EVENT_PACKED_SP_UOP,
|
||||
P4_EVENT_PACKED_DP_UOP,
|
||||
P4_EVENT_SCALAR_SP_UOP,
|
||||
P4_EVENT_SCALAR_DP_UOP,
|
||||
P4_EVENT_64BIT_MMX_UOP,
|
||||
P4_EVENT_128BIT_MMX_UOP,
|
||||
P4_EVENT_X87_FP_UOP,
|
||||
P4_EVENT_TC_MISC,
|
||||
P4_EVENT_GLOBAL_POWER_EVENTS,
|
||||
P4_EVENT_TC_MS_XFER,
|
||||
P4_EVENT_UOP_QUEUE_WRITES,
|
||||
P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE,
|
||||
P4_EVENT_RETIRED_BRANCH_TYPE,
|
||||
P4_EVENT_RESOURCE_STALL,
|
||||
P4_EVENT_WC_BUFFER,
|
||||
P4_EVENT_B2B_CYCLES,
|
||||
P4_EVENT_BNR,
|
||||
P4_EVENT_SNOOP,
|
||||
P4_EVENT_RESPONSE,
|
||||
P4_EVENT_FRONT_END_EVENT,
|
||||
P4_EVENT_EXECUTION_EVENT,
|
||||
P4_EVENT_REPLAY_EVENT,
|
||||
P4_EVENT_INSTR_RETIRED,
|
||||
P4_EVENT_UOPS_RETIRED,
|
||||
P4_EVENT_UOP_TYPE,
|
||||
P4_EVENT_BRANCH_RETIRED,
|
||||
P4_EVENT_MISPRED_BRANCH_RETIRED,
|
||||
P4_EVENT_X87_ASSIST,
|
||||
P4_EVENT_MACHINE_CLEAR,
|
||||
P4_EVENT_INSTR_COMPLETED,
|
||||
};
|
||||
|
||||
#define P4_OPCODE(event) event##_OPCODE
|
||||
#define P4_OPCODE_ESEL(opcode) ((opcode & 0x00ff) >> 0)
|
||||
#define P4_OPCODE_EVNT(opcode) ((opcode & 0xff00) >> 8)
|
||||
#define P4_OPCODE_PACK(event, sel) (((event) << 8) | sel)
|
||||
|
||||
/*
|
||||
* Comments below the event represent ESCR restriction
|
||||
* for this event and counter index per ESCR
|
||||
*
|
||||
* MSR_P4_IQ_ESCR0 and MSR_P4_IQ_ESCR1 are available only on early
|
||||
* processor builds (family 0FH, models 01H-02H). These MSRs
|
||||
* are not available on later versions, so that we don't use
|
||||
* them completely
|
||||
*
|
||||
* Also note that CCCR1 do not have P4_CCCR_ENABLE bit properly
|
||||
* working so that we should not use this CCCR and respective
|
||||
* counter as result
|
||||
*/
|
||||
enum P4_EVENT_OPCODES {
|
||||
P4_OPCODE(P4_EVENT_TC_DELIVER_MODE) = P4_OPCODE_PACK(0x01, 0x01),
|
||||
/*
|
||||
* MSR_P4_TC_ESCR0: 4, 5
|
||||
* MSR_P4_TC_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST) = P4_OPCODE_PACK(0x03, 0x00),
|
||||
/*
|
||||
* MSR_P4_BPU_ESCR0: 0, 1
|
||||
* MSR_P4_BPU_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_ITLB_REFERENCE) = P4_OPCODE_PACK(0x18, 0x03),
|
||||
/*
|
||||
* MSR_P4_ITLB_ESCR0: 0, 1
|
||||
* MSR_P4_ITLB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_MEMORY_CANCEL) = P4_OPCODE_PACK(0x02, 0x05),
|
||||
/*
|
||||
* MSR_P4_DAC_ESCR0: 8, 9
|
||||
* MSR_P4_DAC_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_MEMORY_COMPLETE) = P4_OPCODE_PACK(0x08, 0x02),
|
||||
/*
|
||||
* MSR_P4_SAAT_ESCR0: 8, 9
|
||||
* MSR_P4_SAAT_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY) = P4_OPCODE_PACK(0x04, 0x02),
|
||||
/*
|
||||
* MSR_P4_SAAT_ESCR0: 8, 9
|
||||
* MSR_P4_SAAT_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY) = P4_OPCODE_PACK(0x05, 0x02),
|
||||
/*
|
||||
* MSR_P4_SAAT_ESCR0: 8, 9
|
||||
* MSR_P4_SAAT_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY) = P4_OPCODE_PACK(0x03, 0x02),
|
||||
/*
|
||||
* MSR_P4_MOB_ESCR0: 0, 1
|
||||
* MSR_P4_MOB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE) = P4_OPCODE_PACK(0x01, 0x04),
|
||||
/*
|
||||
* MSR_P4_PMH_ESCR0: 0, 1
|
||||
* MSR_P4_PMH_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE) = P4_OPCODE_PACK(0x0c, 0x07),
|
||||
/*
|
||||
* MSR_P4_BSU_ESCR0: 0, 1
|
||||
* MSR_P4_BSU_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_IOQ_ALLOCATION) = P4_OPCODE_PACK(0x03, 0x06),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x1a, 0x06),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY) = P4_OPCODE_PACK(0x17, 0x06),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BSQ_ALLOCATION) = P4_OPCODE_PACK(0x05, 0x07),
|
||||
/*
|
||||
* MSR_P4_BSU_ESCR0: 0, 1
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x06, 0x07),
|
||||
/*
|
||||
* NOTE: no ESCR name in docs, it's guessed
|
||||
* MSR_P4_BSU_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST) = P4_OPCODE_PACK(0x34, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_PACKED_SP_UOP) = P4_OPCODE_PACK(0x08, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_PACKED_DP_UOP) = P4_OPCODE_PACK(0x0c, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_SCALAR_SP_UOP) = P4_OPCODE_PACK(0x0a, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_SCALAR_DP_UOP) = P4_OPCODE_PACK(0x0e, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_64BIT_MMX_UOP) = P4_OPCODE_PACK(0x02, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_128BIT_MMX_UOP) = P4_OPCODE_PACK(0x1a, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_X87_FP_UOP) = P4_OPCODE_PACK(0x04, 0x01),
|
||||
/*
|
||||
* MSR_P4_FIRM_ESCR0: 8, 9
|
||||
* MSR_P4_FIRM_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_TC_MISC) = P4_OPCODE_PACK(0x06, 0x01),
|
||||
/*
|
||||
* MSR_P4_TC_ESCR0: 4, 5
|
||||
* MSR_P4_TC_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS) = P4_OPCODE_PACK(0x13, 0x06),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_TC_MS_XFER) = P4_OPCODE_PACK(0x05, 0x00),
|
||||
/*
|
||||
* MSR_P4_MS_ESCR0: 4, 5
|
||||
* MSR_P4_MS_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES) = P4_OPCODE_PACK(0x09, 0x00),
|
||||
/*
|
||||
* MSR_P4_MS_ESCR0: 4, 5
|
||||
* MSR_P4_MS_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x05, 0x02),
|
||||
/*
|
||||
* MSR_P4_TBPU_ESCR0: 4, 5
|
||||
* MSR_P4_TBPU_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x04, 0x02),
|
||||
/*
|
||||
* MSR_P4_TBPU_ESCR0: 4, 5
|
||||
* MSR_P4_TBPU_ESCR1: 6, 7
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_RESOURCE_STALL) = P4_OPCODE_PACK(0x01, 0x01),
|
||||
/*
|
||||
* MSR_P4_ALF_ESCR0: 12, 13, 16
|
||||
* MSR_P4_ALF_ESCR1: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_WC_BUFFER) = P4_OPCODE_PACK(0x05, 0x05),
|
||||
/*
|
||||
* MSR_P4_DAC_ESCR0: 8, 9
|
||||
* MSR_P4_DAC_ESCR1: 10, 11
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_B2B_CYCLES) = P4_OPCODE_PACK(0x16, 0x03),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BNR) = P4_OPCODE_PACK(0x08, 0x03),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_SNOOP) = P4_OPCODE_PACK(0x06, 0x03),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_RESPONSE) = P4_OPCODE_PACK(0x04, 0x03),
|
||||
/*
|
||||
* MSR_P4_FSB_ESCR0: 0, 1
|
||||
* MSR_P4_FSB_ESCR1: 2, 3
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_FRONT_END_EVENT) = P4_OPCODE_PACK(0x08, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_EXECUTION_EVENT) = P4_OPCODE_PACK(0x0c, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_REPLAY_EVENT) = P4_OPCODE_PACK(0x09, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_INSTR_RETIRED) = P4_OPCODE_PACK(0x02, 0x04),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR0: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR1: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_UOPS_RETIRED) = P4_OPCODE_PACK(0x01, 0x04),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR0: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR1: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_UOP_TYPE) = P4_OPCODE_PACK(0x02, 0x02),
|
||||
/*
|
||||
* MSR_P4_RAT_ESCR0: 12, 13, 16
|
||||
* MSR_P4_RAT_ESCR1: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_BRANCH_RETIRED) = P4_OPCODE_PACK(0x06, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED) = P4_OPCODE_PACK(0x03, 0x04),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR0: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR1: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_X87_ASSIST) = P4_OPCODE_PACK(0x03, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_MACHINE_CLEAR) = P4_OPCODE_PACK(0x02, 0x05),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR2: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR3: 14, 15, 17
|
||||
*/
|
||||
|
||||
P4_OPCODE(P4_EVENT_INSTR_COMPLETED) = P4_OPCODE_PACK(0x07, 0x04),
|
||||
/*
|
||||
* MSR_P4_CRU_ESCR0: 12, 13, 16
|
||||
* MSR_P4_CRU_ESCR1: 14, 15, 17
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* a caller should use P4_ESCR_EMASK_NAME helper to
|
||||
* pick the EventMask needed, for example
|
||||
*
|
||||
* P4_ESCR_EMASK_NAME(P4_EVENT_TC_DELIVER_MODE, DD)
|
||||
*/
|
||||
enum P4_ESCR_EMASKS {
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DD, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DB, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DI, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BD, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BB, 4),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BI, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, ID, 6),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BPU_FETCH_REQUEST, TCMISS, 0),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, MISS, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT_UK, 2),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, ST_RB_FULL, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, 64K_CONF, 3),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, LSC, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, SSC, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_LOAD_PORT_REPLAY, SPLIT_LD, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_STORE_PORT_REPLAY, SPLIT_ST, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STA, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STD, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, PARTIAL_DATA, 4),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, UNALGN_ADDR, 5),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, DTMISS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, ITMISS, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE, 4),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS, 8),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS, 9),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS, 10),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, DEFAULT, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_READ, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_WRITE, 6),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_UC, 7),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WC, 8),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WT, 9),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WP, 10),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WB, 11),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OWN, 13),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OTHER, 14),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, PREFETCH, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, DEFAULT, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_READ, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_WRITE, 6),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_UC, 7),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WC, 8),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WT, 9),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WP, 10),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WB, 11),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OWN, 13),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OTHER, 14),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, PREFETCH, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OTHER, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_DRV, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OWN, 4),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OTHER, 5),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE0, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE1, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN0, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN1, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_IO_TYPE, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LOCK_TYPE, 6),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_CACHE_TYPE, 7),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_SPLIT_TYPE, 8),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_DEM_TYPE, 9),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_ORD_TYPE, 10),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE0, 11),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE1, 12),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE2, 13),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE0, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE1, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN0, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN1, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_IO_TYPE, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LOCK_TYPE, 6),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_CACHE_TYPE, 7),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_SPLIT_TYPE, 8),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_DEM_TYPE, 9),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_ORD_TYPE, 10),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE0, 11),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE1, 12),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE2, 13),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_SSE_INPUT_ASSIST, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_SP_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_DP_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_SP_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_DP_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_64BIT_MMX_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_128BIT_MMX_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_FP_UOP, ALL, 15),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_MISC, FLUSH, 4),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING, 0),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_TC_MS_XFER, CISC, 0),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_BUILD, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_DELIVER, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_ROM, 2),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CONDITIONAL, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CALL, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, RETURN, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, INDIRECT, 4),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CALL, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT, 4),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_RESOURCE_STALL, SBFULL, 5),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_EVICTS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_FULL_EVICTS, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, NBOGUS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, BOGUS, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS0, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS1, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS2, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS3, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS0, 4),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS1, 5),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS2, 6),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS3, 7),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, NBOGUS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, BOGUS, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSTAG, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSNTAG, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSTAG, 3),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, NBOGUS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, BOGUS, 1),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGLOADS, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGSTORES, 2),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNP, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNM, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTP, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTM, 3),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS, 0),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSU, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSO, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAO, 2),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAU, 3),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, PREA, 4),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, CLEAR, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, MOCLEAR, 1),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, SMCLEAR, 2),
|
||||
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, NBOGUS, 0),
|
||||
P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, BOGUS, 1),
|
||||
};
|
||||
|
||||
/* P4 PEBS: stale for a while */
|
||||
#define P4_PEBS_METRIC_MASK 0x00001fffU
|
||||
#define P4_PEBS_UOB_TAG 0x01000000U
|
||||
#define P4_PEBS_ENABLE 0x02000000U
|
||||
|
||||
/* Replay metrics for MSR_IA32_PEBS_ENABLE and MSR_P4_PEBS_MATRIX_VERT */
|
||||
#define P4_PEBS__1stl_cache_load_miss_retired 0x3000001
|
||||
#define P4_PEBS__2ndl_cache_load_miss_retired 0x3000002
|
||||
#define P4_PEBS__dtlb_load_miss_retired 0x3000004
|
||||
#define P4_PEBS__dtlb_store_miss_retired 0x3000004
|
||||
#define P4_PEBS__dtlb_all_miss_retired 0x3000004
|
||||
#define P4_PEBS__tagged_mispred_branch 0x3018000
|
||||
#define P4_PEBS__mob_load_replay_retired 0x3000200
|
||||
#define P4_PEBS__split_load_retired 0x3000400
|
||||
#define P4_PEBS__split_store_retired 0x3000400
|
||||
|
||||
#define P4_VERT__1stl_cache_load_miss_retired 0x0000001
|
||||
#define P4_VERT__2ndl_cache_load_miss_retired 0x0000001
|
||||
#define P4_VERT__dtlb_load_miss_retired 0x0000001
|
||||
#define P4_VERT__dtlb_store_miss_retired 0x0000002
|
||||
#define P4_VERT__dtlb_all_miss_retired 0x0000003
|
||||
#define P4_VERT__tagged_mispred_branch 0x0000010
|
||||
#define P4_VERT__mob_load_replay_retired 0x0000001
|
||||
#define P4_VERT__split_load_retired 0x0000001
|
||||
#define P4_VERT__split_store_retired 0x0000002
|
||||
|
||||
enum P4_CACHE_EVENTS {
|
||||
P4_CACHE__NONE,
|
||||
|
||||
P4_CACHE__1stl_cache_load_miss_retired,
|
||||
P4_CACHE__2ndl_cache_load_miss_retired,
|
||||
P4_CACHE__dtlb_load_miss_retired,
|
||||
P4_CACHE__dtlb_store_miss_retired,
|
||||
P4_CACHE__itlb_reference_hit,
|
||||
P4_CACHE__itlb_reference_miss,
|
||||
|
||||
P4_CACHE__MAX
|
||||
};
|
||||
|
||||
#endif /* PERF_EVENT_P4_H */
|
@ -21,7 +21,6 @@ struct mm_struct;
|
||||
#include <asm/msr.h>
|
||||
#include <asm/desc_defs.h>
|
||||
#include <asm/nops.h>
|
||||
#include <asm/ds.h>
|
||||
|
||||
#include <linux/personality.h>
|
||||
#include <linux/cpumask.h>
|
||||
@ -29,6 +28,7 @@ struct mm_struct;
|
||||
#include <linux/threads.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#define HBP_NUM 4
|
||||
/*
|
||||
@ -473,10 +473,6 @@ struct thread_struct {
|
||||
unsigned long iopl;
|
||||
/* Max allowed port in the bitmap, in bytes: */
|
||||
unsigned io_bitmap_max;
|
||||
/* MSR_IA32_DEBUGCTLMSR value to switch in if TIF_DEBUGCTLMSR is set. */
|
||||
unsigned long debugctlmsr;
|
||||
/* Debug Store context; see asm/ds.h */
|
||||
struct ds_context *ds_ctx;
|
||||
};
|
||||
|
||||
static inline unsigned long native_get_debugreg(int regno)
|
||||
@ -803,7 +799,7 @@ extern void cpu_init(void);
|
||||
|
||||
static inline unsigned long get_debugctlmsr(void)
|
||||
{
|
||||
unsigned long debugctlmsr = 0;
|
||||
unsigned long debugctlmsr = 0;
|
||||
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
@ -811,21 +807,6 @@ static inline unsigned long get_debugctlmsr(void)
|
||||
#endif
|
||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
||||
|
||||
return debugctlmsr;
|
||||
}
|
||||
|
||||
static inline unsigned long get_debugctlmsr_on_cpu(int cpu)
|
||||
{
|
||||
u64 debugctlmsr = 0;
|
||||
u32 val1, val2;
|
||||
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
return 0;
|
||||
#endif
|
||||
rdmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, &val1, &val2);
|
||||
debugctlmsr = val1 | ((u64)val2 << 32);
|
||||
|
||||
return debugctlmsr;
|
||||
}
|
||||
|
||||
@ -838,18 +819,6 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr)
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
||||
}
|
||||
|
||||
static inline void update_debugctlmsr_on_cpu(int cpu,
|
||||
unsigned long debugctlmsr)
|
||||
{
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
return;
|
||||
#endif
|
||||
wrmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR,
|
||||
(u32)((u64)debugctlmsr),
|
||||
(u32)((u64)debugctlmsr >> 32));
|
||||
}
|
||||
|
||||
/*
|
||||
* from system description table in BIOS. Mostly for MCA use, but
|
||||
* others may find it useful:
|
||||
|
@ -82,61 +82,6 @@
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/types.h>
|
||||
|
||||
/* configuration/status structure used in PTRACE_BTS_CONFIG and
|
||||
PTRACE_BTS_STATUS commands.
|
||||
*/
|
||||
struct ptrace_bts_config {
|
||||
/* requested or actual size of BTS buffer in bytes */
|
||||
__u32 size;
|
||||
/* bitmask of below flags */
|
||||
__u32 flags;
|
||||
/* buffer overflow signal */
|
||||
__u32 signal;
|
||||
/* actual size of bts_struct in bytes */
|
||||
__u32 bts_size;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#define PTRACE_BTS_O_TRACE 0x1 /* branch trace */
|
||||
#define PTRACE_BTS_O_SCHED 0x2 /* scheduling events w/ jiffies */
|
||||
#define PTRACE_BTS_O_SIGNAL 0x4 /* send SIG<signal> on buffer overflow
|
||||
instead of wrapping around */
|
||||
#define PTRACE_BTS_O_ALLOC 0x8 /* (re)allocate buffer */
|
||||
|
||||
#define PTRACE_BTS_CONFIG 40
|
||||
/* Configure branch trace recording.
|
||||
ADDR points to a struct ptrace_bts_config.
|
||||
DATA gives the size of that buffer.
|
||||
A new buffer is allocated, if requested in the flags.
|
||||
An overflow signal may only be requested for new buffers.
|
||||
Returns the number of bytes read.
|
||||
*/
|
||||
#define PTRACE_BTS_STATUS 41
|
||||
/* Return the current configuration in a struct ptrace_bts_config
|
||||
pointed to by ADDR; DATA gives the size of that buffer.
|
||||
Returns the number of bytes written.
|
||||
*/
|
||||
#define PTRACE_BTS_SIZE 42
|
||||
/* Return the number of available BTS records for draining.
|
||||
DATA and ADDR are ignored.
|
||||
*/
|
||||
#define PTRACE_BTS_GET 43
|
||||
/* Get a single BTS record.
|
||||
DATA defines the index into the BTS array, where 0 is the newest
|
||||
entry, and higher indices refer to older entries.
|
||||
ADDR is pointing to struct bts_struct (see asm/ds.h).
|
||||
*/
|
||||
#define PTRACE_BTS_CLEAR 44
|
||||
/* Clear the BTS buffer.
|
||||
DATA and ADDR are ignored.
|
||||
*/
|
||||
#define PTRACE_BTS_DRAIN 45
|
||||
/* Read all available BTS records and clear the buffer.
|
||||
ADDR points to an array of struct bts_struct.
|
||||
DATA gives the size of that buffer.
|
||||
BTS records are read from oldest to newest.
|
||||
Returns number of BTS records drained.
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_X86_PTRACE_ABI_H */
|
||||
|
@ -289,12 +289,6 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
|
||||
extern int do_set_thread_area(struct task_struct *p, int idx,
|
||||
struct user_desc __user *info, int can_allocate);
|
||||
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
extern void ptrace_bts_untrace(struct task_struct *tsk);
|
||||
|
||||
#define arch_ptrace_untrace(tsk) ptrace_bts_untrace(tsk)
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
@ -92,8 +92,7 @@ struct thread_info {
|
||||
#define TIF_IO_BITMAP 22 /* uses I/O bitmap */
|
||||
#define TIF_FREEZE 23 /* is freezing for suspend */
|
||||
#define TIF_FORCED_TF 24 /* true if TF in eflags artificially */
|
||||
#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
|
||||
#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
|
||||
#define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */
|
||||
#define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */
|
||||
#define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */
|
||||
|
||||
@ -115,8 +114,7 @@ struct thread_info {
|
||||
#define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP)
|
||||
#define _TIF_FREEZE (1 << TIF_FREEZE)
|
||||
#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
|
||||
#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
|
||||
#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
|
||||
#define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP)
|
||||
#define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES)
|
||||
#define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT)
|
||||
|
||||
@ -147,7 +145,7 @@ struct thread_info {
|
||||
|
||||
/* flags to check in __switch_to() */
|
||||
#define _TIF_WORK_CTXSW \
|
||||
(_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC)
|
||||
(_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP)
|
||||
|
||||
#define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY)
|
||||
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
|
||||
|
@ -47,8 +47,6 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
|
||||
obj-y += process.o
|
||||
obj-y += i387.o xsave.o
|
||||
obj-y += ptrace.o
|
||||
obj-$(CONFIG_X86_DS) += ds.o
|
||||
obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o
|
||||
obj-$(CONFIG_X86_32) += tls.o
|
||||
obj-$(CONFIG_IA32_EMULATION) += tls.o
|
||||
obj-y += step.o
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/ds.h>
|
||||
#include <asm/bugs.h>
|
||||
#include <asm/cpu.h>
|
||||
|
||||
@ -388,7 +387,6 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c)
|
||||
set_cpu_cap(c, X86_FEATURE_BTS);
|
||||
if (!(l1 & (1<<12)))
|
||||
set_cpu_cap(c, X86_FEATURE_PEBS);
|
||||
ds_init_intel(c);
|
||||
}
|
||||
|
||||
if (c->x86 == 6 && c->x86_model == 29 && cpu_has_clflush)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(amd_nb_lock);
|
||||
|
||||
static __initconst u64 amd_hw_cache_event_ids
|
||||
static __initconst const u64 amd_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
@ -111,22 +111,19 @@ static u64 amd_pmu_event_map(int hw_event)
|
||||
return amd_perfmon_event_map[hw_event];
|
||||
}
|
||||
|
||||
static u64 amd_pmu_raw_event(u64 hw_event)
|
||||
static int amd_pmu_hw_config(struct perf_event *event)
|
||||
{
|
||||
#define K7_EVNTSEL_EVENT_MASK 0xF000000FFULL
|
||||
#define K7_EVNTSEL_UNIT_MASK 0x00000FF00ULL
|
||||
#define K7_EVNTSEL_EDGE_MASK 0x000040000ULL
|
||||
#define K7_EVNTSEL_INV_MASK 0x000800000ULL
|
||||
#define K7_EVNTSEL_REG_MASK 0x0FF000000ULL
|
||||
int ret = x86_pmu_hw_config(event);
|
||||
|
||||
#define K7_EVNTSEL_MASK \
|
||||
(K7_EVNTSEL_EVENT_MASK | \
|
||||
K7_EVNTSEL_UNIT_MASK | \
|
||||
K7_EVNTSEL_EDGE_MASK | \
|
||||
K7_EVNTSEL_INV_MASK | \
|
||||
K7_EVNTSEL_REG_MASK)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return hw_event & K7_EVNTSEL_MASK;
|
||||
if (event->attr.type != PERF_TYPE_RAW)
|
||||
return 0;
|
||||
|
||||
event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -165,7 +162,7 @@ static void amd_put_event_constraints(struct cpu_hw_events *cpuc,
|
||||
* be removed on one CPU at a time AND PMU is disabled
|
||||
* when we come here
|
||||
*/
|
||||
for (i = 0; i < x86_pmu.num_events; i++) {
|
||||
for (i = 0; i < x86_pmu.num_counters; i++) {
|
||||
if (nb->owners[i] == event) {
|
||||
cmpxchg(nb->owners+i, event, NULL);
|
||||
break;
|
||||
@ -215,7 +212,7 @@ amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event)
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
struct amd_nb *nb = cpuc->amd_nb;
|
||||
struct perf_event *old = NULL;
|
||||
int max = x86_pmu.num_events;
|
||||
int max = x86_pmu.num_counters;
|
||||
int i, j, k = -1;
|
||||
|
||||
/*
|
||||
@ -293,7 +290,7 @@ static struct amd_nb *amd_alloc_nb(int cpu, int nb_id)
|
||||
/*
|
||||
* initialize all possible NB constraints
|
||||
*/
|
||||
for (i = 0; i < x86_pmu.num_events; i++) {
|
||||
for (i = 0; i < x86_pmu.num_counters; i++) {
|
||||
__set_bit(i, nb->event_constraints[i].idxmsk);
|
||||
nb->event_constraints[i].weight = 1;
|
||||
}
|
||||
@ -371,21 +368,22 @@ static void amd_pmu_cpu_dead(int cpu)
|
||||
raw_spin_unlock(&amd_nb_lock);
|
||||
}
|
||||
|
||||
static __initconst struct x86_pmu amd_pmu = {
|
||||
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,
|
||||
.raw_event = amd_pmu_raw_event,
|
||||
.max_events = ARRAY_SIZE(amd_perfmon_event_map),
|
||||
.num_events = 4,
|
||||
.event_bits = 48,
|
||||
.event_mask = (1ULL << 48) - 1,
|
||||
.num_counters = 4,
|
||||
.cntval_bits = 48,
|
||||
.cntval_mask = (1ULL << 48) - 1,
|
||||
.apic = 1,
|
||||
/* use highest bit to detect overflow */
|
||||
.max_period = (1ULL << 47) - 1,
|
||||
|
@ -88,7 +88,7 @@ static u64 intel_pmu_event_map(int hw_event)
|
||||
return intel_perfmon_event_map[hw_event];
|
||||
}
|
||||
|
||||
static __initconst u64 westmere_hw_cache_event_ids
|
||||
static __initconst const u64 westmere_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
@ -179,7 +179,7 @@ static __initconst u64 westmere_hw_cache_event_ids
|
||||
},
|
||||
};
|
||||
|
||||
static __initconst u64 nehalem_hw_cache_event_ids
|
||||
static __initconst const u64 nehalem_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
@ -270,7 +270,7 @@ static __initconst u64 nehalem_hw_cache_event_ids
|
||||
},
|
||||
};
|
||||
|
||||
static __initconst u64 core2_hw_cache_event_ids
|
||||
static __initconst const u64 core2_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
@ -361,7 +361,7 @@ static __initconst u64 core2_hw_cache_event_ids
|
||||
},
|
||||
};
|
||||
|
||||
static __initconst u64 atom_hw_cache_event_ids
|
||||
static __initconst const u64 atom_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
@ -452,60 +452,6 @@ static __initconst u64 atom_hw_cache_event_ids
|
||||
},
|
||||
};
|
||||
|
||||
static u64 intel_pmu_raw_event(u64 hw_event)
|
||||
{
|
||||
#define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL
|
||||
#define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL
|
||||
#define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL
|
||||
#define CORE_EVNTSEL_INV_MASK 0x00800000ULL
|
||||
#define CORE_EVNTSEL_REG_MASK 0xFF000000ULL
|
||||
|
||||
#define CORE_EVNTSEL_MASK \
|
||||
(INTEL_ARCH_EVTSEL_MASK | \
|
||||
INTEL_ARCH_UNIT_MASK | \
|
||||
INTEL_ARCH_EDGE_MASK | \
|
||||
INTEL_ARCH_INV_MASK | \
|
||||
INTEL_ARCH_CNT_MASK)
|
||||
|
||||
return hw_event & CORE_EVNTSEL_MASK;
|
||||
}
|
||||
|
||||
static void intel_pmu_enable_bts(u64 config)
|
||||
{
|
||||
unsigned long debugctlmsr;
|
||||
|
||||
debugctlmsr = get_debugctlmsr();
|
||||
|
||||
debugctlmsr |= X86_DEBUGCTL_TR;
|
||||
debugctlmsr |= X86_DEBUGCTL_BTS;
|
||||
debugctlmsr |= X86_DEBUGCTL_BTINT;
|
||||
|
||||
if (!(config & ARCH_PERFMON_EVENTSEL_OS))
|
||||
debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS;
|
||||
|
||||
if (!(config & ARCH_PERFMON_EVENTSEL_USR))
|
||||
debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR;
|
||||
|
||||
update_debugctlmsr(debugctlmsr);
|
||||
}
|
||||
|
||||
static void intel_pmu_disable_bts(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
unsigned long debugctlmsr;
|
||||
|
||||
if (!cpuc->ds)
|
||||
return;
|
||||
|
||||
debugctlmsr = get_debugctlmsr();
|
||||
|
||||
debugctlmsr &=
|
||||
~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT |
|
||||
X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR);
|
||||
|
||||
update_debugctlmsr(debugctlmsr);
|
||||
}
|
||||
|
||||
static void intel_pmu_disable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
@ -514,12 +460,17 @@ static void intel_pmu_disable_all(void)
|
||||
|
||||
if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask))
|
||||
intel_pmu_disable_bts();
|
||||
|
||||
intel_pmu_pebs_disable_all();
|
||||
intel_pmu_lbr_disable_all();
|
||||
}
|
||||
|
||||
static void intel_pmu_enable_all(void)
|
||||
static void intel_pmu_enable_all(int added)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
intel_pmu_pebs_enable_all();
|
||||
intel_pmu_lbr_enable_all();
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
|
||||
|
||||
if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) {
|
||||
@ -533,6 +484,42 @@ static void intel_pmu_enable_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Workaround for:
|
||||
* Intel Errata AAK100 (model 26)
|
||||
* Intel Errata AAP53 (model 30)
|
||||
* Intel Errata BD53 (model 44)
|
||||
*
|
||||
* These chips need to be 'reset' when adding counters by programming
|
||||
* the magic three (non counting) events 0x4300D2, 0x4300B1 and 0x4300B5
|
||||
* either in sequence on the same PMC or on different PMCs.
|
||||
*/
|
||||
static void intel_pmu_nhm_enable_all(int added)
|
||||
{
|
||||
if (added) {
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
int i;
|
||||
|
||||
wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 0, 0x4300D2);
|
||||
wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 1, 0x4300B1);
|
||||
wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 2, 0x4300B5);
|
||||
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x3);
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
struct perf_event *event = cpuc->events[i];
|
||||
|
||||
if (!event)
|
||||
continue;
|
||||
|
||||
__x86_pmu_enable_event(&event->hw,
|
||||
ARCH_PERFMON_EVENTSEL_ENABLE);
|
||||
}
|
||||
}
|
||||
intel_pmu_enable_all(added);
|
||||
}
|
||||
|
||||
static inline u64 intel_pmu_get_status(void)
|
||||
{
|
||||
u64 status;
|
||||
@ -547,8 +534,7 @@ static inline void intel_pmu_ack_status(u64 ack)
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
|
||||
}
|
||||
|
||||
static inline void
|
||||
intel_pmu_disable_fixed(struct hw_perf_event *hwc)
|
||||
static void intel_pmu_disable_fixed(struct hw_perf_event *hwc)
|
||||
{
|
||||
int idx = hwc->idx - X86_PMC_IDX_FIXED;
|
||||
u64 ctrl_val, mask;
|
||||
@ -557,71 +543,10 @@ intel_pmu_disable_fixed(struct hw_perf_event *hwc)
|
||||
|
||||
rdmsrl(hwc->config_base, ctrl_val);
|
||||
ctrl_val &= ~mask;
|
||||
(void)checking_wrmsrl(hwc->config_base, ctrl_val);
|
||||
wrmsrl(hwc->config_base, ctrl_val);
|
||||
}
|
||||
|
||||
static void intel_pmu_drain_bts_buffer(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct debug_store *ds = cpuc->ds;
|
||||
struct bts_record {
|
||||
u64 from;
|
||||
u64 to;
|
||||
u64 flags;
|
||||
};
|
||||
struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS];
|
||||
struct bts_record *at, *top;
|
||||
struct perf_output_handle handle;
|
||||
struct perf_event_header header;
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs regs;
|
||||
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
at = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
|
||||
top = (struct bts_record *)(unsigned long)ds->bts_index;
|
||||
|
||||
if (top <= at)
|
||||
return;
|
||||
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
|
||||
perf_sample_data_init(&data, 0);
|
||||
|
||||
data.period = event->hw.last_period;
|
||||
regs.ip = 0;
|
||||
|
||||
/*
|
||||
* Prepare a generic sample, i.e. fill in the invariant fields.
|
||||
* We will overwrite the from and to address before we output
|
||||
* the sample.
|
||||
*/
|
||||
perf_prepare_sample(&header, &data, event, ®s);
|
||||
|
||||
if (perf_output_begin(&handle, event,
|
||||
header.size * (top - at), 1, 1))
|
||||
return;
|
||||
|
||||
for (; at < top; at++) {
|
||||
data.ip = at->from;
|
||||
data.addr = at->to;
|
||||
|
||||
perf_output_sample(&handle, &header, &data, event);
|
||||
}
|
||||
|
||||
perf_output_end(&handle);
|
||||
|
||||
/* There's new data available. */
|
||||
event->hw.interrupts++;
|
||||
event->pending_kill = POLL_IN;
|
||||
}
|
||||
|
||||
static inline void
|
||||
intel_pmu_disable_event(struct perf_event *event)
|
||||
static void intel_pmu_disable_event(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
@ -637,14 +562,15 @@ intel_pmu_disable_event(struct perf_event *event)
|
||||
}
|
||||
|
||||
x86_pmu_disable_event(event);
|
||||
|
||||
if (unlikely(event->attr.precise_ip))
|
||||
intel_pmu_pebs_disable(event);
|
||||
}
|
||||
|
||||
static inline void
|
||||
intel_pmu_enable_fixed(struct hw_perf_event *hwc)
|
||||
static void intel_pmu_enable_fixed(struct hw_perf_event *hwc)
|
||||
{
|
||||
int idx = hwc->idx - X86_PMC_IDX_FIXED;
|
||||
u64 ctrl_val, bits, mask;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Enable IRQ generation (0x8),
|
||||
@ -669,7 +595,7 @@ intel_pmu_enable_fixed(struct hw_perf_event *hwc)
|
||||
rdmsrl(hwc->config_base, ctrl_val);
|
||||
ctrl_val &= ~mask;
|
||||
ctrl_val |= bits;
|
||||
err = checking_wrmsrl(hwc->config_base, ctrl_val);
|
||||
wrmsrl(hwc->config_base, ctrl_val);
|
||||
}
|
||||
|
||||
static void intel_pmu_enable_event(struct perf_event *event)
|
||||
@ -689,7 +615,10 @@ static void intel_pmu_enable_event(struct perf_event *event)
|
||||
return;
|
||||
}
|
||||
|
||||
__x86_pmu_enable_event(hwc);
|
||||
if (unlikely(event->attr.precise_ip))
|
||||
intel_pmu_pebs_enable(event);
|
||||
|
||||
__x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -708,20 +637,20 @@ static void intel_pmu_reset(void)
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
|
||||
if (!x86_pmu.num_events)
|
||||
if (!x86_pmu.num_counters)
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
printk("clearing PMU state on CPU#%d\n", smp_processor_id());
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_events; idx++) {
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
checking_wrmsrl(x86_pmu.eventsel + idx, 0ull);
|
||||
checking_wrmsrl(x86_pmu.perfctr + idx, 0ull);
|
||||
}
|
||||
for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) {
|
||||
for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++)
|
||||
checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull);
|
||||
}
|
||||
|
||||
if (ds)
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
|
||||
@ -747,7 +676,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
|
||||
intel_pmu_drain_bts_buffer();
|
||||
status = intel_pmu_get_status();
|
||||
if (!status) {
|
||||
intel_pmu_enable_all();
|
||||
intel_pmu_enable_all(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -762,6 +691,15 @@ again:
|
||||
|
||||
inc_irq_stat(apic_perf_irqs);
|
||||
ack = status;
|
||||
|
||||
intel_pmu_lbr_read();
|
||||
|
||||
/*
|
||||
* PEBS overflow sets bit 62 in the global status register
|
||||
*/
|
||||
if (__test_and_clear_bit(62, (unsigned long *)&status))
|
||||
x86_pmu.drain_pebs(regs);
|
||||
|
||||
for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
|
||||
struct perf_event *event = cpuc->events[bit];
|
||||
|
||||
@ -787,26 +725,22 @@ again:
|
||||
goto again;
|
||||
|
||||
done:
|
||||
intel_pmu_enable_all();
|
||||
intel_pmu_enable_all(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct event_constraint bts_constraint =
|
||||
EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0);
|
||||
|
||||
static struct event_constraint *
|
||||
intel_special_constraints(struct perf_event *event)
|
||||
intel_bts_constraints(struct perf_event *event)
|
||||
{
|
||||
unsigned int hw_event;
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
unsigned int hw_event, bts_event;
|
||||
|
||||
hw_event = event->hw.config & INTEL_ARCH_EVENT_MASK;
|
||||
|
||||
if (unlikely((hw_event ==
|
||||
x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) &&
|
||||
(event->hw.sample_period == 1))) {
|
||||
hw_event = hwc->config & INTEL_ARCH_EVENT_MASK;
|
||||
bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
|
||||
|
||||
if (unlikely(hw_event == bts_event && hwc->sample_period == 1))
|
||||
return &bts_constraint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -815,24 +749,53 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event
|
||||
{
|
||||
struct event_constraint *c;
|
||||
|
||||
c = intel_special_constraints(event);
|
||||
c = intel_bts_constraints(event);
|
||||
if (c)
|
||||
return c;
|
||||
|
||||
c = intel_pebs_constraints(event);
|
||||
if (c)
|
||||
return c;
|
||||
|
||||
return x86_get_event_constraints(cpuc, event);
|
||||
}
|
||||
|
||||
static __initconst struct x86_pmu core_pmu = {
|
||||
static int intel_pmu_hw_config(struct perf_event *event)
|
||||
{
|
||||
int ret = x86_pmu_hw_config(event);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (event->attr.type != PERF_TYPE_RAW)
|
||||
return 0;
|
||||
|
||||
if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY))
|
||||
return 0;
|
||||
|
||||
if (x86_pmu.version < 3)
|
||||
return -EINVAL;
|
||||
|
||||
if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __initconst const struct x86_pmu core_pmu = {
|
||||
.name = "core",
|
||||
.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 = x86_pmu_hw_config,
|
||||
.schedule_events = x86_schedule_events,
|
||||
.eventsel = MSR_ARCH_PERFMON_EVENTSEL0,
|
||||
.perfctr = MSR_ARCH_PERFMON_PERFCTR0,
|
||||
.event_map = intel_pmu_event_map,
|
||||
.raw_event = intel_pmu_raw_event,
|
||||
.max_events = ARRAY_SIZE(intel_perfmon_event_map),
|
||||
.apic = 1,
|
||||
/*
|
||||
@ -845,17 +808,32 @@ static __initconst struct x86_pmu core_pmu = {
|
||||
.event_constraints = intel_core_event_constraints,
|
||||
};
|
||||
|
||||
static __initconst struct x86_pmu intel_pmu = {
|
||||
static void intel_pmu_cpu_starting(int cpu)
|
||||
{
|
||||
init_debug_store_on_cpu(cpu);
|
||||
/*
|
||||
* Deal with CPUs that don't clear their LBRs on power-up.
|
||||
*/
|
||||
intel_pmu_lbr_reset();
|
||||
}
|
||||
|
||||
static void intel_pmu_cpu_dying(int cpu)
|
||||
{
|
||||
fini_debug_store_on_cpu(cpu);
|
||||
}
|
||||
|
||||
static __initconst const struct x86_pmu intel_pmu = {
|
||||
.name = "Intel",
|
||||
.handle_irq = intel_pmu_handle_irq,
|
||||
.disable_all = intel_pmu_disable_all,
|
||||
.enable_all = intel_pmu_enable_all,
|
||||
.enable = intel_pmu_enable_event,
|
||||
.disable = intel_pmu_disable_event,
|
||||
.hw_config = intel_pmu_hw_config,
|
||||
.schedule_events = x86_schedule_events,
|
||||
.eventsel = MSR_ARCH_PERFMON_EVENTSEL0,
|
||||
.perfctr = MSR_ARCH_PERFMON_PERFCTR0,
|
||||
.event_map = intel_pmu_event_map,
|
||||
.raw_event = intel_pmu_raw_event,
|
||||
.max_events = ARRAY_SIZE(intel_perfmon_event_map),
|
||||
.apic = 1,
|
||||
/*
|
||||
@ -864,14 +842,38 @@ static __initconst struct x86_pmu intel_pmu = {
|
||||
* the generic event period:
|
||||
*/
|
||||
.max_period = (1ULL << 31) - 1,
|
||||
.enable_bts = intel_pmu_enable_bts,
|
||||
.disable_bts = intel_pmu_disable_bts,
|
||||
.get_event_constraints = intel_get_event_constraints,
|
||||
|
||||
.cpu_starting = init_debug_store_on_cpu,
|
||||
.cpu_dying = fini_debug_store_on_cpu,
|
||||
.cpu_starting = intel_pmu_cpu_starting,
|
||||
.cpu_dying = intel_pmu_cpu_dying,
|
||||
};
|
||||
|
||||
static void intel_clovertown_quirks(void)
|
||||
{
|
||||
/*
|
||||
* PEBS is unreliable due to:
|
||||
*
|
||||
* AJ67 - PEBS may experience CPL leaks
|
||||
* AJ68 - PEBS PMI may be delayed by one event
|
||||
* AJ69 - GLOBAL_STATUS[62] will only be set when DEBUGCTL[12]
|
||||
* AJ106 - FREEZE_LBRS_ON_PMI doesn't work in combination with PEBS
|
||||
*
|
||||
* AJ67 could be worked around by restricting the OS/USR flags.
|
||||
* AJ69 could be worked around by setting PMU_FREEZE_ON_PMI.
|
||||
*
|
||||
* AJ106 could possibly be worked around by not allowing LBR
|
||||
* usage from PEBS, including the fixup.
|
||||
* AJ68 could possibly be worked around by always programming
|
||||
* a pebs_event_reset[0] value and coping with the lost events.
|
||||
*
|
||||
* But taken together it might just make sense to not enable PEBS on
|
||||
* these chips.
|
||||
*/
|
||||
printk(KERN_WARNING "PEBS disabled due to CPU errata.\n");
|
||||
x86_pmu.pebs = 0;
|
||||
x86_pmu.pebs_constraints = NULL;
|
||||
}
|
||||
|
||||
static __init int intel_pmu_init(void)
|
||||
{
|
||||
union cpuid10_edx edx;
|
||||
@ -881,12 +883,13 @@ static __init int intel_pmu_init(void)
|
||||
int version;
|
||||
|
||||
if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
|
||||
/* check for P6 processor family */
|
||||
if (boot_cpu_data.x86 == 6) {
|
||||
return p6_pmu_init();
|
||||
} else {
|
||||
switch (boot_cpu_data.x86) {
|
||||
case 0x6:
|
||||
return p6_pmu_init();
|
||||
case 0xf:
|
||||
return p4_pmu_init();
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -904,16 +907,28 @@ static __init int intel_pmu_init(void)
|
||||
x86_pmu = intel_pmu;
|
||||
|
||||
x86_pmu.version = version;
|
||||
x86_pmu.num_events = eax.split.num_events;
|
||||
x86_pmu.event_bits = eax.split.bit_width;
|
||||
x86_pmu.event_mask = (1ULL << eax.split.bit_width) - 1;
|
||||
x86_pmu.num_counters = eax.split.num_counters;
|
||||
x86_pmu.cntval_bits = eax.split.bit_width;
|
||||
x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1;
|
||||
|
||||
/*
|
||||
* Quirk: v2 perfmon does not report fixed-purpose events, so
|
||||
* assume at least 3 events:
|
||||
*/
|
||||
if (version > 1)
|
||||
x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3);
|
||||
x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3);
|
||||
|
||||
/*
|
||||
* v2 and above have a perf capabilities MSR
|
||||
*/
|
||||
if (version > 1) {
|
||||
u64 capabilities;
|
||||
|
||||
rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities);
|
||||
x86_pmu.intel_cap.capabilities = capabilities;
|
||||
}
|
||||
|
||||
intel_ds_init();
|
||||
|
||||
/*
|
||||
* Install the hw-cache-events table:
|
||||
@ -924,12 +939,15 @@ static __init int intel_pmu_init(void)
|
||||
break;
|
||||
|
||||
case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */
|
||||
x86_pmu.quirks = intel_clovertown_quirks;
|
||||
case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */
|
||||
case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */
|
||||
case 29: /* six-core 45 nm xeon "Dunnington" */
|
||||
memcpy(hw_cache_event_ids, core2_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
intel_pmu_lbr_init_core();
|
||||
|
||||
x86_pmu.event_constraints = intel_core2_event_constraints;
|
||||
pr_cont("Core2 events, ");
|
||||
break;
|
||||
@ -940,13 +958,19 @@ static __init int intel_pmu_init(void)
|
||||
memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
intel_pmu_lbr_init_nhm();
|
||||
|
||||
x86_pmu.event_constraints = intel_nehalem_event_constraints;
|
||||
pr_cont("Nehalem/Corei7 events, ");
|
||||
x86_pmu.enable_all = intel_pmu_nhm_enable_all;
|
||||
pr_cont("Nehalem events, ");
|
||||
break;
|
||||
|
||||
case 28: /* Atom */
|
||||
memcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
intel_pmu_lbr_init_atom();
|
||||
|
||||
x86_pmu.event_constraints = intel_gen_event_constraints;
|
||||
pr_cont("Atom events, ");
|
||||
break;
|
||||
@ -956,7 +980,10 @@ static __init int intel_pmu_init(void)
|
||||
memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
intel_pmu_lbr_init_nhm();
|
||||
|
||||
x86_pmu.event_constraints = intel_westmere_event_constraints;
|
||||
x86_pmu.enable_all = intel_pmu_nhm_enable_all;
|
||||
pr_cont("Westmere events, ");
|
||||
break;
|
||||
|
||||
|
641
arch/x86/kernel/cpu/perf_event_intel_ds.c
Normal file
641
arch/x86/kernel/cpu/perf_event_intel_ds.c
Normal file
@ -0,0 +1,641 @@
|
||||
#ifdef CONFIG_CPU_SUP_INTEL
|
||||
|
||||
/* The maximal number of PEBS events: */
|
||||
#define MAX_PEBS_EVENTS 4
|
||||
|
||||
/* The size of a BTS record in bytes: */
|
||||
#define BTS_RECORD_SIZE 24
|
||||
|
||||
#define BTS_BUFFER_SIZE (PAGE_SIZE << 4)
|
||||
#define PEBS_BUFFER_SIZE PAGE_SIZE
|
||||
|
||||
/*
|
||||
* pebs_record_32 for p4 and core not supported
|
||||
|
||||
struct pebs_record_32 {
|
||||
u32 flags, ip;
|
||||
u32 ax, bc, cx, dx;
|
||||
u32 si, di, bp, sp;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
struct pebs_record_core {
|
||||
u64 flags, ip;
|
||||
u64 ax, bx, cx, dx;
|
||||
u64 si, di, bp, sp;
|
||||
u64 r8, r9, r10, r11;
|
||||
u64 r12, r13, r14, r15;
|
||||
};
|
||||
|
||||
struct pebs_record_nhm {
|
||||
u64 flags, ip;
|
||||
u64 ax, bx, cx, dx;
|
||||
u64 si, di, bp, sp;
|
||||
u64 r8, r9, r10, r11;
|
||||
u64 r12, r13, r14, r15;
|
||||
u64 status, dla, dse, lat;
|
||||
};
|
||||
|
||||
/*
|
||||
* A debug store configuration.
|
||||
*
|
||||
* We only support architectures that use 64bit fields.
|
||||
*/
|
||||
struct debug_store {
|
||||
u64 bts_buffer_base;
|
||||
u64 bts_index;
|
||||
u64 bts_absolute_maximum;
|
||||
u64 bts_interrupt_threshold;
|
||||
u64 pebs_buffer_base;
|
||||
u64 pebs_index;
|
||||
u64 pebs_absolute_maximum;
|
||||
u64 pebs_interrupt_threshold;
|
||||
u64 pebs_event_reset[MAX_PEBS_EVENTS];
|
||||
};
|
||||
|
||||
static void init_debug_store_on_cpu(int cpu)
|
||||
{
|
||||
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA,
|
||||
(u32)((u64)(unsigned long)ds),
|
||||
(u32)((u64)(unsigned long)ds >> 32));
|
||||
}
|
||||
|
||||
static void fini_debug_store_on_cpu(int cpu)
|
||||
{
|
||||
if (!per_cpu(cpu_hw_events, cpu).ds)
|
||||
return;
|
||||
|
||||
wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0);
|
||||
}
|
||||
|
||||
static void release_ds_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (!x86_pmu.bts && !x86_pmu.pebs)
|
||||
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);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int reserve_ds_buffers(void)
|
||||
{
|
||||
int cpu, err = 0;
|
||||
|
||||
if (!x86_pmu.bts && !x86_pmu.pebs)
|
||||
return 0;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct debug_store *ds;
|
||||
void *buffer;
|
||||
int max, thresh;
|
||||
|
||||
err = -ENOMEM;
|
||||
ds = kzalloc(sizeof(*ds), GFP_KERNEL);
|
||||
if (unlikely(!ds))
|
||||
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 {
|
||||
for_each_online_cpu(cpu)
|
||||
init_debug_store_on_cpu(cpu);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* BTS
|
||||
*/
|
||||
|
||||
static struct event_constraint bts_constraint =
|
||||
EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0);
|
||||
|
||||
static void intel_pmu_enable_bts(u64 config)
|
||||
{
|
||||
unsigned long debugctlmsr;
|
||||
|
||||
debugctlmsr = get_debugctlmsr();
|
||||
|
||||
debugctlmsr |= DEBUGCTLMSR_TR;
|
||||
debugctlmsr |= DEBUGCTLMSR_BTS;
|
||||
debugctlmsr |= DEBUGCTLMSR_BTINT;
|
||||
|
||||
if (!(config & ARCH_PERFMON_EVENTSEL_OS))
|
||||
debugctlmsr |= DEBUGCTLMSR_BTS_OFF_OS;
|
||||
|
||||
if (!(config & ARCH_PERFMON_EVENTSEL_USR))
|
||||
debugctlmsr |= DEBUGCTLMSR_BTS_OFF_USR;
|
||||
|
||||
update_debugctlmsr(debugctlmsr);
|
||||
}
|
||||
|
||||
static void intel_pmu_disable_bts(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
unsigned long debugctlmsr;
|
||||
|
||||
if (!cpuc->ds)
|
||||
return;
|
||||
|
||||
debugctlmsr = get_debugctlmsr();
|
||||
|
||||
debugctlmsr &=
|
||||
~(DEBUGCTLMSR_TR | DEBUGCTLMSR_BTS | DEBUGCTLMSR_BTINT |
|
||||
DEBUGCTLMSR_BTS_OFF_OS | DEBUGCTLMSR_BTS_OFF_USR);
|
||||
|
||||
update_debugctlmsr(debugctlmsr);
|
||||
}
|
||||
|
||||
static void intel_pmu_drain_bts_buffer(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct debug_store *ds = cpuc->ds;
|
||||
struct bts_record {
|
||||
u64 from;
|
||||
u64 to;
|
||||
u64 flags;
|
||||
};
|
||||
struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS];
|
||||
struct bts_record *at, *top;
|
||||
struct perf_output_handle handle;
|
||||
struct perf_event_header header;
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs regs;
|
||||
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
at = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
|
||||
top = (struct bts_record *)(unsigned long)ds->bts_index;
|
||||
|
||||
if (top <= at)
|
||||
return;
|
||||
|
||||
ds->bts_index = ds->bts_buffer_base;
|
||||
|
||||
perf_sample_data_init(&data, 0);
|
||||
data.period = event->hw.last_period;
|
||||
regs.ip = 0;
|
||||
|
||||
/*
|
||||
* Prepare a generic sample, i.e. fill in the invariant fields.
|
||||
* We will overwrite the from and to address before we output
|
||||
* the sample.
|
||||
*/
|
||||
perf_prepare_sample(&header, &data, event, ®s);
|
||||
|
||||
if (perf_output_begin(&handle, event, header.size * (top - at), 1, 1))
|
||||
return;
|
||||
|
||||
for (; at < top; at++) {
|
||||
data.ip = at->from;
|
||||
data.addr = at->to;
|
||||
|
||||
perf_output_sample(&handle, &header, &data, event);
|
||||
}
|
||||
|
||||
perf_output_end(&handle);
|
||||
|
||||
/* There's new data available. */
|
||||
event->hw.interrupts++;
|
||||
event->pending_kill = POLL_IN;
|
||||
}
|
||||
|
||||
/*
|
||||
* PEBS
|
||||
*/
|
||||
|
||||
static struct event_constraint intel_core_pebs_events[] = {
|
||||
PEBS_EVENT_CONSTRAINT(0x00c0, 0x1), /* INSTR_RETIRED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */
|
||||
PEBS_EVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0x01cb, 0x1), /* MEM_LOAD_RETIRED.L1D_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x02cb, 0x1), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x04cb, 0x1), /* MEM_LOAD_RETIRED.L2_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x08cb, 0x1), /* MEM_LOAD_RETIRED.L2_LINE_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x10cb, 0x1), /* MEM_LOAD_RETIRED.DTLB_MISS */
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
static struct event_constraint intel_nehalem_pebs_events[] = {
|
||||
PEBS_EVENT_CONSTRAINT(0x00c0, 0xf), /* INSTR_RETIRED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0xfec1, 0xf), /* X87_OPS_RETIRED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0x00c5, 0xf), /* BR_INST_RETIRED.MISPRED */
|
||||
PEBS_EVENT_CONSTRAINT(0x1fc7, 0xf), /* SIMD_INST_RETURED.ANY */
|
||||
PEBS_EVENT_CONSTRAINT(0x01cb, 0xf), /* MEM_LOAD_RETIRED.L1D_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x02cb, 0xf), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x04cb, 0xf), /* MEM_LOAD_RETIRED.L2_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x08cb, 0xf), /* MEM_LOAD_RETIRED.L2_LINE_MISS */
|
||||
PEBS_EVENT_CONSTRAINT(0x10cb, 0xf), /* MEM_LOAD_RETIRED.DTLB_MISS */
|
||||
EVENT_CONSTRAINT_END
|
||||
};
|
||||
|
||||
static struct event_constraint *
|
||||
intel_pebs_constraints(struct perf_event *event)
|
||||
{
|
||||
struct event_constraint *c;
|
||||
|
||||
if (!event->attr.precise_ip)
|
||||
return NULL;
|
||||
|
||||
if (x86_pmu.pebs_constraints) {
|
||||
for_each_event_constraint(c, x86_pmu.pebs_constraints) {
|
||||
if ((event->hw.config & c->cmask) == c->code)
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return &emptyconstraint;
|
||||
}
|
||||
|
||||
static void intel_pmu_pebs_enable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT;
|
||||
|
||||
cpuc->pebs_enabled |= 1ULL << hwc->idx;
|
||||
WARN_ON_ONCE(cpuc->enabled);
|
||||
|
||||
if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1)
|
||||
intel_pmu_lbr_enable(event);
|
||||
}
|
||||
|
||||
static void intel_pmu_pebs_disable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
cpuc->pebs_enabled &= ~(1ULL << hwc->idx);
|
||||
if (cpuc->enabled)
|
||||
wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
||||
|
||||
hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
|
||||
|
||||
if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1)
|
||||
intel_pmu_lbr_disable(event);
|
||||
}
|
||||
|
||||
static void intel_pmu_pebs_enable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (cpuc->pebs_enabled)
|
||||
wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
||||
}
|
||||
|
||||
static void intel_pmu_pebs_disable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (cpuc->pebs_enabled)
|
||||
wrmsrl(MSR_IA32_PEBS_ENABLE, 0);
|
||||
}
|
||||
|
||||
#include <asm/insn.h>
|
||||
|
||||
static inline bool kernel_ip(unsigned long ip)
|
||||
{
|
||||
#ifdef CONFIG_X86_32
|
||||
return ip > PAGE_OFFSET;
|
||||
#else
|
||||
return (long)ip < 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
unsigned long from = cpuc->lbr_entries[0].from;
|
||||
unsigned long old_to, to = cpuc->lbr_entries[0].to;
|
||||
unsigned long ip = regs->ip;
|
||||
|
||||
/*
|
||||
* We don't need to fixup if the PEBS assist is fault like
|
||||
*/
|
||||
if (!x86_pmu.intel_cap.pebs_trap)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* No LBR entry, no basic block, no rewinding
|
||||
*/
|
||||
if (!cpuc->lbr_stack.nr || !from || !to)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Basic blocks should never cross user/kernel boundaries
|
||||
*/
|
||||
if (kernel_ip(ip) != kernel_ip(to))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* unsigned math, either ip is before the start (impossible) or
|
||||
* the basic block is larger than 1 page (sanity)
|
||||
*/
|
||||
if ((ip - to) > PAGE_SIZE)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We sampled a branch insn, rewind using the LBR stack
|
||||
*/
|
||||
if (ip == to) {
|
||||
regs->ip = from;
|
||||
return 1;
|
||||
}
|
||||
|
||||
do {
|
||||
struct insn insn;
|
||||
u8 buf[MAX_INSN_SIZE];
|
||||
void *kaddr;
|
||||
|
||||
old_to = to;
|
||||
if (!kernel_ip(ip)) {
|
||||
int bytes, size = MAX_INSN_SIZE;
|
||||
|
||||
bytes = copy_from_user_nmi(buf, (void __user *)to, size);
|
||||
if (bytes != size)
|
||||
return 0;
|
||||
|
||||
kaddr = buf;
|
||||
} else
|
||||
kaddr = (void *)to;
|
||||
|
||||
kernel_insn_init(&insn, kaddr);
|
||||
insn_get_length(&insn);
|
||||
to += insn.length;
|
||||
} while (to < ip);
|
||||
|
||||
if (to == ip) {
|
||||
regs->ip = old_to;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Even though we decoded the basic block, the instruction stream
|
||||
* never matched the given IP, either the TO or the IP got corrupted.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmu_save_and_restart(struct perf_event *event);
|
||||
|
||||
static void __intel_pmu_pebs_event(struct perf_event *event,
|
||||
struct pt_regs *iregs, void *__pebs)
|
||||
{
|
||||
/*
|
||||
* We cast to pebs_record_core since that is a subset of
|
||||
* both formats and we don't use the other fields in this
|
||||
* routine.
|
||||
*/
|
||||
struct pebs_record_core *pebs = __pebs;
|
||||
struct perf_sample_data data;
|
||||
struct pt_regs regs;
|
||||
|
||||
if (!intel_pmu_save_and_restart(event))
|
||||
return;
|
||||
|
||||
perf_sample_data_init(&data, 0);
|
||||
data.period = event->hw.last_period;
|
||||
|
||||
/*
|
||||
* We use the interrupt regs as a base because the PEBS record
|
||||
* does not contain a full regs set, specifically it seems to
|
||||
* lack segment descriptors, which get used by things like
|
||||
* user_mode().
|
||||
*
|
||||
* In the simple case fix up only the IP and BP,SP regs, for
|
||||
* PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly.
|
||||
* A possible PERF_SAMPLE_REGS will have to transfer all regs.
|
||||
*/
|
||||
regs = *iregs;
|
||||
regs.ip = pebs->ip;
|
||||
regs.bp = pebs->bp;
|
||||
regs.sp = pebs->sp;
|
||||
|
||||
if (event->attr.precise_ip > 1 && intel_pmu_pebs_fixup_ip(®s))
|
||||
regs.flags |= PERF_EFLAGS_EXACT;
|
||||
else
|
||||
regs.flags &= ~PERF_EFLAGS_EXACT;
|
||||
|
||||
if (perf_event_overflow(event, 1, &data, ®s))
|
||||
x86_pmu_stop(event);
|
||||
}
|
||||
|
||||
static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct debug_store *ds = cpuc->ds;
|
||||
struct perf_event *event = cpuc->events[0]; /* PMC0 only */
|
||||
struct pebs_record_core *at, *top;
|
||||
int n;
|
||||
|
||||
if (!ds || !x86_pmu.pebs)
|
||||
return;
|
||||
|
||||
at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base;
|
||||
top = (struct pebs_record_core *)(unsigned long)ds->pebs_index;
|
||||
|
||||
/*
|
||||
* Whatever else happens, drain the thing
|
||||
*/
|
||||
ds->pebs_index = ds->pebs_buffer_base;
|
||||
|
||||
if (!test_bit(0, cpuc->active_mask))
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(!event);
|
||||
|
||||
if (!event->attr.precise_ip)
|
||||
return;
|
||||
|
||||
n = top - at;
|
||||
if (n <= 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Should not happen, we program the threshold at 1 and do not
|
||||
* set a reset value.
|
||||
*/
|
||||
WARN_ON_ONCE(n > 1);
|
||||
at += n - 1;
|
||||
|
||||
__intel_pmu_pebs_event(event, iregs, at);
|
||||
}
|
||||
|
||||
static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
struct debug_store *ds = cpuc->ds;
|
||||
struct pebs_record_nhm *at, *top;
|
||||
struct perf_event *event = NULL;
|
||||
u64 status = 0;
|
||||
int bit, n;
|
||||
|
||||
if (!ds || !x86_pmu.pebs)
|
||||
return;
|
||||
|
||||
at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base;
|
||||
top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index;
|
||||
|
||||
ds->pebs_index = ds->pebs_buffer_base;
|
||||
|
||||
n = top - at;
|
||||
if (n <= 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Should not happen, we program the threshold at 1 and do not
|
||||
* set a reset value.
|
||||
*/
|
||||
WARN_ON_ONCE(n > MAX_PEBS_EVENTS);
|
||||
|
||||
for ( ; at < top; at++) {
|
||||
for_each_set_bit(bit, (unsigned long *)&at->status, MAX_PEBS_EVENTS) {
|
||||
event = cpuc->events[bit];
|
||||
if (!test_bit(bit, cpuc->active_mask))
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(!event);
|
||||
|
||||
if (!event->attr.precise_ip)
|
||||
continue;
|
||||
|
||||
if (__test_and_set_bit(bit, (unsigned long *)&status))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!event || bit >= MAX_PEBS_EVENTS)
|
||||
continue;
|
||||
|
||||
__intel_pmu_pebs_event(event, iregs, at);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BTS, PEBS probe and setup
|
||||
*/
|
||||
|
||||
static void intel_ds_init(void)
|
||||
{
|
||||
/*
|
||||
* No support for 32bit formats
|
||||
*/
|
||||
if (!boot_cpu_has(X86_FEATURE_DTES64))
|
||||
return;
|
||||
|
||||
x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS);
|
||||
x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS);
|
||||
if (x86_pmu.pebs) {
|
||||
char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-';
|
||||
int format = x86_pmu.intel_cap.pebs_format;
|
||||
|
||||
switch (format) {
|
||||
case 0:
|
||||
printk(KERN_CONT "PEBS fmt0%c, ", pebs_type);
|
||||
x86_pmu.pebs_record_size = sizeof(struct pebs_record_core);
|
||||
x86_pmu.drain_pebs = intel_pmu_drain_pebs_core;
|
||||
x86_pmu.pebs_constraints = intel_core_pebs_events;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
printk(KERN_CONT "PEBS fmt1%c, ", pebs_type);
|
||||
x86_pmu.pebs_record_size = sizeof(struct pebs_record_nhm);
|
||||
x86_pmu.drain_pebs = intel_pmu_drain_pebs_nhm;
|
||||
x86_pmu.pebs_constraints = intel_nehalem_pebs_events;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_CONT "no PEBS fmt%d%c, ", format, pebs_type);
|
||||
x86_pmu.pebs = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_CPU_SUP_INTEL */
|
||||
|
||||
static int reserve_ds_buffers(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_ds_buffers(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CPU_SUP_INTEL */
|
218
arch/x86/kernel/cpu/perf_event_intel_lbr.c
Normal file
218
arch/x86/kernel/cpu/perf_event_intel_lbr.c
Normal file
@ -0,0 +1,218 @@
|
||||
#ifdef CONFIG_CPU_SUP_INTEL
|
||||
|
||||
enum {
|
||||
LBR_FORMAT_32 = 0x00,
|
||||
LBR_FORMAT_LIP = 0x01,
|
||||
LBR_FORMAT_EIP = 0x02,
|
||||
LBR_FORMAT_EIP_FLAGS = 0x03,
|
||||
};
|
||||
|
||||
/*
|
||||
* We only support LBR implementations that have FREEZE_LBRS_ON_PMI
|
||||
* otherwise it becomes near impossible to get a reliable stack.
|
||||
*/
|
||||
|
||||
static void __intel_pmu_lbr_enable(void)
|
||||
{
|
||||
u64 debugctl;
|
||||
|
||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
}
|
||||
|
||||
static void __intel_pmu_lbr_disable(void)
|
||||
{
|
||||
u64 debugctl;
|
||||
|
||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
debugctl &= ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_reset_32(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++)
|
||||
wrmsrl(x86_pmu.lbr_from + i, 0);
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_reset_64(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
wrmsrl(x86_pmu.lbr_from + i, 0);
|
||||
wrmsrl(x86_pmu.lbr_to + i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_reset(void)
|
||||
{
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
|
||||
if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32)
|
||||
intel_pmu_lbr_reset_32();
|
||||
else
|
||||
intel_pmu_lbr_reset_64();
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_enable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(cpuc->enabled);
|
||||
|
||||
/*
|
||||
* Reset the LBR stack if we changed task context to
|
||||
* avoid data leaks.
|
||||
*/
|
||||
|
||||
if (event->ctx->task && cpuc->lbr_context != event->ctx) {
|
||||
intel_pmu_lbr_reset();
|
||||
cpuc->lbr_context = event->ctx;
|
||||
}
|
||||
|
||||
cpuc->lbr_users++;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_disable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
|
||||
cpuc->lbr_users--;
|
||||
WARN_ON_ONCE(cpuc->lbr_users < 0);
|
||||
|
||||
if (cpuc->enabled && !cpuc->lbr_users)
|
||||
__intel_pmu_lbr_disable();
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_enable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (cpuc->lbr_users)
|
||||
__intel_pmu_lbr_enable();
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_disable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (cpuc->lbr_users)
|
||||
__intel_pmu_lbr_disable();
|
||||
}
|
||||
|
||||
static inline u64 intel_pmu_lbr_tos(void)
|
||||
{
|
||||
u64 tos;
|
||||
|
||||
rdmsrl(x86_pmu.lbr_tos, tos);
|
||||
|
||||
return tos;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
|
||||
{
|
||||
unsigned long mask = x86_pmu.lbr_nr - 1;
|
||||
u64 tos = intel_pmu_lbr_tos();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
unsigned long lbr_idx = (tos - i) & mask;
|
||||
union {
|
||||
struct {
|
||||
u32 from;
|
||||
u32 to;
|
||||
};
|
||||
u64 lbr;
|
||||
} msr_lastbranch;
|
||||
|
||||
rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
|
||||
|
||||
cpuc->lbr_entries[i].from = msr_lastbranch.from;
|
||||
cpuc->lbr_entries[i].to = msr_lastbranch.to;
|
||||
cpuc->lbr_entries[i].flags = 0;
|
||||
}
|
||||
cpuc->lbr_stack.nr = i;
|
||||
}
|
||||
|
||||
#define LBR_FROM_FLAG_MISPRED (1ULL << 63)
|
||||
|
||||
/*
|
||||
* Due to lack of segmentation in Linux the effective address (offset)
|
||||
* is the same as the linear address, allowing us to merge the LIP and EIP
|
||||
* LBR formats.
|
||||
*/
|
||||
static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
|
||||
{
|
||||
unsigned long mask = x86_pmu.lbr_nr - 1;
|
||||
int lbr_format = x86_pmu.intel_cap.lbr_format;
|
||||
u64 tos = intel_pmu_lbr_tos();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
unsigned long lbr_idx = (tos - i) & mask;
|
||||
u64 from, to, flags = 0;
|
||||
|
||||
rdmsrl(x86_pmu.lbr_from + lbr_idx, from);
|
||||
rdmsrl(x86_pmu.lbr_to + lbr_idx, to);
|
||||
|
||||
if (lbr_format == LBR_FORMAT_EIP_FLAGS) {
|
||||
flags = !!(from & LBR_FROM_FLAG_MISPRED);
|
||||
from = (u64)((((s64)from) << 1) >> 1);
|
||||
}
|
||||
|
||||
cpuc->lbr_entries[i].from = from;
|
||||
cpuc->lbr_entries[i].to = to;
|
||||
cpuc->lbr_entries[i].flags = flags;
|
||||
}
|
||||
cpuc->lbr_stack.nr = i;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_read(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
if (!cpuc->lbr_users)
|
||||
return;
|
||||
|
||||
if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32)
|
||||
intel_pmu_lbr_read_32(cpuc);
|
||||
else
|
||||
intel_pmu_lbr_read_64(cpuc);
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_init_core(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 4;
|
||||
x86_pmu.lbr_tos = 0x01c9;
|
||||
x86_pmu.lbr_from = 0x40;
|
||||
x86_pmu.lbr_to = 0x60;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_init_nhm(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 16;
|
||||
x86_pmu.lbr_tos = 0x01c9;
|
||||
x86_pmu.lbr_from = 0x680;
|
||||
x86_pmu.lbr_to = 0x6c0;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_init_atom(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 8;
|
||||
x86_pmu.lbr_tos = 0x01c9;
|
||||
x86_pmu.lbr_from = 0x40;
|
||||
x86_pmu.lbr_to = 0x60;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CPU_SUP_INTEL */
|
857
arch/x86/kernel/cpu/perf_event_p4.c
Normal file
857
arch/x86/kernel/cpu/perf_event_p4.c
Normal file
@ -0,0 +1,857 @@
|
||||
/*
|
||||
* Netburst Perfomance Events (P4, old Xeon)
|
||||
*
|
||||
* Copyright (C) 2010 Parallels, Inc., Cyrill Gorcunov <gorcunov@openvz.org>
|
||||
* Copyright (C) 2010 Intel Corporation, Lin Ming <ming.m.lin@intel.com>
|
||||
*
|
||||
* For licencing details see kernel-base/COPYING
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_CPU_SUP_INTEL
|
||||
|
||||
#include <asm/perf_event_p4.h>
|
||||
|
||||
#define P4_CNTR_LIMIT 3
|
||||
/*
|
||||
* array indices: 0,1 - HT threads, used with HT enabled cpu
|
||||
*/
|
||||
struct p4_event_bind {
|
||||
unsigned int opcode; /* Event code and ESCR selector */
|
||||
unsigned int escr_msr[2]; /* ESCR MSR for this event */
|
||||
char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on abscence */
|
||||
};
|
||||
|
||||
struct p4_cache_event_bind {
|
||||
unsigned int metric_pebs;
|
||||
unsigned int metric_vert;
|
||||
};
|
||||
|
||||
#define P4_GEN_CACHE_EVENT_BIND(name) \
|
||||
[P4_CACHE__##name] = { \
|
||||
.metric_pebs = P4_PEBS__##name, \
|
||||
.metric_vert = P4_VERT__##name, \
|
||||
}
|
||||
|
||||
static struct p4_cache_event_bind p4_cache_event_bind_map[] = {
|
||||
P4_GEN_CACHE_EVENT_BIND(1stl_cache_load_miss_retired),
|
||||
P4_GEN_CACHE_EVENT_BIND(2ndl_cache_load_miss_retired),
|
||||
P4_GEN_CACHE_EVENT_BIND(dtlb_load_miss_retired),
|
||||
P4_GEN_CACHE_EVENT_BIND(dtlb_store_miss_retired),
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that we don't use CCCR1 here, there is an
|
||||
* exception for P4_BSQ_ALLOCATION but we just have
|
||||
* no workaround
|
||||
*
|
||||
* consider this binding as resources which particular
|
||||
* event may borrow, it doesn't contain EventMask,
|
||||
* Tags and friends -- they are left to a caller
|
||||
*/
|
||||
static struct p4_event_bind p4_event_bind_map[] = {
|
||||
[P4_EVENT_TC_DELIVER_MODE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_TC_DELIVER_MODE),
|
||||
.escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_BPU_FETCH_REQUEST] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST),
|
||||
.escr_msr = { MSR_P4_BPU_ESCR0, MSR_P4_BPU_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_ITLB_REFERENCE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_ITLB_REFERENCE),
|
||||
.escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_MEMORY_CANCEL] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_MEMORY_CANCEL),
|
||||
.escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_MEMORY_COMPLETE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_MEMORY_COMPLETE),
|
||||
.escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_LOAD_PORT_REPLAY] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY),
|
||||
.escr_msr = { MSR_P4_SAAT_ESCR0, MSR_P4_SAAT_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_STORE_PORT_REPLAY] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY),
|
||||
.escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_MOB_LOAD_REPLAY] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY),
|
||||
.escr_msr = { MSR_P4_MOB_ESCR0, MSR_P4_MOB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_PAGE_WALK_TYPE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE),
|
||||
.escr_msr = { MSR_P4_PMH_ESCR0, MSR_P4_PMH_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_BSQ_CACHE_REFERENCE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE),
|
||||
.escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_IOQ_ALLOCATION] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_IOQ_ALLOCATION),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_IOQ_ACTIVE_ENTRIES] = { /* shared ESCR */
|
||||
.opcode = P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR1, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {2, -1, -1}, {3, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_FSB_DATA_ACTIVITY] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_BSQ_ALLOCATION] = { /* shared ESCR, broken CCCR1 */
|
||||
.opcode = P4_OPCODE(P4_EVENT_BSQ_ALLOCATION),
|
||||
.escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR0 },
|
||||
.cntr = { {0, -1, -1}, {1, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_BSQ_ACTIVE_ENTRIES] = { /* shared ESCR */
|
||||
.opcode = P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES),
|
||||
.escr_msr = { MSR_P4_BSU_ESCR1 , MSR_P4_BSU_ESCR1 },
|
||||
.cntr = { {2, -1, -1}, {3, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_SSE_INPUT_ASSIST] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_PACKED_SP_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_PACKED_SP_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_PACKED_DP_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_PACKED_DP_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_SCALAR_SP_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_SCALAR_SP_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_SCALAR_DP_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_SCALAR_DP_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_64BIT_MMX_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_64BIT_MMX_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_128BIT_MMX_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_128BIT_MMX_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_X87_FP_UOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_X87_FP_UOP),
|
||||
.escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_TC_MISC] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_TC_MISC),
|
||||
.escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_GLOBAL_POWER_EVENTS] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_TC_MS_XFER] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_TC_MS_XFER),
|
||||
.escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_UOP_QUEUE_WRITES] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES),
|
||||
.escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE),
|
||||
.escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR0 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_RETIRED_BRANCH_TYPE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE),
|
||||
.escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR1 },
|
||||
.cntr = { {4, 5, -1}, {6, 7, -1} },
|
||||
},
|
||||
[P4_EVENT_RESOURCE_STALL] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_RESOURCE_STALL),
|
||||
.escr_msr = { MSR_P4_ALF_ESCR0, MSR_P4_ALF_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_WC_BUFFER] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_WC_BUFFER),
|
||||
.escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 },
|
||||
.cntr = { {8, 9, -1}, {10, 11, -1} },
|
||||
},
|
||||
[P4_EVENT_B2B_CYCLES] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_B2B_CYCLES),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_BNR] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_BNR),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_SNOOP] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_SNOOP),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_RESPONSE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_RESPONSE),
|
||||
.escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 },
|
||||
.cntr = { {0, -1, -1}, {2, -1, -1} },
|
||||
},
|
||||
[P4_EVENT_FRONT_END_EVENT] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_FRONT_END_EVENT),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_EXECUTION_EVENT] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_EXECUTION_EVENT),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_REPLAY_EVENT] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_REPLAY_EVENT),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_INSTR_RETIRED] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_INSTR_RETIRED),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_UOPS_RETIRED] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_UOPS_RETIRED),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_UOP_TYPE] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_UOP_TYPE),
|
||||
.escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_BRANCH_RETIRED] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_BRANCH_RETIRED),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_MISPRED_BRANCH_RETIRED] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_X87_ASSIST] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_X87_ASSIST),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_MACHINE_CLEAR] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_MACHINE_CLEAR),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
[P4_EVENT_INSTR_COMPLETED] = {
|
||||
.opcode = P4_OPCODE(P4_EVENT_INSTR_COMPLETED),
|
||||
.escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 },
|
||||
.cntr = { {12, 13, 16}, {14, 15, 17} },
|
||||
},
|
||||
};
|
||||
|
||||
#define P4_GEN_CACHE_EVENT(event, bit, cache_event) \
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(event) | \
|
||||
P4_ESCR_EMASK_BIT(event, bit)) | \
|
||||
p4_config_pack_cccr(cache_event | \
|
||||
P4_CCCR_ESEL(P4_OPCODE_ESEL(P4_OPCODE(event))))
|
||||
|
||||
static __initconst const u64 p4_hw_cache_event_ids
|
||||
[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_COUNT_HW_CACHE_RESULT_MAX] =
|
||||
{
|
||||
[ C(L1D ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS,
|
||||
P4_CACHE__1stl_cache_load_miss_retired),
|
||||
},
|
||||
},
|
||||
[ C(LL ) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS,
|
||||
P4_CACHE__2ndl_cache_load_miss_retired),
|
||||
},
|
||||
},
|
||||
[ C(DTLB) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS,
|
||||
P4_CACHE__dtlb_load_miss_retired),
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = 0x0,
|
||||
[ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS,
|
||||
P4_CACHE__dtlb_store_miss_retired),
|
||||
},
|
||||
},
|
||||
[ C(ITLB) ] = {
|
||||
[ C(OP_READ) ] = {
|
||||
[ C(RESULT_ACCESS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, HIT,
|
||||
P4_CACHE__itlb_reference_hit),
|
||||
[ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, MISS,
|
||||
P4_CACHE__itlb_reference_miss),
|
||||
},
|
||||
[ C(OP_WRITE) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
[ C(OP_PREFETCH) ] = {
|
||||
[ C(RESULT_ACCESS) ] = -1,
|
||||
[ C(RESULT_MISS) ] = -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static u64 p4_general_events[PERF_COUNT_HW_MAX] = {
|
||||
/* non-halted CPU clocks */
|
||||
[PERF_COUNT_HW_CPU_CYCLES] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)),
|
||||
|
||||
/*
|
||||
* retired instructions
|
||||
* in a sake of simplicity we don't use the FSB tagging
|
||||
*/
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_INSTR_RETIRED) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, BOGUSNTAG)),
|
||||
|
||||
/* cache hits */
|
||||
[PERF_COUNT_HW_CACHE_REFERENCES] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM)),
|
||||
|
||||
/* cache misses */
|
||||
[PERF_COUNT_HW_CACHE_MISSES] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS)),
|
||||
|
||||
/* branch instructions retired */
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_RETIRED_BRANCH_TYPE) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CALL) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT)),
|
||||
|
||||
/* mispredicted branches retired */
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_MISPRED_BRANCH_RETIRED) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS)),
|
||||
|
||||
/* bus ready clocks (cpu is driving #DRDY_DRV\#DRDY_OWN): */
|
||||
[PERF_COUNT_HW_BUS_CYCLES] =
|
||||
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_FSB_DATA_ACTIVITY) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV) |
|
||||
P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN)) |
|
||||
p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE),
|
||||
};
|
||||
|
||||
static struct p4_event_bind *p4_config_get_bind(u64 config)
|
||||
{
|
||||
unsigned int evnt = p4_config_unpack_event(config);
|
||||
struct p4_event_bind *bind = NULL;
|
||||
|
||||
if (evnt < ARRAY_SIZE(p4_event_bind_map))
|
||||
bind = &p4_event_bind_map[evnt];
|
||||
|
||||
return bind;
|
||||
}
|
||||
|
||||
static u64 p4_pmu_event_map(int hw_event)
|
||||
{
|
||||
struct p4_event_bind *bind;
|
||||
unsigned int esel;
|
||||
u64 config;
|
||||
|
||||
config = p4_general_events[hw_event];
|
||||
bind = p4_config_get_bind(config);
|
||||
esel = P4_OPCODE_ESEL(bind->opcode);
|
||||
config |= p4_config_pack_cccr(P4_CCCR_ESEL(esel));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static int p4_hw_config(struct perf_event *event)
|
||||
{
|
||||
int cpu = get_cpu();
|
||||
int rc = 0;
|
||||
unsigned int evnt;
|
||||
u32 escr, cccr;
|
||||
|
||||
/*
|
||||
* the reason we use cpu that early is that: if we get scheduled
|
||||
* first time on the same cpu -- we will not need swap thread
|
||||
* specific flags in config (and will save some cpu cycles)
|
||||
*/
|
||||
|
||||
cccr = p4_default_cccr_conf(cpu);
|
||||
escr = p4_default_escr_conf(cpu, event->attr.exclude_kernel,
|
||||
event->attr.exclude_user);
|
||||
event->hw.config = p4_config_pack_escr(escr) |
|
||||
p4_config_pack_cccr(cccr);
|
||||
|
||||
if (p4_ht_active() && p4_ht_thread(cpu))
|
||||
event->hw.config = p4_set_ht_bit(event->hw.config);
|
||||
|
||||
if (event->attr.type == PERF_TYPE_RAW) {
|
||||
|
||||
/* user data may have out-of-bound event index */
|
||||
evnt = p4_config_unpack_event(event->attr.config);
|
||||
if (evnt >= ARRAY_SIZE(p4_event_bind_map)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't control raw events so it's up to the caller
|
||||
* to pass sane values (and we don't count the thread number
|
||||
* on HT machine but allow HT-compatible specifics to be
|
||||
* passed on)
|
||||
*
|
||||
* XXX: HT wide things should check perf_paranoid_cpu() &&
|
||||
* CAP_SYS_ADMIN
|
||||
*/
|
||||
event->hw.config |= event->attr.config &
|
||||
(p4_config_pack_escr(P4_ESCR_MASK_HT) |
|
||||
p4_config_pack_cccr(P4_CCCR_MASK_HT));
|
||||
}
|
||||
|
||||
rc = x86_setup_perfctr(event);
|
||||
out:
|
||||
put_cpu();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc)
|
||||
{
|
||||
unsigned long dummy;
|
||||
|
||||
rdmsrl(hwc->config_base + hwc->idx, dummy);
|
||||
if (dummy & P4_CCCR_OVF) {
|
||||
(void)checking_wrmsrl(hwc->config_base + hwc->idx,
|
||||
((u64)dummy) & ~P4_CCCR_OVF);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void p4_pmu_disable_event(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
/*
|
||||
* If event gets disabled while counter is in overflowed
|
||||
* state we need to clear P4_CCCR_OVF, otherwise interrupt get
|
||||
* asserted again and again
|
||||
*/
|
||||
(void)checking_wrmsrl(hwc->config_base + hwc->idx,
|
||||
(u64)(p4_config_unpack_cccr(hwc->config)) &
|
||||
~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED);
|
||||
}
|
||||
|
||||
static void p4_pmu_disable_all(void)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
struct perf_event *event = cpuc->events[idx];
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
continue;
|
||||
p4_pmu_disable_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
static void p4_pmu_enable_event(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int thread = p4_ht_config_thread(hwc->config);
|
||||
u64 escr_conf = p4_config_unpack_escr(p4_clear_ht_bit(hwc->config));
|
||||
unsigned int idx = p4_config_unpack_event(hwc->config);
|
||||
unsigned int idx_cache = p4_config_unpack_cache_event(hwc->config);
|
||||
struct p4_event_bind *bind;
|
||||
struct p4_cache_event_bind *bind_cache;
|
||||
u64 escr_addr, cccr;
|
||||
|
||||
bind = &p4_event_bind_map[idx];
|
||||
escr_addr = (u64)bind->escr_msr[thread];
|
||||
|
||||
/*
|
||||
* - we dont support cascaded counters yet
|
||||
* - and counter 1 is broken (erratum)
|
||||
*/
|
||||
WARN_ON_ONCE(p4_is_event_cascaded(hwc->config));
|
||||
WARN_ON_ONCE(hwc->idx == 1);
|
||||
|
||||
/* we need a real Event value */
|
||||
escr_conf &= ~P4_ESCR_EVENT_MASK;
|
||||
escr_conf |= P4_ESCR_EVENT(P4_OPCODE_EVNT(bind->opcode));
|
||||
|
||||
cccr = p4_config_unpack_cccr(hwc->config);
|
||||
|
||||
/*
|
||||
* it could be Cache event so that we need to
|
||||
* set metrics into additional MSRs
|
||||
*/
|
||||
BUILD_BUG_ON(P4_CACHE__MAX > P4_CCCR_CACHE_OPS_MASK);
|
||||
if (idx_cache > P4_CACHE__NONE &&
|
||||
idx_cache < ARRAY_SIZE(p4_cache_event_bind_map)) {
|
||||
bind_cache = &p4_cache_event_bind_map[idx_cache];
|
||||
(void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)bind_cache->metric_pebs);
|
||||
(void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)bind_cache->metric_vert);
|
||||
}
|
||||
|
||||
(void)checking_wrmsrl(escr_addr, escr_conf);
|
||||
(void)checking_wrmsrl(hwc->config_base + hwc->idx,
|
||||
(cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE);
|
||||
}
|
||||
|
||||
static void p4_pmu_enable_all(int added)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
struct perf_event *event = cpuc->events[idx];
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
continue;
|
||||
p4_pmu_enable_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
static int p4_pmu_handle_irq(struct pt_regs *regs)
|
||||
{
|
||||
struct perf_sample_data data;
|
||||
struct cpu_hw_events *cpuc;
|
||||
struct perf_event *event;
|
||||
struct hw_perf_event *hwc;
|
||||
int idx, handled = 0;
|
||||
u64 val;
|
||||
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
|
||||
cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
continue;
|
||||
|
||||
event = cpuc->events[idx];
|
||||
hwc = &event->hw;
|
||||
|
||||
WARN_ON_ONCE(hwc->idx != idx);
|
||||
|
||||
/*
|
||||
* FIXME: Redundant call, actually not needed
|
||||
* but just to check if we're screwed
|
||||
*/
|
||||
p4_pmu_clear_cccr_ovf(hwc);
|
||||
|
||||
val = x86_perf_event_update(event);
|
||||
if (val & (1ULL << (x86_pmu.cntval_bits - 1)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* event overflow
|
||||
*/
|
||||
handled = 1;
|
||||
data.period = event->hw.last_period;
|
||||
|
||||
if (!x86_perf_event_set_period(event))
|
||||
continue;
|
||||
if (perf_event_overflow(event, 1, &data, regs))
|
||||
p4_pmu_disable_event(event);
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
/* p4 quirk: unmask it again */
|
||||
apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED);
|
||||
inc_irq_stat(apic_perf_irqs);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* swap thread specific fields according to a thread
|
||||
* we are going to run on
|
||||
*/
|
||||
static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu)
|
||||
{
|
||||
u32 escr, cccr;
|
||||
|
||||
/*
|
||||
* we either lucky and continue on same cpu or no HT support
|
||||
*/
|
||||
if (!p4_should_swap_ts(hwc->config, cpu))
|
||||
return;
|
||||
|
||||
/*
|
||||
* the event is migrated from an another logical
|
||||
* cpu, so we need to swap thread specific flags
|
||||
*/
|
||||
|
||||
escr = p4_config_unpack_escr(hwc->config);
|
||||
cccr = p4_config_unpack_cccr(hwc->config);
|
||||
|
||||
if (p4_ht_thread(cpu)) {
|
||||
cccr &= ~P4_CCCR_OVF_PMI_T0;
|
||||
cccr |= P4_CCCR_OVF_PMI_T1;
|
||||
if (escr & P4_ESCR_T0_OS) {
|
||||
escr &= ~P4_ESCR_T0_OS;
|
||||
escr |= P4_ESCR_T1_OS;
|
||||
}
|
||||
if (escr & P4_ESCR_T0_USR) {
|
||||
escr &= ~P4_ESCR_T0_USR;
|
||||
escr |= P4_ESCR_T1_USR;
|
||||
}
|
||||
hwc->config = p4_config_pack_escr(escr);
|
||||
hwc->config |= p4_config_pack_cccr(cccr);
|
||||
hwc->config |= P4_CONFIG_HT;
|
||||
} else {
|
||||
cccr &= ~P4_CCCR_OVF_PMI_T1;
|
||||
cccr |= P4_CCCR_OVF_PMI_T0;
|
||||
if (escr & P4_ESCR_T1_OS) {
|
||||
escr &= ~P4_ESCR_T1_OS;
|
||||
escr |= P4_ESCR_T0_OS;
|
||||
}
|
||||
if (escr & P4_ESCR_T1_USR) {
|
||||
escr &= ~P4_ESCR_T1_USR;
|
||||
escr |= P4_ESCR_T0_USR;
|
||||
}
|
||||
hwc->config = p4_config_pack_escr(escr);
|
||||
hwc->config |= p4_config_pack_cccr(cccr);
|
||||
hwc->config &= ~P4_CONFIG_HT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ESCR address hashing is tricky, ESCRs are not sequential
|
||||
* in memory but all starts from MSR_P4_BSU_ESCR0 (0x03e0) and
|
||||
* the metric between any ESCRs is laid in range [0xa0,0xe1]
|
||||
*
|
||||
* so we make ~70% filled hashtable
|
||||
*/
|
||||
|
||||
#define P4_ESCR_MSR_BASE 0x000003a0
|
||||
#define P4_ESCR_MSR_MAX 0x000003e1
|
||||
#define P4_ESCR_MSR_TABLE_SIZE (P4_ESCR_MSR_MAX - P4_ESCR_MSR_BASE + 1)
|
||||
#define P4_ESCR_MSR_IDX(msr) (msr - P4_ESCR_MSR_BASE)
|
||||
#define P4_ESCR_MSR_TABLE_ENTRY(msr) [P4_ESCR_MSR_IDX(msr)] = msr
|
||||
|
||||
static const unsigned int p4_escr_table[P4_ESCR_MSR_TABLE_SIZE] = {
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ALF_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ALF_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BPU_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BPU_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BSU_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BSU_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR2),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR3),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR4),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR5),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_DAC_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_DAC_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FIRM_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FIRM_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FLAME_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FLAME_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FSB_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FSB_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IQ_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IQ_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IS_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IS_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ITLB_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ITLB_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IX_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IX_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MOB_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MOB_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MS_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MS_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_PMH_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_PMH_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_RAT_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_RAT_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SAAT_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SAAT_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SSU_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SSU_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TBPU_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TBPU_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TC_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TC_ESCR1),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_U2L_ESCR0),
|
||||
P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_U2L_ESCR1),
|
||||
};
|
||||
|
||||
static int p4_get_escr_idx(unsigned int addr)
|
||||
{
|
||||
unsigned int idx = P4_ESCR_MSR_IDX(addr);
|
||||
|
||||
if (unlikely(idx >= P4_ESCR_MSR_TABLE_SIZE ||
|
||||
!p4_escr_table[idx])) {
|
||||
WARN_ONCE(1, "P4 PMU: Wrong address passed: %x\n", addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int p4_next_cntr(int thread, unsigned long *used_mask,
|
||||
struct p4_event_bind *bind)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < P4_CNTR_LIMIT; i++) {
|
||||
j = bind->cntr[thread][i];
|
||||
if (j != -1 && !test_bit(j, used_mask))
|
||||
return j;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
||||
{
|
||||
unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
|
||||
unsigned long escr_mask[BITS_TO_LONGS(P4_ESCR_MSR_TABLE_SIZE)];
|
||||
int cpu = raw_smp_processor_id();
|
||||
struct hw_perf_event *hwc;
|
||||
struct p4_event_bind *bind;
|
||||
unsigned int i, thread, num;
|
||||
int cntr_idx, escr_idx;
|
||||
|
||||
bitmap_zero(used_mask, X86_PMC_IDX_MAX);
|
||||
bitmap_zero(escr_mask, P4_ESCR_MSR_TABLE_SIZE);
|
||||
|
||||
for (i = 0, num = n; i < n; i++, num--) {
|
||||
|
||||
hwc = &cpuc->event_list[i]->hw;
|
||||
thread = p4_ht_thread(cpu);
|
||||
bind = p4_config_get_bind(hwc->config);
|
||||
escr_idx = p4_get_escr_idx(bind->escr_msr[thread]);
|
||||
if (unlikely(escr_idx == -1))
|
||||
goto done;
|
||||
|
||||
if (hwc->idx != -1 && !p4_should_swap_ts(hwc->config, cpu)) {
|
||||
cntr_idx = hwc->idx;
|
||||
if (assign)
|
||||
assign[i] = hwc->idx;
|
||||
goto reserve;
|
||||
}
|
||||
|
||||
cntr_idx = p4_next_cntr(thread, used_mask, bind);
|
||||
if (cntr_idx == -1 || test_bit(escr_idx, escr_mask))
|
||||
goto done;
|
||||
|
||||
p4_pmu_swap_config_ts(hwc, cpu);
|
||||
if (assign)
|
||||
assign[i] = cntr_idx;
|
||||
reserve:
|
||||
set_bit(cntr_idx, used_mask);
|
||||
set_bit(escr_idx, escr_mask);
|
||||
}
|
||||
|
||||
done:
|
||||
return num ? -ENOSPC : 0;
|
||||
}
|
||||
|
||||
static __initconst const struct x86_pmu p4_pmu = {
|
||||
.name = "Netburst P4/Xeon",
|
||||
.handle_irq = p4_pmu_handle_irq,
|
||||
.disable_all = p4_pmu_disable_all,
|
||||
.enable_all = p4_pmu_enable_all,
|
||||
.enable = p4_pmu_enable_event,
|
||||
.disable = p4_pmu_disable_event,
|
||||
.eventsel = MSR_P4_BPU_CCCR0,
|
||||
.perfctr = MSR_P4_BPU_PERFCTR0,
|
||||
.event_map = p4_pmu_event_map,
|
||||
.max_events = ARRAY_SIZE(p4_general_events),
|
||||
.get_event_constraints = x86_get_event_constraints,
|
||||
/*
|
||||
* IF HT disabled we may need to use all
|
||||
* ARCH_P4_MAX_CCCR counters simulaneously
|
||||
* though leave it restricted at moment assuming
|
||||
* HT is on
|
||||
*/
|
||||
.num_counters = ARCH_P4_MAX_CCCR,
|
||||
.apic = 1,
|
||||
.cntval_bits = 40,
|
||||
.cntval_mask = (1ULL << 40) - 1,
|
||||
.max_period = (1ULL << 39) - 1,
|
||||
.hw_config = p4_hw_config,
|
||||
.schedule_events = p4_pmu_schedule_events,
|
||||
};
|
||||
|
||||
static __init int p4_pmu_init(void)
|
||||
{
|
||||
unsigned int low, high;
|
||||
|
||||
/* If we get stripped -- indexig fails */
|
||||
BUILD_BUG_ON(ARCH_P4_MAX_CCCR > X86_PMC_MAX_GENERIC);
|
||||
|
||||
rdmsr(MSR_IA32_MISC_ENABLE, low, high);
|
||||
if (!(low & (1 << 7))) {
|
||||
pr_cont("unsupported Netburst CPU model %d ",
|
||||
boot_cpu_data.x86_model);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
memcpy(hw_cache_event_ids, p4_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
||||
pr_cont("Netburst events, ");
|
||||
|
||||
x86_pmu = p4_pmu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CPU_SUP_INTEL */
|
@ -27,24 +27,6 @@ static u64 p6_pmu_event_map(int hw_event)
|
||||
*/
|
||||
#define P6_NOP_EVENT 0x0000002EULL
|
||||
|
||||
static u64 p6_pmu_raw_event(u64 hw_event)
|
||||
{
|
||||
#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL
|
||||
#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL
|
||||
#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL
|
||||
#define P6_EVNTSEL_INV_MASK 0x00800000ULL
|
||||
#define P6_EVNTSEL_REG_MASK 0xFF000000ULL
|
||||
|
||||
#define P6_EVNTSEL_MASK \
|
||||
(P6_EVNTSEL_EVENT_MASK | \
|
||||
P6_EVNTSEL_UNIT_MASK | \
|
||||
P6_EVNTSEL_EDGE_MASK | \
|
||||
P6_EVNTSEL_INV_MASK | \
|
||||
P6_EVNTSEL_REG_MASK)
|
||||
|
||||
return hw_event & P6_EVNTSEL_MASK;
|
||||
}
|
||||
|
||||
static struct event_constraint p6_event_constraints[] =
|
||||
{
|
||||
INTEL_EVENT_CONSTRAINT(0xc1, 0x1), /* FLOPS */
|
||||
@ -66,7 +48,7 @@ static void p6_pmu_disable_all(void)
|
||||
wrmsrl(MSR_P6_EVNTSEL0, val);
|
||||
}
|
||||
|
||||
static void p6_pmu_enable_all(void)
|
||||
static void p6_pmu_enable_all(int added)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
@ -102,22 +84,23 @@ static void p6_pmu_enable_event(struct perf_event *event)
|
||||
(void)checking_wrmsrl(hwc->config_base + hwc->idx, val);
|
||||
}
|
||||
|
||||
static __initconst struct x86_pmu p6_pmu = {
|
||||
static __initconst const struct x86_pmu p6_pmu = {
|
||||
.name = "p6",
|
||||
.handle_irq = x86_pmu_handle_irq,
|
||||
.disable_all = p6_pmu_disable_all,
|
||||
.enable_all = p6_pmu_enable_all,
|
||||
.enable = p6_pmu_enable_event,
|
||||
.disable = p6_pmu_disable_event,
|
||||
.hw_config = x86_pmu_hw_config,
|
||||
.schedule_events = x86_schedule_events,
|
||||
.eventsel = MSR_P6_EVNTSEL0,
|
||||
.perfctr = MSR_P6_PERFCTR0,
|
||||
.event_map = p6_pmu_event_map,
|
||||
.raw_event = p6_pmu_raw_event,
|
||||
.max_events = ARRAY_SIZE(p6_perfmon_event_map),
|
||||
.apic = 1,
|
||||
.max_period = (1ULL << 31) - 1,
|
||||
.version = 0,
|
||||
.num_events = 2,
|
||||
.num_counters = 2,
|
||||
/*
|
||||
* Events have 40 bits implemented. However they are designed such
|
||||
* that bits [32-39] are sign extensions of bit 31. As such the
|
||||
@ -125,8 +108,8 @@ static __initconst struct x86_pmu p6_pmu = {
|
||||
*
|
||||
* See IA-32 Intel Architecture Software developer manual Vol 3B
|
||||
*/
|
||||
.event_bits = 32,
|
||||
.event_mask = (1ULL << 32) - 1,
|
||||
.cntval_bits = 32,
|
||||
.cntval_mask = (1ULL << 32) - 1,
|
||||
.get_event_constraints = x86_get_event_constraints,
|
||||
.event_constraints = p6_event_constraints,
|
||||
};
|
||||
|
1437
arch/x86/kernel/ds.c
1437
arch/x86/kernel/ds.c
File diff suppressed because it is too large
Load Diff
@ -1,408 +0,0 @@
|
||||
/*
|
||||
* Debug Store support - selftest
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2009
|
||||
*/
|
||||
|
||||
#include "ds_selftest.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <asm/ds.h>
|
||||
|
||||
|
||||
#define BUFFER_SIZE 521 /* Intentionally chose an odd size. */
|
||||
#define SMALL_BUFFER_SIZE 24 /* A single bts entry. */
|
||||
|
||||
struct ds_selftest_bts_conf {
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
int (*suspend)(struct bts_tracer *);
|
||||
int (*resume)(struct bts_tracer *);
|
||||
};
|
||||
|
||||
static int ds_selftest_bts_consistency(const struct bts_trace *trace)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!trace) {
|
||||
printk(KERN_CONT "failed to access trace...");
|
||||
/* Bail out. Other tests are pointless. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!trace->read) {
|
||||
printk(KERN_CONT "bts read not available...");
|
||||
error = -1;
|
||||
}
|
||||
|
||||
/* Do some sanity checks on the trace configuration. */
|
||||
if (!trace->ds.n) {
|
||||
printk(KERN_CONT "empty bts buffer...");
|
||||
error = -1;
|
||||
}
|
||||
if (!trace->ds.size) {
|
||||
printk(KERN_CONT "bad bts trace setup...");
|
||||
error = -1;
|
||||
}
|
||||
if (trace->ds.end !=
|
||||
(char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
|
||||
printk(KERN_CONT "bad bts buffer setup...");
|
||||
error = -1;
|
||||
}
|
||||
/*
|
||||
* We allow top in [begin; end], since its not clear when the
|
||||
* overflow adjustment happens: after the increment or before the
|
||||
* write.
|
||||
*/
|
||||
if ((trace->ds.top < trace->ds.begin) ||
|
||||
(trace->ds.end < trace->ds.top)) {
|
||||
printk(KERN_CONT "bts top out of bounds...");
|
||||
error = -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_read(struct bts_tracer *tracer,
|
||||
const struct bts_trace *trace,
|
||||
const void *from, const void *to)
|
||||
{
|
||||
const unsigned char *at;
|
||||
|
||||
/*
|
||||
* Check a few things which do not belong to this test.
|
||||
* They should be covered by other tests.
|
||||
*/
|
||||
if (!trace)
|
||||
return -1;
|
||||
|
||||
if (!trace->read)
|
||||
return -1;
|
||||
|
||||
if (to < from)
|
||||
return -1;
|
||||
|
||||
if (from < trace->ds.begin)
|
||||
return -1;
|
||||
|
||||
if (trace->ds.end < to)
|
||||
return -1;
|
||||
|
||||
if (!trace->ds.size)
|
||||
return -1;
|
||||
|
||||
/* Now to the test itself. */
|
||||
for (at = from; (void *)at < to; at += trace->ds.size) {
|
||||
struct bts_struct bts;
|
||||
unsigned long index;
|
||||
int error;
|
||||
|
||||
if (((void *)at - trace->ds.begin) % trace->ds.size) {
|
||||
printk(KERN_CONT
|
||||
"read from non-integer index...");
|
||||
return -1;
|
||||
}
|
||||
index = ((void *)at - trace->ds.begin) / trace->ds.size;
|
||||
|
||||
memset(&bts, 0, sizeof(bts));
|
||||
error = trace->read(tracer, at, &bts);
|
||||
if (error < 0) {
|
||||
printk(KERN_CONT
|
||||
"error reading bts trace at [%lu] (0x%p)...",
|
||||
index, at);
|
||||
return error;
|
||||
}
|
||||
|
||||
switch (bts.qualifier) {
|
||||
case BTS_BRANCH:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_CONT
|
||||
"unexpected bts entry %llu at [%lu] (0x%p)...",
|
||||
bts.qualifier, index, at);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds_selftest_bts_cpu(void *arg)
|
||||
{
|
||||
struct ds_selftest_bts_conf *conf = arg;
|
||||
const struct bts_trace *trace;
|
||||
void *top;
|
||||
|
||||
if (IS_ERR(conf->tracer)) {
|
||||
conf->error = PTR_ERR(conf->tracer);
|
||||
conf->tracer = NULL;
|
||||
|
||||
printk(KERN_CONT
|
||||
"initialization failed (err: %d)...", conf->error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We should meanwhile have enough trace. */
|
||||
conf->error = conf->suspend(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/* Let's see if we can access the trace. */
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/* If everything went well, we should have a few trace entries. */
|
||||
if (trace->ds.top == trace->ds.begin) {
|
||||
/*
|
||||
* It is possible but highly unlikely that we got a
|
||||
* buffer overflow and end up at exactly the same
|
||||
* position we started from.
|
||||
* Let's issue a warning, but continue.
|
||||
*/
|
||||
printk(KERN_CONT "no trace/overflow...");
|
||||
}
|
||||
|
||||
/* Let's try to read the trace we collected. */
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.top);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Let's read the trace again.
|
||||
* Since we suspended tracing, we should get the same result.
|
||||
*/
|
||||
top = trace->ds.top;
|
||||
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
if (top != trace->ds.top) {
|
||||
printk(KERN_CONT "suspend not working...");
|
||||
conf->error = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's collect some more trace - see if resume is working. */
|
||||
conf->error = conf->resume(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
conf->error = conf->suspend(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
if (trace->ds.top == top) {
|
||||
/*
|
||||
* It is possible but highly unlikely that we got a
|
||||
* buffer overflow and end up at exactly the same
|
||||
* position we started from.
|
||||
* Let's issue a warning and check the full trace.
|
||||
*/
|
||||
printk(KERN_CONT
|
||||
"no resume progress/overflow...");
|
||||
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.end);
|
||||
} else if (trace->ds.top < top) {
|
||||
/*
|
||||
* We had a buffer overflow - the entire buffer should
|
||||
* contain trace records.
|
||||
*/
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.end);
|
||||
} else {
|
||||
/*
|
||||
* It is quite likely that the buffer did not overflow.
|
||||
* Let's just check the delta trace.
|
||||
*/
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace, top,
|
||||
trace->ds.top);
|
||||
}
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
conf->error = 0;
|
||||
}
|
||||
|
||||
static int ds_suspend_bts_wrap(struct bts_tracer *tracer)
|
||||
{
|
||||
ds_suspend_bts(tracer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds_resume_bts_wrap(struct bts_tracer *tracer)
|
||||
{
|
||||
ds_resume_bts(tracer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds_release_bts_noirq_wrap(void *tracer)
|
||||
{
|
||||
(void)ds_release_bts_noirq(tracer);
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_release_noirq(int cpu,
|
||||
struct bts_tracer *tracer)
|
||||
{
|
||||
int error = -EPERM;
|
||||
|
||||
/* Try to release the tracer on the wrong cpu. */
|
||||
get_cpu();
|
||||
if (cpu != smp_processor_id()) {
|
||||
error = ds_release_bts_noirq(tracer);
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "release on wrong cpu...");
|
||||
}
|
||||
put_cpu();
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer)
|
||||
{
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
|
||||
/* Try to request cpu tracing while task tracing is active. */
|
||||
tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL,
|
||||
(size_t)-1, BTS_KERNEL);
|
||||
error = PTR_ERR(tracer);
|
||||
if (!IS_ERR(tracer)) {
|
||||
ds_release_bts(tracer);
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "cpu/task tracing overlap...");
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_request_task(void *buffer)
|
||||
{
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
|
||||
/* Try to request cpu tracing while task tracing is active. */
|
||||
tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL,
|
||||
(size_t)-1, BTS_KERNEL);
|
||||
error = PTR_ERR(tracer);
|
||||
if (!IS_ERR(tracer)) {
|
||||
error = 0;
|
||||
ds_release_bts(tracer);
|
||||
}
|
||||
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "task/cpu tracing overlap...");
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
int ds_selftest_bts(void)
|
||||
{
|
||||
struct ds_selftest_bts_conf conf;
|
||||
unsigned char buffer[BUFFER_SIZE], *small_buffer;
|
||||
unsigned long irq;
|
||||
int cpu;
|
||||
|
||||
printk(KERN_INFO "[ds] bts selftest...");
|
||||
conf.error = 0;
|
||||
|
||||
small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
conf.suspend = ds_suspend_bts_wrap;
|
||||
conf.resume = ds_resume_bts_wrap;
|
||||
conf.tracer =
|
||||
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_task(buffer);
|
||||
ds_release_bts(conf.tracer);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.suspend = ds_suspend_bts_noirq;
|
||||
conf.resume = ds_resume_bts_noirq;
|
||||
conf.tracer =
|
||||
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1);
|
||||
if (conf.error >= 0) {
|
||||
conf.error =
|
||||
ds_selftest_bts_bad_release_noirq(cpu,
|
||||
conf.tracer);
|
||||
/* We must not release the tracer twice. */
|
||||
if (conf.error < 0)
|
||||
conf.tracer = NULL;
|
||||
}
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_task(buffer);
|
||||
smp_call_function_single(cpu, ds_release_bts_noirq_wrap,
|
||||
conf.tracer, 1);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
conf.suspend = ds_suspend_bts_wrap;
|
||||
conf.resume = ds_resume_bts_wrap;
|
||||
conf.tracer =
|
||||
ds_request_bts_task(current, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
|
||||
ds_release_bts(conf.tracer);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.suspend = ds_suspend_bts_noirq;
|
||||
conf.resume = ds_resume_bts_noirq;
|
||||
conf.tracer =
|
||||
ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
local_irq_save(irq);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
|
||||
ds_release_bts_noirq(conf.tracer);
|
||||
local_irq_restore(irq);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.error = 0;
|
||||
out:
|
||||
put_online_cpus();
|
||||
printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));
|
||||
|
||||
return conf.error;
|
||||
}
|
||||
|
||||
int ds_selftest_pebs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Debug Store support - selftest
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2009
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_X86_DS_SELFTEST
|
||||
extern int ds_selftest_bts(void);
|
||||
extern int ds_selftest_pebs(void);
|
||||
#else
|
||||
static inline int ds_selftest_bts(void) { return 0; }
|
||||
static inline int ds_selftest_pebs(void) { return 0; }
|
||||
#endif
|
@ -224,11 +224,6 @@ unsigned __kprobes long oops_begin(void)
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
|
||||
/* notify the hw-branch tracer so it may disable tracing and
|
||||
add the last trace to the trace buffer -
|
||||
the earlier this happens, the more useful the trace. */
|
||||
trace_hw_branch_oops();
|
||||
|
||||
oops_enter();
|
||||
|
||||
/* racy, but better than risking deadlock. */
|
||||
|
@ -188,26 +188,17 @@ static int get_hbp_len(u8 hbp_len)
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for virtual address in user space.
|
||||
*/
|
||||
int arch_check_va_in_userspace(unsigned long va, u8 hbp_len)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = get_hbp_len(hbp_len);
|
||||
|
||||
return (va <= TASK_SIZE - len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for virtual address in kernel space.
|
||||
*/
|
||||
static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len)
|
||||
int arch_check_bp_in_kernelspace(struct perf_event *bp)
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned long va;
|
||||
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
|
||||
|
||||
len = get_hbp_len(hbp_len);
|
||||
va = info->address;
|
||||
len = get_hbp_len(info->len);
|
||||
|
||||
return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
|
||||
}
|
||||
@ -300,8 +291,7 @@ static int arch_build_bp_info(struct perf_event *bp)
|
||||
/*
|
||||
* Validate the arch-specific HW Breakpoint register settings
|
||||
*/
|
||||
int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
struct task_struct *tsk)
|
||||
int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
{
|
||||
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
|
||||
unsigned int align;
|
||||
@ -314,16 +304,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (info->type == X86_BREAKPOINT_EXECUTE)
|
||||
/*
|
||||
* Ptrace-refactoring code
|
||||
* For now, we'll allow instruction breakpoint only for user-space
|
||||
* addresses
|
||||
*/
|
||||
if ((!arch_check_va_in_userspace(info->address, info->len)) &&
|
||||
info->len != X86_BREAKPOINT_EXECUTE)
|
||||
return ret;
|
||||
|
||||
switch (info->len) {
|
||||
case X86_BREAKPOINT_LEN_1:
|
||||
align = 0;
|
||||
@ -350,15 +330,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
||||
if (info->address & align)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that the virtual address is in the proper range */
|
||||
if (tsk) {
|
||||
if (!arch_check_va_in_userspace(info->address, info->len))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (!arch_check_va_in_kernelspace(info->address, info->len))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -422,14 +422,22 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
|
||||
|
||||
static void __kprobes clear_btf(void)
|
||||
{
|
||||
if (test_thread_flag(TIF_DEBUGCTLMSR))
|
||||
update_debugctlmsr(0);
|
||||
if (test_thread_flag(TIF_BLOCKSTEP)) {
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
debugctl &= ~DEBUGCTLMSR_BTF;
|
||||
update_debugctlmsr(debugctl);
|
||||
}
|
||||
}
|
||||
|
||||
static void __kprobes restore_btf(void)
|
||||
{
|
||||
if (test_thread_flag(TIF_DEBUGCTLMSR))
|
||||
update_debugctlmsr(current->thread.debugctlmsr);
|
||||
if (test_thread_flag(TIF_BLOCKSTEP)) {
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
debugctl |= DEBUGCTLMSR_BTF;
|
||||
update_debugctlmsr(debugctl);
|
||||
}
|
||||
}
|
||||
|
||||
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <asm/idle.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/i387.h>
|
||||
#include <asm/ds.h>
|
||||
#include <asm/debugreg.h>
|
||||
|
||||
unsigned long idle_halt;
|
||||
@ -50,8 +49,6 @@ void free_thread_xstate(struct task_struct *tsk)
|
||||
kmem_cache_free(task_xstate_cachep, tsk->thread.xstate);
|
||||
tsk->thread.xstate = NULL;
|
||||
}
|
||||
|
||||
WARN(tsk->thread.ds_ctx, "leaking DS context\n");
|
||||
}
|
||||
|
||||
void free_thread_info(struct thread_info *ti)
|
||||
@ -198,11 +195,16 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
|
||||
prev = &prev_p->thread;
|
||||
next = &next_p->thread;
|
||||
|
||||
if (test_tsk_thread_flag(next_p, TIF_DS_AREA_MSR) ||
|
||||
test_tsk_thread_flag(prev_p, TIF_DS_AREA_MSR))
|
||||
ds_switch_to(prev_p, next_p);
|
||||
else if (next->debugctlmsr != prev->debugctlmsr)
|
||||
update_debugctlmsr(next->debugctlmsr);
|
||||
if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^
|
||||
test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) {
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
debugctl &= ~DEBUGCTLMSR_BTF;
|
||||
if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP))
|
||||
debugctl |= DEBUGCTLMSR_BTF;
|
||||
|
||||
update_debugctlmsr(debugctl);
|
||||
}
|
||||
|
||||
if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^
|
||||
test_tsk_thread_flag(next_p, TIF_NOTSC)) {
|
||||
|
@ -55,7 +55,6 @@
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/syscalls.h>
|
||||
#include <asm/ds.h>
|
||||
#include <asm/debugreg.h>
|
||||
|
||||
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
||||
@ -238,13 +237,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||
kfree(p->thread.io_bitmap_ptr);
|
||||
p->thread.io_bitmap_max = 0;
|
||||
}
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
||||
p->thread.ds_ctx = NULL;
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
|
||||
p->thread.debugctlmsr = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include <asm/ia32.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/syscalls.h>
|
||||
#include <asm/ds.h>
|
||||
#include <asm/debugreg.h>
|
||||
|
||||
asmlinkage extern void ret_from_fork(void);
|
||||
@ -313,13 +312,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
||||
p->thread.ds_ctx = NULL;
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
|
||||
p->thread.debugctlmsr = 0;
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
if (err && p->thread.io_bitmap_ptr) {
|
||||
|
@ -2,9 +2,6 @@
|
||||
/*
|
||||
* Pentium III FXSR, SSE support
|
||||
* Gareth Hughes <gareth@valinux.com>, May 2000
|
||||
*
|
||||
* BTS tracing
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, Dec 2007
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -22,7 +19,6 @@
|
||||
#include <linux/audit.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
@ -36,7 +32,6 @@
|
||||
#include <asm/desc.h>
|
||||
#include <asm/prctl.h>
|
||||
#include <asm/proto.h>
|
||||
#include <asm/ds.h>
|
||||
#include <asm/hw_breakpoint.h>
|
||||
|
||||
#include "tls.h"
|
||||
@ -693,7 +688,7 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
||||
struct perf_event_attr attr;
|
||||
|
||||
if (!t->ptrace_bps[nr]) {
|
||||
hw_breakpoint_init(&attr);
|
||||
ptrace_breakpoint_init(&attr);
|
||||
/*
|
||||
* Put stub len and type to register (reserve) an inactive but
|
||||
* correct bp
|
||||
@ -789,342 +784,6 @@ static int ioperm_get(struct task_struct *target,
|
||||
0, IO_BITMAP_BYTES);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
/*
|
||||
* A branch trace store context.
|
||||
*
|
||||
* Contexts may only be installed by ptrace_bts_config() and only for
|
||||
* ptraced tasks.
|
||||
*
|
||||
* Contexts are destroyed when the tracee is detached from the tracer.
|
||||
* The actual destruction work requires interrupts enabled, so the
|
||||
* work is deferred and will be scheduled during __ptrace_unlink().
|
||||
*
|
||||
* Contexts hold an additional task_struct reference on the traced
|
||||
* task, as well as a reference on the tracer's mm.
|
||||
*
|
||||
* Ptrace already holds a task_struct for the duration of ptrace operations,
|
||||
* but since destruction is deferred, it may be executed after both
|
||||
* tracer and tracee exited.
|
||||
*/
|
||||
struct bts_context {
|
||||
/* The branch trace handle. */
|
||||
struct bts_tracer *tracer;
|
||||
|
||||
/* The buffer used to store the branch trace and its size. */
|
||||
void *buffer;
|
||||
unsigned int size;
|
||||
|
||||
/* The mm that paid for the above buffer. */
|
||||
struct mm_struct *mm;
|
||||
|
||||
/* The task this context belongs to. */
|
||||
struct task_struct *task;
|
||||
|
||||
/* The signal to send on a bts buffer overflow. */
|
||||
unsigned int bts_ovfl_signal;
|
||||
|
||||
/* The work struct to destroy a context. */
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static int alloc_bts_buffer(struct bts_context *context, unsigned int size)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int err = -ENOMEM;
|
||||
|
||||
err = account_locked_memory(current->mm, current->signal->rlim, size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
buffer = kzalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
goto out_refund;
|
||||
|
||||
context->buffer = buffer;
|
||||
context->size = size;
|
||||
context->mm = get_task_mm(current);
|
||||
|
||||
return 0;
|
||||
|
||||
out_refund:
|
||||
refund_locked_memory(current->mm, size);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void free_bts_buffer(struct bts_context *context)
|
||||
{
|
||||
if (!context->buffer)
|
||||
return;
|
||||
|
||||
kfree(context->buffer);
|
||||
context->buffer = NULL;
|
||||
|
||||
refund_locked_memory(context->mm, context->size);
|
||||
context->size = 0;
|
||||
|
||||
mmput(context->mm);
|
||||
context->mm = NULL;
|
||||
}
|
||||
|
||||
static void free_bts_context_work(struct work_struct *w)
|
||||
{
|
||||
struct bts_context *context;
|
||||
|
||||
context = container_of(w, struct bts_context, work);
|
||||
|
||||
ds_release_bts(context->tracer);
|
||||
put_task_struct(context->task);
|
||||
free_bts_buffer(context);
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
static inline void free_bts_context(struct bts_context *context)
|
||||
{
|
||||
INIT_WORK(&context->work, free_bts_context_work);
|
||||
schedule_work(&context->work);
|
||||
}
|
||||
|
||||
static inline struct bts_context *alloc_bts_context(struct task_struct *task)
|
||||
{
|
||||
struct bts_context *context = kzalloc(sizeof(*context), GFP_KERNEL);
|
||||
if (context) {
|
||||
context->task = task;
|
||||
task->bts = context;
|
||||
|
||||
get_task_struct(task);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static int ptrace_bts_read_record(struct task_struct *child, size_t index,
|
||||
struct bts_struct __user *out)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
struct bts_struct bts;
|
||||
const unsigned char *at;
|
||||
int error;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -ESRCH;
|
||||
|
||||
at = trace->ds.top - ((index + 1) * trace->ds.size);
|
||||
if ((void *)at < trace->ds.begin)
|
||||
at += (trace->ds.n * trace->ds.size);
|
||||
|
||||
if (!trace->read)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
error = trace->read(context->tracer, at, &bts);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (copy_to_user(out, &bts, sizeof(bts)))
|
||||
return -EFAULT;
|
||||
|
||||
return sizeof(bts);
|
||||
}
|
||||
|
||||
static int ptrace_bts_drain(struct task_struct *child,
|
||||
long size,
|
||||
struct bts_struct __user *out)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
const unsigned char *at;
|
||||
int error, drained = 0;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -ESRCH;
|
||||
|
||||
if (!trace->read)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (size < (trace->ds.top - trace->ds.begin))
|
||||
return -EIO;
|
||||
|
||||
for (at = trace->ds.begin; (void *)at < trace->ds.top;
|
||||
out++, drained++, at += trace->ds.size) {
|
||||
struct bts_struct bts;
|
||||
|
||||
error = trace->read(context->tracer, at, &bts);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (copy_to_user(out, &bts, sizeof(bts)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
|
||||
|
||||
error = ds_reset_bts(context->tracer);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
return drained;
|
||||
}
|
||||
|
||||
static int ptrace_bts_config(struct task_struct *child,
|
||||
long cfg_size,
|
||||
const struct ptrace_bts_config __user *ucfg)
|
||||
{
|
||||
struct bts_context *context;
|
||||
struct ptrace_bts_config cfg;
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (cfg_size < sizeof(cfg))
|
||||
return -EIO;
|
||||
|
||||
if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
context = alloc_bts_context(child);
|
||||
if (!context)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
|
||||
if (!cfg.signal)
|
||||
return -EINVAL;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
context->bts_ovfl_signal = cfg.signal;
|
||||
}
|
||||
|
||||
ds_release_bts(context->tracer);
|
||||
context->tracer = NULL;
|
||||
|
||||
if ((cfg.flags & PTRACE_BTS_O_ALLOC) && (cfg.size != context->size)) {
|
||||
int err;
|
||||
|
||||
free_bts_buffer(context);
|
||||
if (!cfg.size)
|
||||
return 0;
|
||||
|
||||
err = alloc_bts_buffer(context, cfg.size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (cfg.flags & PTRACE_BTS_O_TRACE)
|
||||
flags |= BTS_USER;
|
||||
|
||||
if (cfg.flags & PTRACE_BTS_O_SCHED)
|
||||
flags |= BTS_TIMESTAMPS;
|
||||
|
||||
context->tracer =
|
||||
ds_request_bts_task(child, context->buffer, context->size,
|
||||
NULL, (size_t)-1, flags);
|
||||
if (unlikely(IS_ERR(context->tracer))) {
|
||||
int error = PTR_ERR(context->tracer);
|
||||
|
||||
free_bts_buffer(context);
|
||||
context->tracer = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
return sizeof(cfg);
|
||||
}
|
||||
|
||||
static int ptrace_bts_status(struct task_struct *child,
|
||||
long cfg_size,
|
||||
struct ptrace_bts_config __user *ucfg)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
struct ptrace_bts_config cfg;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
if (cfg_size < sizeof(cfg))
|
||||
return -EIO;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -ESRCH;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.size = trace->ds.end - trace->ds.begin;
|
||||
cfg.signal = context->bts_ovfl_signal;
|
||||
cfg.bts_size = sizeof(struct bts_struct);
|
||||
|
||||
if (cfg.signal)
|
||||
cfg.flags |= PTRACE_BTS_O_SIGNAL;
|
||||
|
||||
if (trace->ds.flags & BTS_USER)
|
||||
cfg.flags |= PTRACE_BTS_O_TRACE;
|
||||
|
||||
if (trace->ds.flags & BTS_TIMESTAMPS)
|
||||
cfg.flags |= PTRACE_BTS_O_SCHED;
|
||||
|
||||
if (copy_to_user(ucfg, &cfg, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
return sizeof(cfg);
|
||||
}
|
||||
|
||||
static int ptrace_bts_clear(struct task_struct *child)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -ESRCH;
|
||||
|
||||
memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
|
||||
|
||||
return ds_reset_bts(context->tracer);
|
||||
}
|
||||
|
||||
static int ptrace_bts_size(struct task_struct *child)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -ESRCH;
|
||||
|
||||
return (trace->ds.top - trace->ds.begin) / trace->ds.size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from __ptrace_unlink() after the child has been moved back
|
||||
* to its original parent.
|
||||
*/
|
||||
void ptrace_bts_untrace(struct task_struct *child)
|
||||
{
|
||||
if (unlikely(child->bts)) {
|
||||
free_bts_context(child->bts);
|
||||
child->bts = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching..
|
||||
*
|
||||
@ -1252,39 +911,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||
break;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These bits need more cooking - not enabled yet:
|
||||
*/
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
case PTRACE_BTS_CONFIG:
|
||||
ret = ptrace_bts_config
|
||||
(child, data, (struct ptrace_bts_config __user *)addr);
|
||||
break;
|
||||
|
||||
case PTRACE_BTS_STATUS:
|
||||
ret = ptrace_bts_status
|
||||
(child, data, (struct ptrace_bts_config __user *)addr);
|
||||
break;
|
||||
|
||||
case PTRACE_BTS_SIZE:
|
||||
ret = ptrace_bts_size(child);
|
||||
break;
|
||||
|
||||
case PTRACE_BTS_GET:
|
||||
ret = ptrace_bts_read_record
|
||||
(child, data, (struct bts_struct __user *) addr);
|
||||
break;
|
||||
|
||||
case PTRACE_BTS_CLEAR:
|
||||
ret = ptrace_bts_clear(child);
|
||||
break;
|
||||
|
||||
case PTRACE_BTS_DRAIN:
|
||||
ret = ptrace_bts_drain
|
||||
(child, data, (struct bts_struct __user *) addr);
|
||||
break;
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
@ -1544,14 +1170,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
||||
|
||||
case PTRACE_GET_THREAD_AREA:
|
||||
case PTRACE_SET_THREAD_AREA:
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
case PTRACE_BTS_CONFIG:
|
||||
case PTRACE_BTS_STATUS:
|
||||
case PTRACE_BTS_SIZE:
|
||||
case PTRACE_BTS_GET:
|
||||
case PTRACE_BTS_CLEAR:
|
||||
case PTRACE_BTS_DRAIN:
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
return arch_ptrace(child, request, addr, data);
|
||||
|
||||
default:
|
||||
|
@ -157,22 +157,6 @@ static int enable_single_step(struct task_struct *child)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install this value in MSR_IA32_DEBUGCTLMSR whenever child is running.
|
||||
*/
|
||||
static void write_debugctlmsr(struct task_struct *child, unsigned long val)
|
||||
{
|
||||
if (child->thread.debugctlmsr == val)
|
||||
return;
|
||||
|
||||
child->thread.debugctlmsr = val;
|
||||
|
||||
if (child != current)
|
||||
return;
|
||||
|
||||
update_debugctlmsr(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable single or block step.
|
||||
*/
|
||||
@ -186,15 +170,17 @@ static void enable_step(struct task_struct *child, bool block)
|
||||
* that uses user-mode single stepping itself.
|
||||
*/
|
||||
if (enable_single_step(child) && block) {
|
||||
set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
||||
write_debugctlmsr(child,
|
||||
child->thread.debugctlmsr | DEBUGCTLMSR_BTF);
|
||||
} else {
|
||||
write_debugctlmsr(child,
|
||||
child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF);
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
if (!child->thread.debugctlmsr)
|
||||
clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
||||
debugctl |= DEBUGCTLMSR_BTF;
|
||||
update_debugctlmsr(debugctl);
|
||||
set_tsk_thread_flag(child, TIF_BLOCKSTEP);
|
||||
} else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
debugctl &= ~DEBUGCTLMSR_BTF;
|
||||
update_debugctlmsr(debugctl);
|
||||
clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,11 +199,13 @@ void user_disable_single_step(struct task_struct *child)
|
||||
/*
|
||||
* Make sure block stepping (BTF) is disabled.
|
||||
*/
|
||||
write_debugctlmsr(child,
|
||||
child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF);
|
||||
if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
|
||||
unsigned long debugctl = get_debugctlmsr();
|
||||
|
||||
if (!child->thread.debugctlmsr)
|
||||
clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
|
||||
debugctl &= ~DEBUGCTLMSR_BTF;
|
||||
update_debugctlmsr(debugctl);
|
||||
clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
|
||||
}
|
||||
|
||||
/* Always clear TIF_SINGLESTEP... */
|
||||
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||
|
@ -543,11 +543,11 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
||||
|
||||
/* DR6 may or may not be cleared by the CPU */
|
||||
set_debugreg(0, 6);
|
||||
|
||||
/*
|
||||
* The processor cleared BTF, so don't mark that we need it set.
|
||||
*/
|
||||
clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
|
||||
tsk->thread.debugctlmsr = 0;
|
||||
clear_tsk_thread_flag(tsk, TIF_BLOCKSTEP);
|
||||
|
||||
/* Store the virtualized DR6 value */
|
||||
tsk->thread.debugreg6 = dr6;
|
||||
|
@ -3659,8 +3659,11 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx)
|
||||
|
||||
/* We need to handle NMIs before interrupts are enabled */
|
||||
if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR &&
|
||||
(exit_intr_info & INTR_INFO_VALID_MASK))
|
||||
(exit_intr_info & INTR_INFO_VALID_MASK)) {
|
||||
kvm_before_handle_nmi(&vmx->vcpu);
|
||||
asm("int $2");
|
||||
kvm_after_handle_nmi(&vmx->vcpu);
|
||||
}
|
||||
|
||||
idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK;
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/user-return-notifier.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <trace/events/kvm.h>
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define CREATE_TRACE_POINTS
|
||||
@ -3747,6 +3748,51 @@ static void kvm_timer_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct kvm_vcpu *, current_vcpu);
|
||||
|
||||
static int kvm_is_in_guest(void)
|
||||
{
|
||||
return percpu_read(current_vcpu) != NULL;
|
||||
}
|
||||
|
||||
static int kvm_is_user_mode(void)
|
||||
{
|
||||
int user_mode = 3;
|
||||
|
||||
if (percpu_read(current_vcpu))
|
||||
user_mode = kvm_x86_ops->get_cpl(percpu_read(current_vcpu));
|
||||
|
||||
return user_mode != 0;
|
||||
}
|
||||
|
||||
static unsigned long kvm_get_guest_ip(void)
|
||||
{
|
||||
unsigned long ip = 0;
|
||||
|
||||
if (percpu_read(current_vcpu))
|
||||
ip = kvm_rip_read(percpu_read(current_vcpu));
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
static struct perf_guest_info_callbacks kvm_guest_cbs = {
|
||||
.is_in_guest = kvm_is_in_guest,
|
||||
.is_user_mode = kvm_is_user_mode,
|
||||
.get_guest_ip = kvm_get_guest_ip,
|
||||
};
|
||||
|
||||
void kvm_before_handle_nmi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
percpu_write(current_vcpu, vcpu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_before_handle_nmi);
|
||||
|
||||
void kvm_after_handle_nmi(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
percpu_write(current_vcpu, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_after_handle_nmi);
|
||||
|
||||
int kvm_arch_init(void *opaque)
|
||||
{
|
||||
int r;
|
||||
@ -3783,6 +3829,8 @@ int kvm_arch_init(void *opaque)
|
||||
|
||||
kvm_timer_init();
|
||||
|
||||
perf_register_guest_info_callbacks(&kvm_guest_cbs);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@ -3791,6 +3839,8 @@ out:
|
||||
|
||||
void kvm_arch_exit(void)
|
||||
{
|
||||
perf_unregister_guest_info_callbacks(&kvm_guest_cbs);
|
||||
|
||||
if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
|
||||
cpufreq_unregister_notifier(&kvmclock_cpufreq_notifier_block,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
|
@ -65,4 +65,7 @@ static inline int is_paging(struct kvm_vcpu *vcpu)
|
||||
return kvm_read_cr0_bits(vcpu, X86_CR0_PG);
|
||||
}
|
||||
|
||||
void kvm_before_handle_nmi(struct kvm_vcpu *vcpu);
|
||||
void kvm_after_handle_nmi(struct kvm_vcpu *vcpu);
|
||||
|
||||
#endif
|
||||
|
@ -20,7 +20,7 @@ lib-y := delay.o
|
||||
lib-y += thunk_$(BITS).o
|
||||
lib-y += usercopy_$(BITS).o getuser.o putuser.o
|
||||
lib-y += memcpy_$(BITS).o
|
||||
lib-$(CONFIG_KPROBES) += insn.o inat.o
|
||||
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
|
||||
|
||||
obj-y += msr.o msr-reg.o msr-reg-export.o
|
||||
|
||||
|
@ -238,11 +238,11 @@ static void arch_perfmon_setup_counters(void)
|
||||
if (eax.split.version_id == 0 && current_cpu_data.x86 == 6 &&
|
||||
current_cpu_data.x86_model == 15) {
|
||||
eax.split.version_id = 2;
|
||||
eax.split.num_events = 2;
|
||||
eax.split.num_counters = 2;
|
||||
eax.split.bit_width = 40;
|
||||
}
|
||||
|
||||
num_counters = eax.split.num_events;
|
||||
num_counters = eax.split.num_counters;
|
||||
|
||||
op_arch_perfmon_spec.num_counters = num_counters;
|
||||
op_arch_perfmon_spec.num_controls = num_counters;
|
||||
|
@ -504,18 +504,6 @@ extern int ftrace_dump_on_oops;
|
||||
#define INIT_TRACE_RECURSION
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HW_BRANCH_TRACER
|
||||
|
||||
void trace_hw_branch(u64 from, u64 to);
|
||||
void trace_hw_branch_oops(void);
|
||||
|
||||
#else /* CONFIG_HW_BRANCH_TRACER */
|
||||
|
||||
static inline void trace_hw_branch(u64 from, u64 to) {}
|
||||
static inline void trace_hw_branch_oops(void) {}
|
||||
|
||||
#endif /* CONFIG_HW_BRANCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_FTRACE_SYSCALLS
|
||||
|
||||
unsigned long arch_syscall_addr(int nr);
|
||||
|
@ -9,9 +9,22 @@ enum {
|
||||
};
|
||||
|
||||
enum {
|
||||
HW_BREAKPOINT_R = 1,
|
||||
HW_BREAKPOINT_W = 2,
|
||||
HW_BREAKPOINT_X = 4,
|
||||
HW_BREAKPOINT_EMPTY = 0,
|
||||
HW_BREAKPOINT_R = 1,
|
||||
HW_BREAKPOINT_W = 2,
|
||||
HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W,
|
||||
HW_BREAKPOINT_X = 4,
|
||||
HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X,
|
||||
};
|
||||
|
||||
enum bp_type_idx {
|
||||
TYPE_INST = 0,
|
||||
#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS
|
||||
TYPE_DATA = 0,
|
||||
#else
|
||||
TYPE_DATA = 1,
|
||||
#endif
|
||||
TYPE_MAX
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
@ -34,6 +47,12 @@ static inline void hw_breakpoint_init(struct perf_event_attr *attr)
|
||||
attr->sample_period = 1;
|
||||
}
|
||||
|
||||
static inline void ptrace_breakpoint_init(struct perf_event_attr *attr)
|
||||
{
|
||||
hw_breakpoint_init(attr);
|
||||
attr->exclude_kernel = 1;
|
||||
}
|
||||
|
||||
static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
|
||||
{
|
||||
return bp->attr.bp_addr;
|
||||
|
@ -19,7 +19,6 @@ struct anon_vma;
|
||||
struct file_ra_state;
|
||||
struct user_struct;
|
||||
struct writeback_control;
|
||||
struct rlimit;
|
||||
|
||||
#ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */
|
||||
extern unsigned long max_mapnr;
|
||||
@ -1449,9 +1448,6 @@ int vmemmap_populate_basepages(struct page *start_page,
|
||||
int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
|
||||
void vmemmap_populate_print_last(void);
|
||||
|
||||
extern int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim,
|
||||
size_t size);
|
||||
extern void refund_locked_memory(struct mm_struct *mm, size_t size);
|
||||
|
||||
enum mf_flags {
|
||||
MF_COUNT_INCREASED = 1 << 0,
|
||||
|
@ -203,8 +203,19 @@ struct perf_event_attr {
|
||||
enable_on_exec : 1, /* next exec enables */
|
||||
task : 1, /* trace fork/exit */
|
||||
watermark : 1, /* wakeup_watermark */
|
||||
/*
|
||||
* precise_ip:
|
||||
*
|
||||
* 0 - SAMPLE_IP can have arbitrary skid
|
||||
* 1 - SAMPLE_IP must have constant skid
|
||||
* 2 - SAMPLE_IP requested to have 0 skid
|
||||
* 3 - SAMPLE_IP must have 0 skid
|
||||
*
|
||||
* See also PERF_RECORD_MISC_EXACT_IP
|
||||
*/
|
||||
precise_ip : 2, /* skid constraint */
|
||||
|
||||
__reserved_1 : 49;
|
||||
__reserved_1 : 47;
|
||||
|
||||
union {
|
||||
__u32 wakeup_events; /* wakeup every n events */
|
||||
@ -287,11 +298,24 @@ struct perf_event_mmap_page {
|
||||
__u64 data_tail; /* user-space written tail */
|
||||
};
|
||||
|
||||
#define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0)
|
||||
#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
|
||||
#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
|
||||
#define PERF_RECORD_MISC_KERNEL (1 << 0)
|
||||
#define PERF_RECORD_MISC_USER (2 << 0)
|
||||
#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
|
||||
#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
|
||||
#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
|
||||
|
||||
/*
|
||||
* Indicates that the content of PERF_SAMPLE_IP points to
|
||||
* the actual instruction that triggered the event. See also
|
||||
* perf_event_attr::precise_ip.
|
||||
*/
|
||||
#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
|
||||
/*
|
||||
* Reserve the last bit to indicate some extended misc field
|
||||
*/
|
||||
#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
|
||||
|
||||
struct perf_event_header {
|
||||
__u32 type;
|
||||
@ -439,6 +463,12 @@ enum perf_callchain_context {
|
||||
# include <asm/perf_event.h>
|
||||
#endif
|
||||
|
||||
struct perf_guest_info_callbacks {
|
||||
int (*is_in_guest) (void);
|
||||
int (*is_user_mode) (void);
|
||||
unsigned long (*get_guest_ip) (void);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
#include <asm/hw_breakpoint.h>
|
||||
#endif
|
||||
@ -468,6 +498,17 @@ struct perf_raw_record {
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct perf_branch_entry {
|
||||
__u64 from;
|
||||
__u64 to;
|
||||
__u64 flags;
|
||||
};
|
||||
|
||||
struct perf_branch_stack {
|
||||
__u64 nr;
|
||||
struct perf_branch_entry entries[0];
|
||||
};
|
||||
|
||||
struct task_struct;
|
||||
|
||||
/**
|
||||
@ -506,6 +547,8 @@ struct hw_perf_event {
|
||||
|
||||
struct perf_event;
|
||||
|
||||
#define PERF_EVENT_TXN_STARTED 1
|
||||
|
||||
/**
|
||||
* struct pmu - generic performance monitoring unit
|
||||
*/
|
||||
@ -516,6 +559,16 @@ struct pmu {
|
||||
void (*stop) (struct perf_event *event);
|
||||
void (*read) (struct perf_event *event);
|
||||
void (*unthrottle) (struct perf_event *event);
|
||||
|
||||
/*
|
||||
* group events scheduling is treated as a transaction,
|
||||
* add group events as a whole and perform one schedulability test.
|
||||
* If test fails, roll back the whole group
|
||||
*/
|
||||
|
||||
void (*start_txn) (const struct pmu *pmu);
|
||||
void (*cancel_txn) (const struct pmu *pmu);
|
||||
int (*commit_txn) (const struct pmu *pmu);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -571,6 +624,14 @@ enum perf_group_flag {
|
||||
PERF_GROUP_SOFTWARE = 0x1,
|
||||
};
|
||||
|
||||
#define SWEVENT_HLIST_BITS 8
|
||||
#define SWEVENT_HLIST_SIZE (1 << SWEVENT_HLIST_BITS)
|
||||
|
||||
struct swevent_hlist {
|
||||
struct hlist_head heads[SWEVENT_HLIST_SIZE];
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct perf_event - performance event kernel representation:
|
||||
*/
|
||||
@ -579,6 +640,7 @@ struct perf_event {
|
||||
struct list_head group_entry;
|
||||
struct list_head event_entry;
|
||||
struct list_head sibling_list;
|
||||
struct hlist_node hlist_entry;
|
||||
int nr_siblings;
|
||||
int group_flags;
|
||||
struct perf_event *group_leader;
|
||||
@ -726,6 +788,9 @@ struct perf_cpu_context {
|
||||
int active_oncpu;
|
||||
int max_pertask;
|
||||
int exclusive;
|
||||
struct swevent_hlist *swevent_hlist;
|
||||
struct mutex hlist_mutex;
|
||||
int hlist_refcount;
|
||||
|
||||
/*
|
||||
* Recursion avoidance:
|
||||
@ -769,9 +834,6 @@ extern void perf_disable(void);
|
||||
extern void perf_enable(void);
|
||||
extern int perf_event_task_disable(void);
|
||||
extern int perf_event_task_enable(void);
|
||||
extern int hw_perf_group_sched_in(struct perf_event *group_leader,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx);
|
||||
extern void perf_event_update_userpage(struct perf_event *event);
|
||||
extern int perf_event_release_kernel(struct perf_event *event);
|
||||
extern struct perf_event *
|
||||
@ -902,6 +964,10 @@ static inline void perf_event_mmap(struct vm_area_struct *vma)
|
||||
__perf_event_mmap(vma);
|
||||
}
|
||||
|
||||
extern struct perf_guest_info_callbacks *perf_guest_cbs;
|
||||
extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
|
||||
extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
|
||||
|
||||
extern void perf_event_comm(struct task_struct *tsk);
|
||||
extern void perf_event_fork(struct task_struct *tsk);
|
||||
|
||||
@ -971,6 +1037,11 @@ perf_sw_event(u32 event_id, u64 nr, int nmi,
|
||||
static inline void
|
||||
perf_bp_event(struct perf_event *event, void *data) { }
|
||||
|
||||
static inline int perf_register_guest_info_callbacks
|
||||
(struct perf_guest_info_callbacks *callbacks) { return 0; }
|
||||
static inline int perf_unregister_guest_info_callbacks
|
||||
(struct perf_guest_info_callbacks *callbacks) { return 0; }
|
||||
|
||||
static inline void perf_event_mmap(struct vm_area_struct *vma) { }
|
||||
static inline void perf_event_comm(struct task_struct *tsk) { }
|
||||
static inline void perf_event_fork(struct task_struct *tsk) { }
|
||||
|
@ -345,18 +345,6 @@ static inline void user_single_step_siginfo(struct task_struct *tsk,
|
||||
#define arch_ptrace_stop(code, info) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef arch_ptrace_untrace
|
||||
/*
|
||||
* Do machine-specific work before untracing child.
|
||||
*
|
||||
* This is called for a normal detach as well as from ptrace_exit()
|
||||
* when the tracing task dies.
|
||||
*
|
||||
* Called with write_lock(&tasklist_lock) held.
|
||||
*/
|
||||
#define arch_ptrace_untrace(task) do { } while (0)
|
||||
#endif
|
||||
|
||||
extern int task_current_syscall(struct task_struct *target, long *callno,
|
||||
unsigned long args[6], unsigned int maxargs,
|
||||
unsigned long *sp, unsigned long *pc);
|
||||
|
@ -99,7 +99,6 @@ struct futex_pi_state;
|
||||
struct robust_list_head;
|
||||
struct bio_list;
|
||||
struct fs_struct;
|
||||
struct bts_context;
|
||||
struct perf_event_context;
|
||||
|
||||
/*
|
||||
@ -1272,12 +1271,6 @@ struct task_struct {
|
||||
struct list_head ptraced;
|
||||
struct list_head ptrace_entry;
|
||||
|
||||
/*
|
||||
* This is the tracer handle for the ptrace BTS extension.
|
||||
* This field actually belongs to the ptracer task.
|
||||
*/
|
||||
struct bts_context *bts;
|
||||
|
||||
/* PID/PID hash table linkage. */
|
||||
struct pid_link pids[PIDTYPE_MAX];
|
||||
struct list_head thread_group;
|
||||
@ -2122,10 +2115,8 @@ extern void set_task_comm(struct task_struct *tsk, char *from);
|
||||
extern char *get_task_comm(char *to, struct task_struct *tsk);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
extern void wait_task_context_switch(struct task_struct *p);
|
||||
extern unsigned long wait_task_inactive(struct task_struct *, long match_state);
|
||||
#else
|
||||
static inline void wait_task_context_switch(struct task_struct *p) {}
|
||||
static inline unsigned long wait_task_inactive(struct task_struct *p,
|
||||
long match_state)
|
||||
{
|
||||
|
@ -35,37 +35,15 @@ TRACE_EVENT(lock_acquire,
|
||||
__get_str(name))
|
||||
);
|
||||
|
||||
TRACE_EVENT(lock_release,
|
||||
|
||||
TP_PROTO(struct lockdep_map *lock, int nested, unsigned long ip),
|
||||
|
||||
TP_ARGS(lock, nested, ip),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, lock->name)
|
||||
__field(void *, lockdep_addr)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, lock->name);
|
||||
__entry->lockdep_addr = lock;
|
||||
),
|
||||
|
||||
TP_printk("%p %s",
|
||||
__entry->lockdep_addr, __get_str(name))
|
||||
);
|
||||
|
||||
#ifdef CONFIG_LOCK_STAT
|
||||
|
||||
TRACE_EVENT(lock_contended,
|
||||
DECLARE_EVENT_CLASS(lock,
|
||||
|
||||
TP_PROTO(struct lockdep_map *lock, unsigned long ip),
|
||||
|
||||
TP_ARGS(lock, ip),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, lock->name)
|
||||
__field(void *, lockdep_addr)
|
||||
__string( name, lock->name )
|
||||
__field( void *, lockdep_addr )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@ -73,29 +51,30 @@ TRACE_EVENT(lock_contended,
|
||||
__entry->lockdep_addr = lock;
|
||||
),
|
||||
|
||||
TP_printk("%p %s",
|
||||
__entry->lockdep_addr, __get_str(name))
|
||||
TP_printk("%p %s", __entry->lockdep_addr, __get_str(name))
|
||||
);
|
||||
|
||||
TRACE_EVENT(lock_acquired,
|
||||
TP_PROTO(struct lockdep_map *lock, unsigned long ip, s64 waittime),
|
||||
DEFINE_EVENT(lock, lock_release,
|
||||
|
||||
TP_ARGS(lock, ip, waittime),
|
||||
TP_PROTO(struct lockdep_map *lock, unsigned long ip),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, lock->name)
|
||||
__field(s64, wait_nsec)
|
||||
__field(void *, lockdep_addr)
|
||||
),
|
||||
TP_ARGS(lock, ip)
|
||||
);
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name, lock->name);
|
||||
__entry->wait_nsec = waittime;
|
||||
__entry->lockdep_addr = lock;
|
||||
),
|
||||
TP_printk("%p %s (%llu ns)", __entry->lockdep_addr,
|
||||
__get_str(name),
|
||||
__entry->wait_nsec)
|
||||
#ifdef CONFIG_LOCK_STAT
|
||||
|
||||
DEFINE_EVENT(lock, lock_contended,
|
||||
|
||||
TP_PROTO(struct lockdep_map *lock, unsigned long ip),
|
||||
|
||||
TP_ARGS(lock, ip)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(lock, lock_acquired,
|
||||
|
||||
TP_PROTO(struct lockdep_map *lock, unsigned long ip),
|
||||
|
||||
TP_ARGS(lock, ip)
|
||||
);
|
||||
|
||||
#endif
|
||||
|
@ -763,13 +763,12 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
|
||||
#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
|
||||
static notrace void \
|
||||
perf_trace_templ_##call(struct ftrace_event_call *event_call, \
|
||||
proto) \
|
||||
struct pt_regs *__regs, proto) \
|
||||
{ \
|
||||
struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
|
||||
struct ftrace_raw_##call *entry; \
|
||||
u64 __addr = 0, __count = 1; \
|
||||
unsigned long irq_flags; \
|
||||
struct pt_regs *__regs; \
|
||||
int __entry_size; \
|
||||
int __data_size; \
|
||||
int rctx; \
|
||||
@ -790,20 +789,22 @@ perf_trace_templ_##call(struct ftrace_event_call *event_call, \
|
||||
\
|
||||
{ assign; } \
|
||||
\
|
||||
__regs = &__get_cpu_var(perf_trace_regs); \
|
||||
perf_fetch_caller_regs(__regs, 2); \
|
||||
\
|
||||
perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \
|
||||
__count, irq_flags, __regs); \
|
||||
}
|
||||
|
||||
#undef DEFINE_EVENT
|
||||
#define DEFINE_EVENT(template, call, proto, args) \
|
||||
static notrace void perf_trace_##call(proto) \
|
||||
{ \
|
||||
struct ftrace_event_call *event_call = &event_##call; \
|
||||
\
|
||||
perf_trace_templ_##template(event_call, args); \
|
||||
#define DEFINE_EVENT(template, call, proto, args) \
|
||||
static notrace void perf_trace_##call(proto) \
|
||||
{ \
|
||||
struct ftrace_event_call *event_call = &event_##call; \
|
||||
struct pt_regs *__regs = &get_cpu_var(perf_trace_regs); \
|
||||
\
|
||||
perf_fetch_caller_regs(__regs, 1); \
|
||||
\
|
||||
perf_trace_templ_##template(event_call, __regs, args); \
|
||||
\
|
||||
put_cpu_var(perf_trace_regs); \
|
||||
}
|
||||
|
||||
#undef DEFINE_EVENT_PRINT
|
||||
|
@ -1112,8 +1112,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
p->memcg_batch.memcg = NULL;
|
||||
#endif
|
||||
|
||||
p->bts = NULL;
|
||||
|
||||
/* Perform scheduler related setup. Assign this task to a CPU. */
|
||||
sched_fork(p, clone_flags);
|
||||
|
||||
|
@ -40,23 +40,29 @@
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
|
||||
/*
|
||||
* Constraints data
|
||||
*/
|
||||
|
||||
/* Number of pinned cpu breakpoints in a cpu */
|
||||
static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned);
|
||||
static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]);
|
||||
|
||||
/* Number of pinned task breakpoints in a cpu */
|
||||
static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]);
|
||||
static DEFINE_PER_CPU(unsigned int *, nr_task_bp_pinned[TYPE_MAX]);
|
||||
|
||||
/* Number of non-pinned cpu/task breakpoints in a cpu */
|
||||
static DEFINE_PER_CPU(unsigned int, nr_bp_flexible);
|
||||
static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]);
|
||||
|
||||
static int nr_slots[TYPE_MAX];
|
||||
|
||||
static int constraints_initialized;
|
||||
|
||||
/* Gather the number of total pinned and un-pinned bp in a cpuset */
|
||||
struct bp_busy_slots {
|
||||
@ -67,16 +73,29 @@ struct bp_busy_slots {
|
||||
/* Serialize accesses to the above constraints */
|
||||
static DEFINE_MUTEX(nr_bp_mutex);
|
||||
|
||||
__weak int hw_breakpoint_weight(struct perf_event *bp)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
|
||||
{
|
||||
if (bp->attr.bp_type & HW_BREAKPOINT_RW)
|
||||
return TYPE_DATA;
|
||||
|
||||
return TYPE_INST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the maximum number of pinned breakpoints a task
|
||||
* have in this cpu
|
||||
*/
|
||||
static unsigned int max_task_bp_pinned(int cpu)
|
||||
static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type)
|
||||
{
|
||||
int i;
|
||||
unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu);
|
||||
unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
|
||||
|
||||
for (i = HBP_NUM -1; i >= 0; i--) {
|
||||
for (i = nr_slots[type] - 1; i >= 0; i--) {
|
||||
if (tsk_pinned[i] > 0)
|
||||
return i + 1;
|
||||
}
|
||||
@ -84,7 +103,7 @@ static unsigned int max_task_bp_pinned(int cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int task_bp_pinned(struct task_struct *tsk)
|
||||
static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type)
|
||||
{
|
||||
struct perf_event_context *ctx = tsk->perf_event_ctxp;
|
||||
struct list_head *list;
|
||||
@ -105,7 +124,8 @@ static int task_bp_pinned(struct task_struct *tsk)
|
||||
*/
|
||||
list_for_each_entry(bp, list, event_entry) {
|
||||
if (bp->attr.type == PERF_TYPE_BREAKPOINT)
|
||||
count++;
|
||||
if (find_slot_idx(bp) == type)
|
||||
count += hw_breakpoint_weight(bp);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
@ -118,18 +138,19 @@ static int task_bp_pinned(struct task_struct *tsk)
|
||||
* a given cpu (cpu > -1) or in all of them (cpu = -1).
|
||||
*/
|
||||
static void
|
||||
fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp)
|
||||
fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
|
||||
enum bp_type_idx type)
|
||||
{
|
||||
int cpu = bp->cpu;
|
||||
struct task_struct *tsk = bp->ctx->task;
|
||||
|
||||
if (cpu >= 0) {
|
||||
slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu);
|
||||
if (!tsk)
|
||||
slots->pinned += max_task_bp_pinned(cpu);
|
||||
slots->pinned += max_task_bp_pinned(cpu, type);
|
||||
else
|
||||
slots->pinned += task_bp_pinned(tsk);
|
||||
slots->flexible = per_cpu(nr_bp_flexible, cpu);
|
||||
slots->pinned += task_bp_pinned(tsk, type);
|
||||
slots->flexible = per_cpu(nr_bp_flexible[type], cpu);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -137,48 +158,66 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp)
|
||||
for_each_online_cpu(cpu) {
|
||||
unsigned int nr;
|
||||
|
||||
nr = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
nr = per_cpu(nr_cpu_bp_pinned[type], cpu);
|
||||
if (!tsk)
|
||||
nr += max_task_bp_pinned(cpu);
|
||||
nr += max_task_bp_pinned(cpu, type);
|
||||
else
|
||||
nr += task_bp_pinned(tsk);
|
||||
nr += task_bp_pinned(tsk, type);
|
||||
|
||||
if (nr > slots->pinned)
|
||||
slots->pinned = nr;
|
||||
|
||||
nr = per_cpu(nr_bp_flexible, cpu);
|
||||
nr = per_cpu(nr_bp_flexible[type], cpu);
|
||||
|
||||
if (nr > slots->flexible)
|
||||
slots->flexible = nr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, continue to consider flexible as pinned, until we can
|
||||
* ensure no flexible event can ever be scheduled before a pinned event
|
||||
* in a same cpu.
|
||||
*/
|
||||
static void
|
||||
fetch_this_slot(struct bp_busy_slots *slots, int weight)
|
||||
{
|
||||
slots->pinned += weight;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a pinned breakpoint for the given task in our constraint table
|
||||
*/
|
||||
static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable)
|
||||
static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable,
|
||||
enum bp_type_idx type, int weight)
|
||||
{
|
||||
unsigned int *tsk_pinned;
|
||||
int count = 0;
|
||||
int old_count = 0;
|
||||
int old_idx = 0;
|
||||
int idx = 0;
|
||||
|
||||
count = task_bp_pinned(tsk);
|
||||
old_count = task_bp_pinned(tsk, type);
|
||||
old_idx = old_count - 1;
|
||||
idx = old_idx + weight;
|
||||
|
||||
tsk_pinned = per_cpu(nr_task_bp_pinned, cpu);
|
||||
tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
|
||||
if (enable) {
|
||||
tsk_pinned[count]++;
|
||||
if (count > 0)
|
||||
tsk_pinned[count-1]--;
|
||||
tsk_pinned[idx]++;
|
||||
if (old_count > 0)
|
||||
tsk_pinned[old_idx]--;
|
||||
} else {
|
||||
tsk_pinned[count]--;
|
||||
if (count > 0)
|
||||
tsk_pinned[count-1]++;
|
||||
tsk_pinned[idx]--;
|
||||
if (old_count > 0)
|
||||
tsk_pinned[old_idx]++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add/remove the given breakpoint in our constraint table
|
||||
*/
|
||||
static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
||||
static void
|
||||
toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type,
|
||||
int weight)
|
||||
{
|
||||
int cpu = bp->cpu;
|
||||
struct task_struct *tsk = bp->ctx->task;
|
||||
@ -186,20 +225,20 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
||||
/* Pinned counter task profiling */
|
||||
if (tsk) {
|
||||
if (cpu >= 0) {
|
||||
toggle_bp_task_slot(tsk, cpu, enable);
|
||||
toggle_bp_task_slot(tsk, cpu, enable, type, weight);
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
toggle_bp_task_slot(tsk, cpu, enable);
|
||||
toggle_bp_task_slot(tsk, cpu, enable, type, weight);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Pinned counter cpu profiling */
|
||||
if (enable)
|
||||
per_cpu(nr_cpu_bp_pinned, bp->cpu)++;
|
||||
per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight;
|
||||
else
|
||||
per_cpu(nr_cpu_bp_pinned, bp->cpu)--;
|
||||
per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -246,14 +285,29 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
||||
static int __reserve_bp_slot(struct perf_event *bp)
|
||||
{
|
||||
struct bp_busy_slots slots = {0};
|
||||
enum bp_type_idx type;
|
||||
int weight;
|
||||
|
||||
fetch_bp_busy_slots(&slots, bp);
|
||||
/* We couldn't initialize breakpoint constraints on boot */
|
||||
if (!constraints_initialized)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Basic checks */
|
||||
if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
|
||||
bp->attr.bp_type == HW_BREAKPOINT_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
type = find_slot_idx(bp);
|
||||
weight = hw_breakpoint_weight(bp);
|
||||
|
||||
fetch_bp_busy_slots(&slots, bp, type);
|
||||
fetch_this_slot(&slots, weight);
|
||||
|
||||
/* Flexible counters need to keep at least one slot */
|
||||
if (slots.pinned + (!!slots.flexible) == HBP_NUM)
|
||||
if (slots.pinned + (!!slots.flexible) > nr_slots[type])
|
||||
return -ENOSPC;
|
||||
|
||||
toggle_bp_slot(bp, true);
|
||||
toggle_bp_slot(bp, true, type, weight);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -273,7 +327,12 @@ int reserve_bp_slot(struct perf_event *bp)
|
||||
|
||||
static void __release_bp_slot(struct perf_event *bp)
|
||||
{
|
||||
toggle_bp_slot(bp, false);
|
||||
enum bp_type_idx type;
|
||||
int weight;
|
||||
|
||||
type = find_slot_idx(bp);
|
||||
weight = hw_breakpoint_weight(bp);
|
||||
toggle_bp_slot(bp, false, type, weight);
|
||||
}
|
||||
|
||||
void release_bp_slot(struct perf_event *bp)
|
||||
@ -308,6 +367,28 @@ int dbg_release_bp_slot(struct perf_event *bp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_hw_breakpoint(struct perf_event *bp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = arch_validate_hwbkpt_settings(bp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (arch_check_bp_in_kernelspace(bp)) {
|
||||
if (bp->attr.exclude_kernel)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Don't let unprivileged users set a breakpoint in the trap
|
||||
* path to avoid trap recursion attacks.
|
||||
*/
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int register_perf_hw_breakpoint(struct perf_event *bp)
|
||||
{
|
||||
int ret;
|
||||
@ -316,17 +397,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Ptrace breakpoints can be temporary perf events only
|
||||
* meant to reserve a slot. In this case, it is created disabled and
|
||||
* we don't want to check the params right now (as we put a null addr)
|
||||
* But perf tools create events as disabled and we want to check
|
||||
* the params for them.
|
||||
* This is a quick hack that will be removed soon, once we remove
|
||||
* the tmp breakpoints from ptrace
|
||||
*/
|
||||
if (!bp->attr.disabled || !bp->overflow_handler)
|
||||
ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
|
||||
ret = validate_hw_breakpoint(bp);
|
||||
|
||||
/* if arch_validate_hwbkpt_settings() fails then release bp slot */
|
||||
if (ret)
|
||||
@ -373,7 +444,7 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
|
||||
if (attr->disabled)
|
||||
goto end;
|
||||
|
||||
err = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
|
||||
err = validate_hw_breakpoint(bp);
|
||||
if (!err)
|
||||
perf_event_enable(bp);
|
||||
|
||||
@ -480,7 +551,36 @@ static struct notifier_block hw_breakpoint_exceptions_nb = {
|
||||
|
||||
static int __init init_hw_breakpoint(void)
|
||||
{
|
||||
unsigned int **task_bp_pinned;
|
||||
int cpu, err_cpu;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TYPE_MAX; i++)
|
||||
nr_slots[i] = hw_breakpoint_slots(i);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
for (i = 0; i < TYPE_MAX; i++) {
|
||||
task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu);
|
||||
*task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i],
|
||||
GFP_KERNEL);
|
||||
if (!*task_bp_pinned)
|
||||
goto err_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
constraints_initialized = 1;
|
||||
|
||||
return register_die_notifier(&hw_breakpoint_exceptions_nb);
|
||||
|
||||
err_alloc:
|
||||
for_each_possible_cpu(err_cpu) {
|
||||
if (err_cpu == cpu)
|
||||
break;
|
||||
for (i = 0; i < TYPE_MAX; i++)
|
||||
kfree(per_cpu(nr_task_bp_pinned[i], cpu));
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
core_initcall(init_hw_breakpoint);
|
||||
|
||||
|
132
kernel/kprobes.c
132
kernel/kprobes.c
@ -1588,6 +1588,72 @@ static void __kprobes kill_kprobe(struct kprobe *p)
|
||||
arch_remove_kprobe(p);
|
||||
}
|
||||
|
||||
/* Disable one kprobe */
|
||||
int __kprobes disable_kprobe(struct kprobe *kp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kprobe *p;
|
||||
|
||||
mutex_lock(&kprobe_mutex);
|
||||
|
||||
/* Check whether specified probe is valid. */
|
||||
p = __get_valid_kprobe(kp);
|
||||
if (unlikely(p == NULL)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the probe is already disabled (or gone), just return */
|
||||
if (kprobe_disabled(kp))
|
||||
goto out;
|
||||
|
||||
kp->flags |= KPROBE_FLAG_DISABLED;
|
||||
if (p != kp)
|
||||
/* When kp != p, p is always enabled. */
|
||||
try_to_disable_aggr_kprobe(p);
|
||||
|
||||
if (!kprobes_all_disarmed && kprobe_disabled(p))
|
||||
disarm_kprobe(p);
|
||||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(disable_kprobe);
|
||||
|
||||
/* Enable one kprobe */
|
||||
int __kprobes enable_kprobe(struct kprobe *kp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kprobe *p;
|
||||
|
||||
mutex_lock(&kprobe_mutex);
|
||||
|
||||
/* Check whether specified probe is valid. */
|
||||
p = __get_valid_kprobe(kp);
|
||||
if (unlikely(p == NULL)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kprobe_gone(kp)) {
|
||||
/* This kprobe has gone, we couldn't enable it. */
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p != kp)
|
||||
kp->flags &= ~KPROBE_FLAG_DISABLED;
|
||||
|
||||
if (!kprobes_all_disarmed && kprobe_disabled(p)) {
|
||||
p->flags &= ~KPROBE_FLAG_DISABLED;
|
||||
arm_kprobe(p);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(enable_kprobe);
|
||||
|
||||
void __kprobes dump_kprobe(struct kprobe *kp)
|
||||
{
|
||||
printk(KERN_WARNING "Dumping kprobe:\n");
|
||||
@ -1805,72 +1871,6 @@ static const struct file_operations debugfs_kprobes_operations = {
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/* Disable one kprobe */
|
||||
int __kprobes disable_kprobe(struct kprobe *kp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kprobe *p;
|
||||
|
||||
mutex_lock(&kprobe_mutex);
|
||||
|
||||
/* Check whether specified probe is valid. */
|
||||
p = __get_valid_kprobe(kp);
|
||||
if (unlikely(p == NULL)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the probe is already disabled (or gone), just return */
|
||||
if (kprobe_disabled(kp))
|
||||
goto out;
|
||||
|
||||
kp->flags |= KPROBE_FLAG_DISABLED;
|
||||
if (p != kp)
|
||||
/* When kp != p, p is always enabled. */
|
||||
try_to_disable_aggr_kprobe(p);
|
||||
|
||||
if (!kprobes_all_disarmed && kprobe_disabled(p))
|
||||
disarm_kprobe(p);
|
||||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(disable_kprobe);
|
||||
|
||||
/* Enable one kprobe */
|
||||
int __kprobes enable_kprobe(struct kprobe *kp)
|
||||
{
|
||||
int ret = 0;
|
||||
struct kprobe *p;
|
||||
|
||||
mutex_lock(&kprobe_mutex);
|
||||
|
||||
/* Check whether specified probe is valid. */
|
||||
p = __get_valid_kprobe(kp);
|
||||
if (unlikely(p == NULL)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (kprobe_gone(kp)) {
|
||||
/* This kprobe has gone, we couldn't enable it. */
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (p != kp)
|
||||
kp->flags &= ~KPROBE_FLAG_DISABLED;
|
||||
|
||||
if (!kprobes_all_disarmed && kprobe_disabled(p)) {
|
||||
p->flags &= ~KPROBE_FLAG_DISABLED;
|
||||
arm_kprobe(p);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(enable_kprobe);
|
||||
|
||||
static void __kprobes arm_all_kprobes(void)
|
||||
{
|
||||
struct hlist_head *head;
|
||||
|
@ -3232,7 +3232,7 @@ void lock_release(struct lockdep_map *lock, int nested,
|
||||
raw_local_irq_save(flags);
|
||||
check_flags(flags);
|
||||
current->lockdep_recursion = 1;
|
||||
trace_lock_release(lock, nested, ip);
|
||||
trace_lock_release(lock, ip);
|
||||
__lock_release(lock, nested, ip);
|
||||
current->lockdep_recursion = 0;
|
||||
raw_local_irq_restore(flags);
|
||||
@ -3385,7 +3385,7 @@ found_it:
|
||||
hlock->holdtime_stamp = now;
|
||||
}
|
||||
|
||||
trace_lock_acquired(lock, ip, waittime);
|
||||
trace_lock_acquired(lock, ip);
|
||||
|
||||
stats = get_lock_stats(hlock_class(hlock));
|
||||
if (waittime) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/percpu.h>
|
||||
@ -82,14 +83,6 @@ extern __weak const struct pmu *hw_perf_event_init(struct perf_event *event)
|
||||
void __weak hw_perf_disable(void) { barrier(); }
|
||||
void __weak hw_perf_enable(void) { barrier(); }
|
||||
|
||||
int __weak
|
||||
hw_perf_group_sched_in(struct perf_event *group_leader,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __weak perf_event_print_debug(void) { }
|
||||
|
||||
static DEFINE_PER_CPU(int, perf_disable_count);
|
||||
@ -262,6 +255,18 @@ static void update_event_times(struct perf_event *event)
|
||||
event->total_time_running = run_end - event->tstamp_running;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update total_time_enabled and total_time_running for all events in a group.
|
||||
*/
|
||||
static void update_group_times(struct perf_event *leader)
|
||||
{
|
||||
struct perf_event *event;
|
||||
|
||||
update_event_times(leader);
|
||||
list_for_each_entry(event, &leader->sibling_list, group_entry)
|
||||
update_event_times(event);
|
||||
}
|
||||
|
||||
static struct list_head *
|
||||
ctx_group_list(struct perf_event *event, struct perf_event_context *ctx)
|
||||
{
|
||||
@ -315,8 +320,6 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
static void
|
||||
list_del_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
{
|
||||
struct perf_event *sibling, *tmp;
|
||||
|
||||
if (list_empty(&event->group_entry))
|
||||
return;
|
||||
ctx->nr_events--;
|
||||
@ -329,7 +332,7 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
if (event->group_leader != event)
|
||||
event->group_leader->nr_siblings--;
|
||||
|
||||
update_event_times(event);
|
||||
update_group_times(event);
|
||||
|
||||
/*
|
||||
* If event was in error state, then keep it
|
||||
@ -340,6 +343,12 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
*/
|
||||
if (event->state > PERF_EVENT_STATE_OFF)
|
||||
event->state = PERF_EVENT_STATE_OFF;
|
||||
}
|
||||
|
||||
static void
|
||||
perf_destroy_group(struct perf_event *event, struct perf_event_context *ctx)
|
||||
{
|
||||
struct perf_event *sibling, *tmp;
|
||||
|
||||
/*
|
||||
* If this was a group event with sibling events then
|
||||
@ -504,18 +513,6 @@ retry:
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update total_time_enabled and total_time_running for all events in a group.
|
||||
*/
|
||||
static void update_group_times(struct perf_event *leader)
|
||||
{
|
||||
struct perf_event *event;
|
||||
|
||||
update_event_times(leader);
|
||||
list_for_each_entry(event, &leader->sibling_list, group_entry)
|
||||
update_event_times(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cross CPU call to disable a performance event
|
||||
*/
|
||||
@ -640,15 +637,20 @@ group_sched_in(struct perf_event *group_event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx)
|
||||
{
|
||||
struct perf_event *event, *partial_group;
|
||||
struct perf_event *event, *partial_group = NULL;
|
||||
const struct pmu *pmu = group_event->pmu;
|
||||
bool txn = false;
|
||||
int ret;
|
||||
|
||||
if (group_event->state == PERF_EVENT_STATE_OFF)
|
||||
return 0;
|
||||
|
||||
ret = hw_perf_group_sched_in(group_event, cpuctx, ctx);
|
||||
if (ret)
|
||||
return ret < 0 ? ret : 0;
|
||||
/* Check if group transaction availabe */
|
||||
if (pmu->start_txn)
|
||||
txn = true;
|
||||
|
||||
if (txn)
|
||||
pmu->start_txn(pmu);
|
||||
|
||||
if (event_sched_in(group_event, cpuctx, ctx))
|
||||
return -EAGAIN;
|
||||
@ -663,9 +665,19 @@ group_sched_in(struct perf_event *group_event,
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (!txn)
|
||||
return 0;
|
||||
|
||||
ret = pmu->commit_txn(pmu);
|
||||
if (!ret) {
|
||||
pmu->cancel_txn(pmu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
group_error:
|
||||
if (txn)
|
||||
pmu->cancel_txn(pmu);
|
||||
|
||||
/*
|
||||
* Groups can be scheduled in as one unit only, so undo any
|
||||
* partial group before returning:
|
||||
@ -1367,6 +1379,8 @@ void perf_event_task_sched_in(struct task_struct *task)
|
||||
if (cpuctx->task_ctx == ctx)
|
||||
return;
|
||||
|
||||
perf_disable();
|
||||
|
||||
/*
|
||||
* We want to keep the following priority order:
|
||||
* cpu pinned (that don't need to move), task pinned,
|
||||
@ -1379,6 +1393,8 @@ void perf_event_task_sched_in(struct task_struct *task)
|
||||
ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE);
|
||||
|
||||
cpuctx->task_ctx = ctx;
|
||||
|
||||
perf_enable();
|
||||
}
|
||||
|
||||
#define MAX_INTERRUPTS (~0ULL)
|
||||
@ -1856,9 +1872,30 @@ int perf_event_release_kernel(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
|
||||
/*
|
||||
* Remove from the PMU, can't get re-enabled since we got
|
||||
* here because the last ref went.
|
||||
*/
|
||||
perf_event_disable(event);
|
||||
|
||||
WARN_ON_ONCE(ctx->parent_ctx);
|
||||
mutex_lock(&ctx->mutex);
|
||||
perf_event_remove_from_context(event);
|
||||
/*
|
||||
* There are two ways this annotation is useful:
|
||||
*
|
||||
* 1) there is a lock recursion from perf_event_exit_task
|
||||
* see the comment there.
|
||||
*
|
||||
* 2) there is a lock-inversion with mmap_sem through
|
||||
* perf_event_read_group(), which takes faults while
|
||||
* holding ctx->mutex, however this is called after
|
||||
* the last filedesc died, so there is no possibility
|
||||
* to trigger the AB-BA case.
|
||||
*/
|
||||
mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING);
|
||||
raw_spin_lock_irq(&ctx->lock);
|
||||
list_del_event(event, ctx);
|
||||
perf_destroy_group(event, ctx);
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
mutex_unlock(&ctx->mutex);
|
||||
|
||||
mutex_lock(&event->owner->perf_event_mutex);
|
||||
@ -2642,6 +2679,7 @@ static int perf_fasync(int fd, struct file *filp, int on)
|
||||
}
|
||||
|
||||
static const struct file_operations perf_fops = {
|
||||
.llseek = no_llseek,
|
||||
.release = perf_release,
|
||||
.read = perf_read,
|
||||
.poll = perf_poll,
|
||||
@ -2791,6 +2829,27 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We assume there is only KVM supporting the callbacks.
|
||||
* Later on, we might change it to a list if there is
|
||||
* another virtualization implementation supporting the callbacks.
|
||||
*/
|
||||
struct perf_guest_info_callbacks *perf_guest_cbs;
|
||||
|
||||
int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
|
||||
{
|
||||
perf_guest_cbs = cbs;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks);
|
||||
|
||||
int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
|
||||
{
|
||||
perf_guest_cbs = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
|
||||
|
||||
/*
|
||||
* Output
|
||||
*/
|
||||
@ -3743,7 +3802,7 @@ void __perf_event_mmap(struct vm_area_struct *vma)
|
||||
.event_id = {
|
||||
.header = {
|
||||
.type = PERF_RECORD_MMAP,
|
||||
.misc = 0,
|
||||
.misc = PERF_RECORD_MISC_USER,
|
||||
/* .size */
|
||||
},
|
||||
/* .pid */
|
||||
@ -3961,36 +4020,6 @@ static void perf_swevent_add(struct perf_event *event, u64 nr,
|
||||
perf_swevent_overflow(event, 0, nmi, data, regs);
|
||||
}
|
||||
|
||||
static int perf_swevent_is_counting(struct perf_event *event)
|
||||
{
|
||||
/*
|
||||
* The event is active, we're good!
|
||||
*/
|
||||
if (event->state == PERF_EVENT_STATE_ACTIVE)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* The event is off/error, not counting.
|
||||
*/
|
||||
if (event->state != PERF_EVENT_STATE_INACTIVE)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The event is inactive, if the context is active
|
||||
* we're part of a group that didn't make it on the 'pmu',
|
||||
* not counting.
|
||||
*/
|
||||
if (event->ctx->is_active)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We're inactive and the context is too, this means the
|
||||
* task is scheduled out, we're counting events that happen
|
||||
* to us, like migration events.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int perf_tp_event_match(struct perf_event *event,
|
||||
struct perf_sample_data *data);
|
||||
|
||||
@ -4014,12 +4043,6 @@ static int perf_swevent_match(struct perf_event *event,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (event->cpu != -1 && event->cpu != smp_processor_id())
|
||||
return 0;
|
||||
|
||||
if (!perf_swevent_is_counting(event))
|
||||
return 0;
|
||||
|
||||
if (event->attr.type != type)
|
||||
return 0;
|
||||
|
||||
@ -4036,18 +4059,53 @@ static int perf_swevent_match(struct perf_event *event,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void perf_swevent_ctx_event(struct perf_event_context *ctx,
|
||||
enum perf_type_id type,
|
||||
u32 event_id, u64 nr, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
static inline u64 swevent_hash(u64 type, u32 event_id)
|
||||
{
|
||||
struct perf_event *event;
|
||||
u64 val = event_id | (type << 32);
|
||||
|
||||
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
|
||||
return hash_64(val, SWEVENT_HLIST_BITS);
|
||||
}
|
||||
|
||||
static struct hlist_head *
|
||||
find_swevent_head(struct perf_cpu_context *ctx, u64 type, u32 event_id)
|
||||
{
|
||||
u64 hash;
|
||||
struct swevent_hlist *hlist;
|
||||
|
||||
hash = swevent_hash(type, event_id);
|
||||
|
||||
hlist = rcu_dereference(ctx->swevent_hlist);
|
||||
if (!hlist)
|
||||
return NULL;
|
||||
|
||||
return &hlist->heads[hash];
|
||||
}
|
||||
|
||||
static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
|
||||
u64 nr, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx;
|
||||
struct perf_event *event;
|
||||
struct hlist_node *node;
|
||||
struct hlist_head *head;
|
||||
|
||||
cpuctx = &__get_cpu_var(perf_cpu_context);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
head = find_swevent_head(cpuctx, type, event_id);
|
||||
|
||||
if (!head)
|
||||
goto end;
|
||||
|
||||
hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
|
||||
if (perf_swevent_match(event, type, event_id, data, regs))
|
||||
perf_swevent_add(event, nr, nmi, data, regs);
|
||||
}
|
||||
end:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
int perf_swevent_get_recursion_context(void)
|
||||
@ -4085,27 +4143,6 @@ void perf_swevent_put_recursion_context(int rctx)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context);
|
||||
|
||||
static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
|
||||
u64 nr, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx;
|
||||
struct perf_event_context *ctx;
|
||||
|
||||
cpuctx = &__get_cpu_var(perf_cpu_context);
|
||||
rcu_read_lock();
|
||||
perf_swevent_ctx_event(&cpuctx->ctx, type, event_id,
|
||||
nr, nmi, data, regs);
|
||||
/*
|
||||
* doesn't really matter which of the child contexts the
|
||||
* events ends up in.
|
||||
*/
|
||||
ctx = rcu_dereference(current->perf_event_ctxp);
|
||||
if (ctx)
|
||||
perf_swevent_ctx_event(ctx, type, event_id, nr, nmi, data, regs);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void __perf_sw_event(u32 event_id, u64 nr, int nmi,
|
||||
struct pt_regs *regs, u64 addr)
|
||||
@ -4131,16 +4168,28 @@ static void perf_swevent_read(struct perf_event *event)
|
||||
static int perf_swevent_enable(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
struct perf_cpu_context *cpuctx;
|
||||
struct hlist_head *head;
|
||||
|
||||
cpuctx = &__get_cpu_var(perf_cpu_context);
|
||||
|
||||
if (hwc->sample_period) {
|
||||
hwc->last_period = hwc->sample_period;
|
||||
perf_swevent_set_period(event);
|
||||
}
|
||||
|
||||
head = find_swevent_head(cpuctx, event->attr.type, event->attr.config);
|
||||
if (WARN_ON_ONCE(!head))
|
||||
return -EINVAL;
|
||||
|
||||
hlist_add_head_rcu(&event->hlist_entry, head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_swevent_disable(struct perf_event *event)
|
||||
{
|
||||
hlist_del_rcu(&event->hlist_entry);
|
||||
}
|
||||
|
||||
static const struct pmu perf_ops_generic = {
|
||||
@ -4168,15 +4217,8 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
|
||||
perf_sample_data_init(&data, 0);
|
||||
data.period = event->hw.last_period;
|
||||
regs = get_irq_regs();
|
||||
/*
|
||||
* In case we exclude kernel IPs or are somehow not in interrupt
|
||||
* context, provide the next best thing, the user IP.
|
||||
*/
|
||||
if ((event->attr.exclude_kernel || !regs) &&
|
||||
!event->attr.exclude_user)
|
||||
regs = task_pt_regs(current);
|
||||
|
||||
if (regs) {
|
||||
if (regs && !perf_exclude_event(event, regs)) {
|
||||
if (!(event->attr.exclude_idle && current->pid == 0))
|
||||
if (perf_event_overflow(event, 0, &data, regs))
|
||||
ret = HRTIMER_NORESTART;
|
||||
@ -4324,6 +4366,105 @@ static const struct pmu perf_ops_task_clock = {
|
||||
.read = task_clock_perf_event_read,
|
||||
};
|
||||
|
||||
static void swevent_hlist_release_rcu(struct rcu_head *rcu_head)
|
||||
{
|
||||
struct swevent_hlist *hlist;
|
||||
|
||||
hlist = container_of(rcu_head, struct swevent_hlist, rcu_head);
|
||||
kfree(hlist);
|
||||
}
|
||||
|
||||
static void swevent_hlist_release(struct perf_cpu_context *cpuctx)
|
||||
{
|
||||
struct swevent_hlist *hlist;
|
||||
|
||||
if (!cpuctx->swevent_hlist)
|
||||
return;
|
||||
|
||||
hlist = cpuctx->swevent_hlist;
|
||||
rcu_assign_pointer(cpuctx->swevent_hlist, NULL);
|
||||
call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu);
|
||||
}
|
||||
|
||||
static void swevent_hlist_put_cpu(struct perf_event *event, int cpu)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||
|
||||
mutex_lock(&cpuctx->hlist_mutex);
|
||||
|
||||
if (!--cpuctx->hlist_refcount)
|
||||
swevent_hlist_release(cpuctx);
|
||||
|
||||
mutex_unlock(&cpuctx->hlist_mutex);
|
||||
}
|
||||
|
||||
static void swevent_hlist_put(struct perf_event *event)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (event->cpu != -1) {
|
||||
swevent_hlist_put_cpu(event, event->cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
swevent_hlist_put_cpu(event, cpu);
|
||||
}
|
||||
|
||||
static int swevent_hlist_get_cpu(struct perf_event *event, int cpu)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&cpuctx->hlist_mutex);
|
||||
|
||||
if (!cpuctx->swevent_hlist && cpu_online(cpu)) {
|
||||
struct swevent_hlist *hlist;
|
||||
|
||||
hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
|
||||
if (!hlist) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
|
||||
}
|
||||
cpuctx->hlist_refcount++;
|
||||
exit:
|
||||
mutex_unlock(&cpuctx->hlist_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int swevent_hlist_get(struct perf_event *event)
|
||||
{
|
||||
int err;
|
||||
int cpu, failed_cpu;
|
||||
|
||||
if (event->cpu != -1)
|
||||
return swevent_hlist_get_cpu(event, event->cpu);
|
||||
|
||||
get_online_cpus();
|
||||
for_each_possible_cpu(cpu) {
|
||||
err = swevent_hlist_get_cpu(event, cpu);
|
||||
if (err) {
|
||||
failed_cpu = cpu;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
put_online_cpus();
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == failed_cpu)
|
||||
break;
|
||||
swevent_hlist_put_cpu(event, cpu);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EVENT_TRACING
|
||||
|
||||
void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
|
||||
@ -4357,10 +4498,13 @@ static int perf_tp_event_match(struct perf_event *event,
|
||||
static void tp_perf_event_destroy(struct perf_event *event)
|
||||
{
|
||||
perf_trace_disable(event->attr.config);
|
||||
swevent_hlist_put(event);
|
||||
}
|
||||
|
||||
static const struct pmu *tp_perf_event_init(struct perf_event *event)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Raw tracepoint data is a severe data leak, only allow root to
|
||||
* have these.
|
||||
@ -4374,6 +4518,11 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event)
|
||||
return NULL;
|
||||
|
||||
event->destroy = tp_perf_event_destroy;
|
||||
err = swevent_hlist_get(event);
|
||||
if (err) {
|
||||
perf_trace_disable(event->attr.config);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return &perf_ops_generic;
|
||||
}
|
||||
@ -4474,6 +4623,7 @@ static void sw_perf_event_destroy(struct perf_event *event)
|
||||
WARN_ON(event->parent);
|
||||
|
||||
atomic_dec(&perf_swevent_enabled[event_id]);
|
||||
swevent_hlist_put(event);
|
||||
}
|
||||
|
||||
static const struct pmu *sw_perf_event_init(struct perf_event *event)
|
||||
@ -4512,6 +4662,12 @@ static const struct pmu *sw_perf_event_init(struct perf_event *event)
|
||||
case PERF_COUNT_SW_ALIGNMENT_FAULTS:
|
||||
case PERF_COUNT_SW_EMULATION_FAULTS:
|
||||
if (!event->parent) {
|
||||
int err;
|
||||
|
||||
err = swevent_hlist_get(event);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
atomic_inc(&perf_swevent_enabled[event_id]);
|
||||
event->destroy = sw_perf_event_destroy;
|
||||
}
|
||||
@ -5176,7 +5332,7 @@ void perf_event_exit_task(struct task_struct *child)
|
||||
*
|
||||
* But since its the parent context it won't be the same instance.
|
||||
*/
|
||||
mutex_lock_nested(&child_ctx->mutex, SINGLE_DEPTH_NESTING);
|
||||
mutex_lock(&child_ctx->mutex);
|
||||
|
||||
again:
|
||||
list_for_each_entry_safe(child_event, tmp, &child_ctx->pinned_groups,
|
||||
@ -5384,6 +5540,7 @@ static void __init perf_event_init_all_cpus(void)
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||
mutex_init(&cpuctx->hlist_mutex);
|
||||
__perf_event_init_context(&cpuctx->ctx, NULL);
|
||||
}
|
||||
}
|
||||
@ -5397,6 +5554,16 @@ static void __cpuinit perf_event_init_cpu(int cpu)
|
||||
spin_lock(&perf_resource_lock);
|
||||
cpuctx->max_pertask = perf_max_events - perf_reserved_percpu;
|
||||
spin_unlock(&perf_resource_lock);
|
||||
|
||||
mutex_lock(&cpuctx->hlist_mutex);
|
||||
if (cpuctx->hlist_refcount > 0) {
|
||||
struct swevent_hlist *hlist;
|
||||
|
||||
hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
|
||||
WARN_ON_ONCE(!hlist);
|
||||
rcu_assign_pointer(cpuctx->swevent_hlist, hlist);
|
||||
}
|
||||
mutex_unlock(&cpuctx->hlist_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
@ -5416,6 +5583,10 @@ static void perf_event_exit_cpu(int cpu)
|
||||
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||
struct perf_event_context *ctx = &cpuctx->ctx;
|
||||
|
||||
mutex_lock(&cpuctx->hlist_mutex);
|
||||
swevent_hlist_release(cpuctx);
|
||||
mutex_unlock(&cpuctx->hlist_mutex);
|
||||
|
||||
mutex_lock(&ctx->mutex);
|
||||
smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1);
|
||||
mutex_unlock(&ctx->mutex);
|
||||
|
@ -75,7 +75,6 @@ void __ptrace_unlink(struct task_struct *child)
|
||||
child->parent = child->real_parent;
|
||||
list_del_init(&child->ptrace_entry);
|
||||
|
||||
arch_ptrace_untrace(child);
|
||||
if (task_is_traced(child))
|
||||
ptrace_untrace(child);
|
||||
}
|
||||
|
@ -2087,49 +2087,6 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait_task_context_switch - wait for a thread to complete at least one
|
||||
* context switch.
|
||||
*
|
||||
* @p must not be current.
|
||||
*/
|
||||
void wait_task_context_switch(struct task_struct *p)
|
||||
{
|
||||
unsigned long nvcsw, nivcsw, flags;
|
||||
int running;
|
||||
struct rq *rq;
|
||||
|
||||
nvcsw = p->nvcsw;
|
||||
nivcsw = p->nivcsw;
|
||||
for (;;) {
|
||||
/*
|
||||
* The runqueue is assigned before the actual context
|
||||
* switch. We need to take the runqueue lock.
|
||||
*
|
||||
* We could check initially without the lock but it is
|
||||
* very likely that we need to take the lock in every
|
||||
* iteration.
|
||||
*/
|
||||
rq = task_rq_lock(p, &flags);
|
||||
running = task_running(rq, p);
|
||||
task_rq_unlock(rq, &flags);
|
||||
|
||||
if (likely(!running))
|
||||
break;
|
||||
/*
|
||||
* The switch count is incremented before the actual
|
||||
* context switch. We thus wait for two switches to be
|
||||
* sure at least one completed.
|
||||
*/
|
||||
if ((p->nvcsw - nvcsw) > 1)
|
||||
break;
|
||||
if ((p->nivcsw - nivcsw) > 1)
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* wait_task_inactive - wait for a thread to unschedule.
|
||||
*
|
||||
|
@ -44,9 +44,6 @@ config HAVE_FTRACE_MCOUNT_RECORD
|
||||
help
|
||||
See Documentation/trace/ftrace-design.txt
|
||||
|
||||
config HAVE_HW_BRANCH_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_SYSCALL_TRACEPOINTS
|
||||
bool
|
||||
help
|
||||
@ -374,14 +371,6 @@ config STACK_TRACER
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config HW_BRANCH_TRACER
|
||||
depends on HAVE_HW_BRANCH_TRACER
|
||||
bool "Trace hw branches"
|
||||
select GENERIC_TRACER
|
||||
help
|
||||
This tracer records all branches on the system in a circular
|
||||
buffer, giving access to the last N branches for each cpu.
|
||||
|
||||
config KMEMTRACE
|
||||
bool "Trace SLAB allocations"
|
||||
select GENERIC_TRACER
|
||||
|
@ -41,7 +41,6 @@ obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
|
||||
obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
|
||||
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
|
||||
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
|
||||
obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
|
||||
obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o
|
||||
obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
|
||||
|
@ -34,7 +34,6 @@ enum trace_type {
|
||||
TRACE_GRAPH_RET,
|
||||
TRACE_GRAPH_ENT,
|
||||
TRACE_USER_STACK,
|
||||
TRACE_HW_BRANCHES,
|
||||
TRACE_KMEM_ALLOC,
|
||||
TRACE_KMEM_FREE,
|
||||
TRACE_BLK,
|
||||
@ -103,29 +102,17 @@ struct syscall_trace_exit {
|
||||
long ret;
|
||||
};
|
||||
|
||||
struct kprobe_trace_entry {
|
||||
struct kprobe_trace_entry_head {
|
||||
struct trace_entry ent;
|
||||
unsigned long ip;
|
||||
int nargs;
|
||||
unsigned long args[];
|
||||
};
|
||||
|
||||
#define SIZEOF_KPROBE_TRACE_ENTRY(n) \
|
||||
(offsetof(struct kprobe_trace_entry, args) + \
|
||||
(sizeof(unsigned long) * (n)))
|
||||
|
||||
struct kretprobe_trace_entry {
|
||||
struct kretprobe_trace_entry_head {
|
||||
struct trace_entry ent;
|
||||
unsigned long func;
|
||||
unsigned long ret_ip;
|
||||
int nargs;
|
||||
unsigned long args[];
|
||||
};
|
||||
|
||||
#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \
|
||||
(offsetof(struct kretprobe_trace_entry, args) + \
|
||||
(sizeof(unsigned long) * (n)))
|
||||
|
||||
/*
|
||||
* trace_flag_type is an enumeration that holds different
|
||||
* states when a trace occurs. These are:
|
||||
@ -229,7 +216,6 @@ extern void __ftrace_bad_type(void);
|
||||
TRACE_GRAPH_ENT); \
|
||||
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
|
||||
TRACE_GRAPH_RET); \
|
||||
IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\
|
||||
IF_ASSIGN(var, ent, struct kmemtrace_alloc_entry, \
|
||||
TRACE_KMEM_ALLOC); \
|
||||
IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \
|
||||
@ -467,8 +453,6 @@ extern int trace_selftest_startup_sysprof(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_hw_branches(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_ksym(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
#endif /* CONFIG_FTRACE_STARTUP_TEST */
|
||||
|
@ -318,18 +318,6 @@ FTRACE_ENTRY(branch, trace_branch,
|
||||
__entry->func, __entry->file, __entry->correct)
|
||||
);
|
||||
|
||||
FTRACE_ENTRY(hw_branch, hw_branch_entry,
|
||||
|
||||
TRACE_HW_BRANCHES,
|
||||
|
||||
F_STRUCT(
|
||||
__field( u64, from )
|
||||
__field( u64, to )
|
||||
),
|
||||
|
||||
F_printk("from: %llx to: %llx", __entry->from, __entry->to)
|
||||
);
|
||||
|
||||
FTRACE_ENTRY(kmem_alloc, kmemtrace_alloc_entry,
|
||||
|
||||
TRACE_KMEM_ALLOC,
|
||||
|
@ -1398,7 +1398,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
|
||||
}
|
||||
|
||||
err = -EINVAL;
|
||||
if (!call)
|
||||
if (&call->list == &ftrace_events)
|
||||
goto out_unlock;
|
||||
|
||||
err = -EEXIST;
|
||||
|
@ -1,312 +0,0 @@
|
||||
/*
|
||||
* h/w branch tracer for x86 based on BTS
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@gmail.com>, 2008-2009
|
||||
*/
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <asm/ds.h>
|
||||
|
||||
#include "trace_output.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#define BTS_BUFFER_SIZE (1 << 13)
|
||||
|
||||
static DEFINE_PER_CPU(struct bts_tracer *, hwb_tracer);
|
||||
static DEFINE_PER_CPU(unsigned char[BTS_BUFFER_SIZE], hwb_buffer);
|
||||
|
||||
#define this_tracer per_cpu(hwb_tracer, smp_processor_id())
|
||||
|
||||
static int trace_hw_branches_enabled __read_mostly;
|
||||
static int trace_hw_branches_suspended __read_mostly;
|
||||
static struct trace_array *hw_branch_trace __read_mostly;
|
||||
|
||||
|
||||
static void bts_trace_init_cpu(int cpu)
|
||||
{
|
||||
per_cpu(hwb_tracer, cpu) =
|
||||
ds_request_bts_cpu(cpu, per_cpu(hwb_buffer, cpu),
|
||||
BTS_BUFFER_SIZE, NULL, (size_t)-1,
|
||||
BTS_KERNEL);
|
||||
|
||||
if (IS_ERR(per_cpu(hwb_tracer, cpu)))
|
||||
per_cpu(hwb_tracer, cpu) = NULL;
|
||||
}
|
||||
|
||||
static int bts_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
hw_branch_trace = tr;
|
||||
trace_hw_branches_enabled = 0;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
bts_trace_init_cpu(cpu);
|
||||
|
||||
if (likely(per_cpu(hwb_tracer, cpu)))
|
||||
trace_hw_branches_enabled = 1;
|
||||
}
|
||||
trace_hw_branches_suspended = 0;
|
||||
put_online_cpus();
|
||||
|
||||
/* If we could not enable tracing on a single cpu, we fail. */
|
||||
return trace_hw_branches_enabled ? 0 : -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void bts_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
if (likely(per_cpu(hwb_tracer, cpu))) {
|
||||
ds_release_bts(per_cpu(hwb_tracer, cpu));
|
||||
per_cpu(hwb_tracer, cpu) = NULL;
|
||||
}
|
||||
}
|
||||
trace_hw_branches_enabled = 0;
|
||||
trace_hw_branches_suspended = 0;
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static void bts_trace_start(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu)
|
||||
if (likely(per_cpu(hwb_tracer, cpu)))
|
||||
ds_resume_bts(per_cpu(hwb_tracer, cpu));
|
||||
trace_hw_branches_suspended = 0;
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static void bts_trace_stop(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu)
|
||||
if (likely(per_cpu(hwb_tracer, cpu)))
|
||||
ds_suspend_bts(per_cpu(hwb_tracer, cpu));
|
||||
trace_hw_branches_suspended = 1;
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
int cpu = (long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
case CPU_ONLINE:
|
||||
case CPU_DOWN_FAILED:
|
||||
/* The notification is sent with interrupts enabled. */
|
||||
if (trace_hw_branches_enabled) {
|
||||
bts_trace_init_cpu(cpu);
|
||||
|
||||
if (trace_hw_branches_suspended &&
|
||||
likely(per_cpu(hwb_tracer, cpu)))
|
||||
ds_suspend_bts(per_cpu(hwb_tracer, cpu));
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_DOWN_PREPARE:
|
||||
/* The notification is sent with interrupts enabled. */
|
||||
if (likely(per_cpu(hwb_tracer, cpu))) {
|
||||
ds_release_bts(per_cpu(hwb_tracer, cpu));
|
||||
per_cpu(hwb_tracer, cpu) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block bts_hotcpu_notifier __cpuinitdata = {
|
||||
.notifier_call = bts_hotcpu_handler
|
||||
};
|
||||
|
||||
static void bts_trace_print_header(struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, "# CPU# TO <- FROM\n");
|
||||
}
|
||||
|
||||
static enum print_line_t bts_trace_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
unsigned long symflags = TRACE_ITER_SYM_OFFSET;
|
||||
struct trace_entry *entry = iter->ent;
|
||||
struct trace_seq *seq = &iter->seq;
|
||||
struct hw_branch_entry *it;
|
||||
|
||||
trace_assign_type(it, entry);
|
||||
|
||||
if (entry->type == TRACE_HW_BRANCHES) {
|
||||
if (trace_seq_printf(seq, "%4d ", iter->cpu) &&
|
||||
seq_print_ip_sym(seq, it->to, symflags) &&
|
||||
trace_seq_printf(seq, "\t <- ") &&
|
||||
seq_print_ip_sym(seq, it->from, symflags) &&
|
||||
trace_seq_printf(seq, "\n"))
|
||||
return TRACE_TYPE_HANDLED;
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
}
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
|
||||
void trace_hw_branch(u64 from, u64 to)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_hw_branch;
|
||||
struct trace_array *tr = hw_branch_trace;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buf;
|
||||
struct hw_branch_entry *entry;
|
||||
unsigned long irq1;
|
||||
int cpu;
|
||||
|
||||
if (unlikely(!tr))
|
||||
return;
|
||||
|
||||
if (unlikely(!trace_hw_branches_enabled))
|
||||
return;
|
||||
|
||||
local_irq_save(irq1);
|
||||
cpu = raw_smp_processor_id();
|
||||
if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
|
||||
goto out;
|
||||
|
||||
buf = tr->buffer;
|
||||
event = trace_buffer_lock_reserve(buf, TRACE_HW_BRANCHES,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, from);
|
||||
entry->ent.type = TRACE_HW_BRANCHES;
|
||||
entry->from = from;
|
||||
entry->to = to;
|
||||
if (!filter_check_discard(call, entry, buf, event))
|
||||
trace_buffer_unlock_commit(buf, event, 0, 0);
|
||||
|
||||
out:
|
||||
atomic_dec(&tr->data[cpu]->disabled);
|
||||
local_irq_restore(irq1);
|
||||
}
|
||||
|
||||
static void trace_bts_at(const struct bts_trace *trace, void *at)
|
||||
{
|
||||
struct bts_struct bts;
|
||||
int err = 0;
|
||||
|
||||
WARN_ON_ONCE(!trace->read);
|
||||
if (!trace->read)
|
||||
return;
|
||||
|
||||
err = trace->read(this_tracer, at, &bts);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
switch (bts.qualifier) {
|
||||
case BTS_BRANCH:
|
||||
trace_hw_branch(bts.variant.lbr.from, bts.variant.lbr.to);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect the trace on the current cpu and write it into the ftrace buffer.
|
||||
*
|
||||
* pre: tracing must be suspended on the current cpu
|
||||
*/
|
||||
static void trace_bts_cpu(void *arg)
|
||||
{
|
||||
struct trace_array *tr = (struct trace_array *)arg;
|
||||
const struct bts_trace *trace;
|
||||
unsigned char *at;
|
||||
|
||||
if (unlikely(!tr))
|
||||
return;
|
||||
|
||||
if (unlikely(atomic_read(&tr->data[raw_smp_processor_id()]->disabled)))
|
||||
return;
|
||||
|
||||
if (unlikely(!this_tracer))
|
||||
return;
|
||||
|
||||
trace = ds_read_bts(this_tracer);
|
||||
if (!trace)
|
||||
return;
|
||||
|
||||
for (at = trace->ds.top; (void *)at < trace->ds.end;
|
||||
at += trace->ds.size)
|
||||
trace_bts_at(trace, at);
|
||||
|
||||
for (at = trace->ds.begin; (void *)at < trace->ds.top;
|
||||
at += trace->ds.size)
|
||||
trace_bts_at(trace, at);
|
||||
}
|
||||
|
||||
static void trace_bts_prepare(struct trace_iterator *iter)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu)
|
||||
if (likely(per_cpu(hwb_tracer, cpu)))
|
||||
ds_suspend_bts(per_cpu(hwb_tracer, cpu));
|
||||
/*
|
||||
* We need to collect the trace on the respective cpu since ftrace
|
||||
* implicitly adds the record for the current cpu.
|
||||
* Once that is more flexible, we could collect the data from any cpu.
|
||||
*/
|
||||
on_each_cpu(trace_bts_cpu, iter->tr, 1);
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
if (likely(per_cpu(hwb_tracer, cpu)))
|
||||
ds_resume_bts(per_cpu(hwb_tracer, cpu));
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static void trace_bts_close(struct trace_iterator *iter)
|
||||
{
|
||||
tracing_reset_online_cpus(iter->tr);
|
||||
}
|
||||
|
||||
void trace_hw_branch_oops(void)
|
||||
{
|
||||
if (this_tracer) {
|
||||
ds_suspend_bts_noirq(this_tracer);
|
||||
trace_bts_cpu(hw_branch_trace);
|
||||
ds_resume_bts_noirq(this_tracer);
|
||||
}
|
||||
}
|
||||
|
||||
struct tracer bts_tracer __read_mostly =
|
||||
{
|
||||
.name = "hw-branch-tracer",
|
||||
.init = bts_trace_init,
|
||||
.reset = bts_trace_reset,
|
||||
.print_header = bts_trace_print_header,
|
||||
.print_line = bts_trace_print_line,
|
||||
.start = bts_trace_start,
|
||||
.stop = bts_trace_stop,
|
||||
.open = trace_bts_prepare,
|
||||
.close = trace_bts_close,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_hw_branches,
|
||||
#endif /* CONFIG_FTRACE_SELFTEST */
|
||||
};
|
||||
|
||||
__init static int init_bts_trace(void)
|
||||
{
|
||||
register_hotcpu_notifier(&bts_hotcpu_notifier);
|
||||
return register_tracer(&bts_tracer);
|
||||
}
|
||||
device_initcall(init_bts_trace);
|
@ -29,6 +29,8 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/bitsperlong.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
@ -40,7 +42,6 @@
|
||||
|
||||
/* Reserved field names */
|
||||
#define FIELD_STRING_IP "__probe_ip"
|
||||
#define FIELD_STRING_NARGS "__probe_nargs"
|
||||
#define FIELD_STRING_RETIP "__probe_ret_ip"
|
||||
#define FIELD_STRING_FUNC "__probe_func"
|
||||
|
||||
@ -52,56 +53,102 @@ const char *reserved_field_names[] = {
|
||||
"common_tgid",
|
||||
"common_lock_depth",
|
||||
FIELD_STRING_IP,
|
||||
FIELD_STRING_NARGS,
|
||||
FIELD_STRING_RETIP,
|
||||
FIELD_STRING_FUNC,
|
||||
};
|
||||
|
||||
struct fetch_func {
|
||||
unsigned long (*func)(struct pt_regs *, void *);
|
||||
/* Printing function type */
|
||||
typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *);
|
||||
#define PRINT_TYPE_FUNC_NAME(type) print_type_##type
|
||||
#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type
|
||||
|
||||
/* Printing in basic type function template */
|
||||
#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \
|
||||
static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \
|
||||
const char *name, void *data)\
|
||||
{ \
|
||||
return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
|
||||
} \
|
||||
static const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
|
||||
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
|
||||
DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
|
||||
|
||||
/* Data fetch function type */
|
||||
typedef void (*fetch_func_t)(struct pt_regs *, void *, void *);
|
||||
|
||||
struct fetch_param {
|
||||
fetch_func_t fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static __kprobes unsigned long call_fetch(struct fetch_func *f,
|
||||
struct pt_regs *regs)
|
||||
static __kprobes void call_fetch(struct fetch_param *fprm,
|
||||
struct pt_regs *regs, void *dest)
|
||||
{
|
||||
return f->func(regs, f->data);
|
||||
return fprm->fn(regs, fprm->data, dest);
|
||||
}
|
||||
|
||||
/* fetch handlers */
|
||||
static __kprobes unsigned long fetch_register(struct pt_regs *regs,
|
||||
void *offset)
|
||||
{
|
||||
return regs_get_register(regs, (unsigned int)((unsigned long)offset));
|
||||
}
|
||||
#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type
|
||||
/*
|
||||
* Define macro for basic types - we don't need to define s* types, because
|
||||
* we have to care only about bitwidth at recording time.
|
||||
*/
|
||||
#define DEFINE_BASIC_FETCH_FUNCS(kind) \
|
||||
DEFINE_FETCH_##kind(u8) \
|
||||
DEFINE_FETCH_##kind(u16) \
|
||||
DEFINE_FETCH_##kind(u32) \
|
||||
DEFINE_FETCH_##kind(u64)
|
||||
|
||||
static __kprobes unsigned long fetch_stack(struct pt_regs *regs,
|
||||
void *num)
|
||||
{
|
||||
return regs_get_kernel_stack_nth(regs,
|
||||
(unsigned int)((unsigned long)num));
|
||||
}
|
||||
#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \
|
||||
((FETCH_FUNC_NAME(kind, u8) == fn) || \
|
||||
(FETCH_FUNC_NAME(kind, u16) == fn) || \
|
||||
(FETCH_FUNC_NAME(kind, u32) == fn) || \
|
||||
(FETCH_FUNC_NAME(kind, u64) == fn))
|
||||
|
||||
static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr)
|
||||
{
|
||||
unsigned long retval;
|
||||
|
||||
if (probe_kernel_address(addr, retval))
|
||||
return 0;
|
||||
return retval;
|
||||
/* Data fetch function templates */
|
||||
#define DEFINE_FETCH_reg(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \
|
||||
void *offset, void *dest) \
|
||||
{ \
|
||||
*(type *)dest = (type)regs_get_register(regs, \
|
||||
(unsigned int)((unsigned long)offset)); \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(reg)
|
||||
|
||||
static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs,
|
||||
void *dummy)
|
||||
{
|
||||
return regs_return_value(regs);
|
||||
#define DEFINE_FETCH_stack(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
|
||||
void *offset, void *dest) \
|
||||
{ \
|
||||
*(type *)dest = (type)regs_get_kernel_stack_nth(regs, \
|
||||
(unsigned int)((unsigned long)offset)); \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(stack)
|
||||
|
||||
static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs,
|
||||
void *dummy)
|
||||
{
|
||||
return kernel_stack_pointer(regs);
|
||||
#define DEFINE_FETCH_retval(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
|
||||
void *dummy, void *dest) \
|
||||
{ \
|
||||
*(type *)dest = (type)regs_return_value(regs); \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(retval)
|
||||
|
||||
#define DEFINE_FETCH_memory(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
|
||||
void *addr, void *dest) \
|
||||
{ \
|
||||
type retval; \
|
||||
if (probe_kernel_address(addr, retval)) \
|
||||
*(type *)dest = 0; \
|
||||
else \
|
||||
*(type *)dest = retval; \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(memory)
|
||||
|
||||
/* Memory fetching by symbol */
|
||||
struct symbol_cache {
|
||||
@ -145,51 +192,126 @@ static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
|
||||
return sc;
|
||||
}
|
||||
|
||||
static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data)
|
||||
{
|
||||
struct symbol_cache *sc = data;
|
||||
|
||||
if (sc->addr)
|
||||
return fetch_memory(regs, (void *)sc->addr);
|
||||
else
|
||||
return 0;
|
||||
#define DEFINE_FETCH_symbol(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
|
||||
void *data, void *dest) \
|
||||
{ \
|
||||
struct symbol_cache *sc = data; \
|
||||
if (sc->addr) \
|
||||
fetch_memory_##type(regs, (void *)sc->addr, dest); \
|
||||
else \
|
||||
*(type *)dest = 0; \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(symbol)
|
||||
|
||||
/* Special indirect memory access interface */
|
||||
struct indirect_fetch_data {
|
||||
struct fetch_func orig;
|
||||
/* Dereference memory access function */
|
||||
struct deref_fetch_param {
|
||||
struct fetch_param orig;
|
||||
long offset;
|
||||
};
|
||||
|
||||
static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data)
|
||||
{
|
||||
struct indirect_fetch_data *ind = data;
|
||||
unsigned long addr;
|
||||
|
||||
addr = call_fetch(&ind->orig, regs);
|
||||
if (addr) {
|
||||
addr += ind->offset;
|
||||
return fetch_memory(regs, (void *)addr);
|
||||
} else
|
||||
return 0;
|
||||
#define DEFINE_FETCH_deref(type) \
|
||||
static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
|
||||
void *data, void *dest) \
|
||||
{ \
|
||||
struct deref_fetch_param *dprm = data; \
|
||||
unsigned long addr; \
|
||||
call_fetch(&dprm->orig, regs, &addr); \
|
||||
if (addr) { \
|
||||
addr += dprm->offset; \
|
||||
fetch_memory_##type(regs, (void *)addr, dest); \
|
||||
} else \
|
||||
*(type *)dest = 0; \
|
||||
}
|
||||
DEFINE_BASIC_FETCH_FUNCS(deref)
|
||||
|
||||
static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data)
|
||||
static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
|
||||
{
|
||||
if (data->orig.func == fetch_indirect)
|
||||
free_indirect_fetch_data(data->orig.data);
|
||||
else if (data->orig.func == fetch_symbol)
|
||||
if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn))
|
||||
free_deref_fetch_param(data->orig.data);
|
||||
else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn))
|
||||
free_symbol_cache(data->orig.data);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
/* Default (unsigned long) fetch type */
|
||||
#define __DEFAULT_FETCH_TYPE(t) u##t
|
||||
#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
|
||||
#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
|
||||
#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
|
||||
|
||||
#define ASSIGN_FETCH_FUNC(kind, type) \
|
||||
.kind = FETCH_FUNC_NAME(kind, type)
|
||||
|
||||
#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
|
||||
{.name = #ptype, \
|
||||
.size = sizeof(ftype), \
|
||||
.is_signed = sign, \
|
||||
.print = PRINT_TYPE_FUNC_NAME(ptype), \
|
||||
.fmt = PRINT_TYPE_FMT_NAME(ptype), \
|
||||
ASSIGN_FETCH_FUNC(reg, ftype), \
|
||||
ASSIGN_FETCH_FUNC(stack, ftype), \
|
||||
ASSIGN_FETCH_FUNC(retval, ftype), \
|
||||
ASSIGN_FETCH_FUNC(memory, ftype), \
|
||||
ASSIGN_FETCH_FUNC(symbol, ftype), \
|
||||
ASSIGN_FETCH_FUNC(deref, ftype), \
|
||||
}
|
||||
|
||||
/* Fetch type information table */
|
||||
static const struct fetch_type {
|
||||
const char *name; /* Name of type */
|
||||
size_t size; /* Byte size of type */
|
||||
int is_signed; /* Signed flag */
|
||||
print_type_func_t print; /* Print functions */
|
||||
const char *fmt; /* Fromat string */
|
||||
/* Fetch functions */
|
||||
fetch_func_t reg;
|
||||
fetch_func_t stack;
|
||||
fetch_func_t retval;
|
||||
fetch_func_t memory;
|
||||
fetch_func_t symbol;
|
||||
fetch_func_t deref;
|
||||
} fetch_type_table[] = {
|
||||
ASSIGN_FETCH_TYPE(u8, u8, 0),
|
||||
ASSIGN_FETCH_TYPE(u16, u16, 0),
|
||||
ASSIGN_FETCH_TYPE(u32, u32, 0),
|
||||
ASSIGN_FETCH_TYPE(u64, u64, 0),
|
||||
ASSIGN_FETCH_TYPE(s8, u8, 1),
|
||||
ASSIGN_FETCH_TYPE(s16, u16, 1),
|
||||
ASSIGN_FETCH_TYPE(s32, u32, 1),
|
||||
ASSIGN_FETCH_TYPE(s64, u64, 1),
|
||||
};
|
||||
|
||||
static const struct fetch_type *find_fetch_type(const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!type)
|
||||
type = DEFAULT_FETCH_TYPE_STR;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
|
||||
if (strcmp(type, fetch_type_table[i].name) == 0)
|
||||
return &fetch_type_table[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Special function : only accept unsigned long */
|
||||
static __kprobes void fetch_stack_address(struct pt_regs *regs,
|
||||
void *dummy, void *dest)
|
||||
{
|
||||
*(unsigned long *)dest = kernel_stack_pointer(regs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kprobe event core functions
|
||||
*/
|
||||
|
||||
struct probe_arg {
|
||||
struct fetch_func fetch;
|
||||
const char *name;
|
||||
struct fetch_param fetch;
|
||||
unsigned int offset; /* Offset from argument entry */
|
||||
const char *name; /* Name of this argument */
|
||||
const char *comm; /* Command of this argument */
|
||||
const struct fetch_type *type; /* Type of this argument */
|
||||
};
|
||||
|
||||
/* Flags for trace_probe */
|
||||
@ -204,6 +326,7 @@ struct trace_probe {
|
||||
const char *symbol; /* symbol name */
|
||||
struct ftrace_event_call call;
|
||||
struct trace_event event;
|
||||
ssize_t size; /* trace entry size */
|
||||
unsigned int nr_args;
|
||||
struct probe_arg args[];
|
||||
};
|
||||
@ -212,6 +335,7 @@ struct trace_probe {
|
||||
(offsetof(struct trace_probe, args) + \
|
||||
(sizeof(struct probe_arg) * (n)))
|
||||
|
||||
|
||||
static __kprobes int probe_is_return(struct trace_probe *tp)
|
||||
{
|
||||
return tp->rp.handler != NULL;
|
||||
@ -222,49 +346,6 @@ static __kprobes const char *probe_symbol(struct trace_probe *tp)
|
||||
return tp->symbol ? tp->symbol : "unknown";
|
||||
}
|
||||
|
||||
static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (ff->func == fetch_register) {
|
||||
const char *name;
|
||||
name = regs_query_register_name((unsigned int)((long)ff->data));
|
||||
ret = snprintf(buf, n, "%%%s", name);
|
||||
} else if (ff->func == fetch_stack)
|
||||
ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data);
|
||||
else if (ff->func == fetch_memory)
|
||||
ret = snprintf(buf, n, "@0x%p", ff->data);
|
||||
else if (ff->func == fetch_symbol) {
|
||||
struct symbol_cache *sc = ff->data;
|
||||
if (sc->offset)
|
||||
ret = snprintf(buf, n, "@%s%+ld", sc->symbol,
|
||||
sc->offset);
|
||||
else
|
||||
ret = snprintf(buf, n, "@%s", sc->symbol);
|
||||
} else if (ff->func == fetch_retvalue)
|
||||
ret = snprintf(buf, n, "$retval");
|
||||
else if (ff->func == fetch_stack_address)
|
||||
ret = snprintf(buf, n, "$stack");
|
||||
else if (ff->func == fetch_indirect) {
|
||||
struct indirect_fetch_data *id = ff->data;
|
||||
size_t l = 0;
|
||||
ret = snprintf(buf, n, "%+ld(", id->offset);
|
||||
if (ret >= n)
|
||||
goto end;
|
||||
l += ret;
|
||||
ret = probe_arg_string(buf + l, n - l, &id->orig);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
l += ret;
|
||||
ret = snprintf(buf + l, n - l, ")");
|
||||
ret += l;
|
||||
}
|
||||
end:
|
||||
if (ret >= n)
|
||||
return -ENOSPC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int register_probe_event(struct trace_probe *tp);
|
||||
static void unregister_probe_event(struct trace_probe *tp);
|
||||
|
||||
@ -347,11 +428,12 @@ error:
|
||||
|
||||
static void free_probe_arg(struct probe_arg *arg)
|
||||
{
|
||||
if (arg->fetch.func == fetch_symbol)
|
||||
if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn))
|
||||
free_deref_fetch_param(arg->fetch.data);
|
||||
else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn))
|
||||
free_symbol_cache(arg->fetch.data);
|
||||
else if (arg->fetch.func == fetch_indirect)
|
||||
free_indirect_fetch_data(arg->fetch.data);
|
||||
kfree(arg->name);
|
||||
kfree(arg->comm);
|
||||
}
|
||||
|
||||
static void free_trace_probe(struct trace_probe *tp)
|
||||
@ -457,28 +539,30 @@ static int split_symbol_offset(char *symbol, unsigned long *offset)
|
||||
#define PARAM_MAX_ARGS 16
|
||||
#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
|
||||
|
||||
static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
|
||||
static int parse_probe_vars(char *arg, const struct fetch_type *t,
|
||||
struct fetch_param *f, int is_return)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long param;
|
||||
|
||||
if (strcmp(arg, "retval") == 0) {
|
||||
if (is_return) {
|
||||
ff->func = fetch_retvalue;
|
||||
ff->data = NULL;
|
||||
} else
|
||||
if (is_return)
|
||||
f->fn = t->retval;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else if (strncmp(arg, "stack", 5) == 0) {
|
||||
if (arg[5] == '\0') {
|
||||
ff->func = fetch_stack_address;
|
||||
ff->data = NULL;
|
||||
if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0)
|
||||
f->fn = fetch_stack_address;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
} else if (isdigit(arg[5])) {
|
||||
ret = strict_strtoul(arg + 5, 10, ¶m);
|
||||
if (ret || param > PARAM_MAX_STACK)
|
||||
ret = -EINVAL;
|
||||
else {
|
||||
ff->func = fetch_stack;
|
||||
ff->data = (void *)param;
|
||||
f->fn = t->stack;
|
||||
f->data = (void *)param;
|
||||
}
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
@ -488,7 +572,8 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
|
||||
}
|
||||
|
||||
/* Recursive argument parser */
|
||||
static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
|
||||
static int __parse_probe_arg(char *arg, const struct fetch_type *t,
|
||||
struct fetch_param *f, int is_return)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long param;
|
||||
@ -497,13 +582,13 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
|
||||
|
||||
switch (arg[0]) {
|
||||
case '$':
|
||||
ret = parse_probe_vars(arg + 1, ff, is_return);
|
||||
ret = parse_probe_vars(arg + 1, t, f, is_return);
|
||||
break;
|
||||
case '%': /* named register */
|
||||
ret = regs_query_register_offset(arg + 1);
|
||||
if (ret >= 0) {
|
||||
ff->func = fetch_register;
|
||||
ff->data = (void *)(unsigned long)ret;
|
||||
f->fn = t->reg;
|
||||
f->data = (void *)(unsigned long)ret;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
@ -512,26 +597,22 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
|
||||
ret = strict_strtoul(arg + 1, 0, ¶m);
|
||||
if (ret)
|
||||
break;
|
||||
ff->func = fetch_memory;
|
||||
ff->data = (void *)param;
|
||||
f->fn = t->memory;
|
||||
f->data = (void *)param;
|
||||
} else {
|
||||
ret = split_symbol_offset(arg + 1, &offset);
|
||||
if (ret)
|
||||
break;
|
||||
ff->data = alloc_symbol_cache(arg + 1, offset);
|
||||
if (ff->data)
|
||||
ff->func = fetch_symbol;
|
||||
else
|
||||
ret = -EINVAL;
|
||||
f->data = alloc_symbol_cache(arg + 1, offset);
|
||||
if (f->data)
|
||||
f->fn = t->symbol;
|
||||
}
|
||||
break;
|
||||
case '+': /* indirect memory */
|
||||
case '+': /* deref memory */
|
||||
case '-':
|
||||
tmp = strchr(arg, '(');
|
||||
if (!tmp) {
|
||||
ret = -EINVAL;
|
||||
if (!tmp)
|
||||
break;
|
||||
}
|
||||
*tmp = '\0';
|
||||
ret = strict_strtol(arg + 1, 0, &offset);
|
||||
if (ret)
|
||||
@ -541,38 +622,58 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
|
||||
arg = tmp + 1;
|
||||
tmp = strrchr(arg, ')');
|
||||
if (tmp) {
|
||||
struct indirect_fetch_data *id;
|
||||
struct deref_fetch_param *dprm;
|
||||
const struct fetch_type *t2 = find_fetch_type(NULL);
|
||||
*tmp = '\0';
|
||||
id = kzalloc(sizeof(struct indirect_fetch_data),
|
||||
GFP_KERNEL);
|
||||
if (!id)
|
||||
dprm = kzalloc(sizeof(struct deref_fetch_param),
|
||||
GFP_KERNEL);
|
||||
if (!dprm)
|
||||
return -ENOMEM;
|
||||
id->offset = offset;
|
||||
ret = __parse_probe_arg(arg, &id->orig, is_return);
|
||||
dprm->offset = offset;
|
||||
ret = __parse_probe_arg(arg, t2, &dprm->orig,
|
||||
is_return);
|
||||
if (ret)
|
||||
kfree(id);
|
||||
kfree(dprm);
|
||||
else {
|
||||
ff->func = fetch_indirect;
|
||||
ff->data = (void *)id;
|
||||
f->fn = t->deref;
|
||||
f->data = (void *)dprm;
|
||||
}
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* TODO: support custom handler */
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (!ret && !f->fn)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* String length checking wrapper */
|
||||
static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
|
||||
static int parse_probe_arg(char *arg, struct trace_probe *tp,
|
||||
struct probe_arg *parg, int is_return)
|
||||
{
|
||||
const char *t;
|
||||
|
||||
if (strlen(arg) > MAX_ARGSTR_LEN) {
|
||||
pr_info("Argument is too long.: %s\n", arg);
|
||||
return -ENOSPC;
|
||||
}
|
||||
return __parse_probe_arg(arg, ff, is_return);
|
||||
parg->comm = kstrdup(arg, GFP_KERNEL);
|
||||
if (!parg->comm) {
|
||||
pr_info("Failed to allocate memory for command '%s'.\n", arg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
t = strchr(parg->comm, ':');
|
||||
if (t) {
|
||||
arg[t - parg->comm] = '\0';
|
||||
t++;
|
||||
}
|
||||
parg->type = find_fetch_type(t);
|
||||
if (!parg->type) {
|
||||
pr_info("Unsupported type: %s\n", t);
|
||||
return -EINVAL;
|
||||
}
|
||||
parg->offset = tp->size;
|
||||
tp->size += parg->type->size;
|
||||
return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
|
||||
}
|
||||
|
||||
/* Return 1 if name is reserved or already used by another argument */
|
||||
@ -602,15 +703,18 @@ static int create_trace_probe(int argc, char **argv)
|
||||
* @ADDR : fetch memory at ADDR (ADDR should be in kernel)
|
||||
* @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol)
|
||||
* %REG : fetch register REG
|
||||
* Indirect memory fetch:
|
||||
* Dereferencing memory fetch:
|
||||
* +|-offs(ARG) : fetch memory at ARG +|- offs address.
|
||||
* Alias name of args:
|
||||
* NAME=FETCHARG : set NAME as alias of FETCHARG.
|
||||
* Type of args:
|
||||
* FETCHARG:TYPE : use TYPE instead of unsigned long.
|
||||
*/
|
||||
struct trace_probe *tp;
|
||||
int i, ret = 0;
|
||||
int is_return = 0, is_delete = 0;
|
||||
char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL;
|
||||
char *symbol = NULL, *event = NULL, *group = NULL;
|
||||
char *arg, *tmp;
|
||||
unsigned long offset = 0;
|
||||
void *addr = NULL;
|
||||
char buf[MAX_EVENT_NAME_LEN];
|
||||
@ -723,13 +827,6 @@ static int create_trace_probe(int argc, char **argv)
|
||||
else
|
||||
arg = argv[i];
|
||||
|
||||
if (conflict_field_name(argv[i], tp->args, i)) {
|
||||
pr_info("Argument%d name '%s' conflicts with "
|
||||
"another field.\n", i, argv[i]);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
tp->args[i].name = kstrdup(argv[i], GFP_KERNEL);
|
||||
if (!tp->args[i].name) {
|
||||
pr_info("Failed to allocate argument%d name '%s'.\n",
|
||||
@ -737,9 +834,19 @@ static int create_trace_probe(int argc, char **argv)
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
tmp = strchr(tp->args[i].name, ':');
|
||||
if (tmp)
|
||||
*tmp = '_'; /* convert : to _ */
|
||||
|
||||
if (conflict_field_name(tp->args[i].name, tp->args, i)) {
|
||||
pr_info("Argument%d name '%s' conflicts with "
|
||||
"another field.\n", i, argv[i]);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Parse fetch argument */
|
||||
ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return);
|
||||
ret = parse_probe_arg(arg, tp, &tp->args[i], is_return);
|
||||
if (ret) {
|
||||
pr_info("Parse error at argument%d. (%d)\n", i, ret);
|
||||
kfree(tp->args[i].name);
|
||||
@ -794,8 +901,7 @@ static void probes_seq_stop(struct seq_file *m, void *v)
|
||||
static int probes_seq_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct trace_probe *tp = v;
|
||||
int i, ret;
|
||||
char buf[MAX_ARGSTR_LEN + 1];
|
||||
int i;
|
||||
|
||||
seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p');
|
||||
seq_printf(m, ":%s/%s", tp->call.system, tp->call.name);
|
||||
@ -807,15 +913,10 @@ static int probes_seq_show(struct seq_file *m, void *v)
|
||||
else
|
||||
seq_printf(m, " %s", probe_symbol(tp));
|
||||
|
||||
for (i = 0; i < tp->nr_args; i++) {
|
||||
ret = probe_arg_string(buf, MAX_ARGSTR_LEN, &tp->args[i].fetch);
|
||||
if (ret < 0) {
|
||||
pr_warning("Argument%d decoding error(%d).\n", i, ret);
|
||||
return ret;
|
||||
}
|
||||
seq_printf(m, " %s=%s", tp->args[i].name, buf);
|
||||
}
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
|
||||
seq_printf(m, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -945,9 +1046,10 @@ static const struct file_operations kprobe_profile_ops = {
|
||||
static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
|
||||
{
|
||||
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
|
||||
struct kprobe_trace_entry *entry;
|
||||
struct kprobe_trace_entry_head *entry;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
u8 *data;
|
||||
int size, i, pc;
|
||||
unsigned long irq_flags;
|
||||
struct ftrace_event_call *call = &tp->call;
|
||||
@ -957,7 +1059,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
|
||||
local_save_flags(irq_flags);
|
||||
pc = preempt_count();
|
||||
|
||||
size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
|
||||
size = sizeof(*entry) + tp->size;
|
||||
|
||||
event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
|
||||
irq_flags, pc);
|
||||
@ -965,10 +1067,10 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
|
||||
return;
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->nargs = tp->nr_args;
|
||||
entry->ip = (unsigned long)kp->addr;
|
||||
data = (u8 *)&entry[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
|
||||
call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
|
||||
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
|
||||
@ -979,9 +1081,10 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
|
||||
struct kretprobe_trace_entry *entry;
|
||||
struct kretprobe_trace_entry_head *entry;
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
u8 *data;
|
||||
int size, i, pc;
|
||||
unsigned long irq_flags;
|
||||
struct ftrace_event_call *call = &tp->call;
|
||||
@ -989,7 +1092,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
|
||||
local_save_flags(irq_flags);
|
||||
pc = preempt_count();
|
||||
|
||||
size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
|
||||
size = sizeof(*entry) + tp->size;
|
||||
|
||||
event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
|
||||
irq_flags, pc);
|
||||
@ -997,11 +1100,11 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
|
||||
return;
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->nargs = tp->nr_args;
|
||||
entry->func = (unsigned long)tp->rp.kp.addr;
|
||||
entry->ret_ip = (unsigned long)ri->ret_addr;
|
||||
data = (u8 *)&entry[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
|
||||
call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
|
||||
|
||||
if (!filter_current_check_discard(buffer, call, entry, event))
|
||||
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
|
||||
@ -1011,13 +1114,14 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
|
||||
enum print_line_t
|
||||
print_kprobe_event(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct kprobe_trace_entry *field;
|
||||
struct kprobe_trace_entry_head *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_event *event;
|
||||
struct trace_probe *tp;
|
||||
u8 *data;
|
||||
int i;
|
||||
|
||||
field = (struct kprobe_trace_entry *)iter->ent;
|
||||
field = (struct kprobe_trace_entry_head *)iter->ent;
|
||||
event = ftrace_find_event(field->ent.type);
|
||||
tp = container_of(event, struct trace_probe, event);
|
||||
|
||||
@ -1030,9 +1134,10 @@ print_kprobe_event(struct trace_iterator *iter, int flags)
|
||||
if (!trace_seq_puts(s, ")"))
|
||||
goto partial;
|
||||
|
||||
for (i = 0; i < field->nargs; i++)
|
||||
if (!trace_seq_printf(s, " %s=%lx",
|
||||
tp->args[i].name, field->args[i]))
|
||||
data = (u8 *)&field[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
if (!tp->args[i].type->print(s, tp->args[i].name,
|
||||
data + tp->args[i].offset))
|
||||
goto partial;
|
||||
|
||||
if (!trace_seq_puts(s, "\n"))
|
||||
@ -1046,13 +1151,14 @@ partial:
|
||||
enum print_line_t
|
||||
print_kretprobe_event(struct trace_iterator *iter, int flags)
|
||||
{
|
||||
struct kretprobe_trace_entry *field;
|
||||
struct kretprobe_trace_entry_head *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_event *event;
|
||||
struct trace_probe *tp;
|
||||
u8 *data;
|
||||
int i;
|
||||
|
||||
field = (struct kretprobe_trace_entry *)iter->ent;
|
||||
field = (struct kretprobe_trace_entry_head *)iter->ent;
|
||||
event = ftrace_find_event(field->ent.type);
|
||||
tp = container_of(event, struct trace_probe, event);
|
||||
|
||||
@ -1071,9 +1177,10 @@ print_kretprobe_event(struct trace_iterator *iter, int flags)
|
||||
if (!trace_seq_puts(s, ")"))
|
||||
goto partial;
|
||||
|
||||
for (i = 0; i < field->nargs; i++)
|
||||
if (!trace_seq_printf(s, " %s=%lx",
|
||||
tp->args[i].name, field->args[i]))
|
||||
data = (u8 *)&field[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
if (!tp->args[i].type->print(s, tp->args[i].name,
|
||||
data + tp->args[i].offset))
|
||||
goto partial;
|
||||
|
||||
if (!trace_seq_puts(s, "\n"))
|
||||
@ -1129,29 +1236,43 @@ static int probe_event_raw_init(struct ftrace_event_call *event_call)
|
||||
static int kprobe_event_define_fields(struct ftrace_event_call *event_call)
|
||||
{
|
||||
int ret, i;
|
||||
struct kprobe_trace_entry field;
|
||||
struct kprobe_trace_entry_head field;
|
||||
struct trace_probe *tp = (struct trace_probe *)event_call->data;
|
||||
|
||||
DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
|
||||
DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
|
||||
/* Set argument names as fields */
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
|
||||
for (i = 0; i < tp->nr_args; i++) {
|
||||
ret = trace_define_field(event_call, tp->args[i].type->name,
|
||||
tp->args[i].name,
|
||||
sizeof(field) + tp->args[i].offset,
|
||||
tp->args[i].type->size,
|
||||
tp->args[i].type->is_signed,
|
||||
FILTER_OTHER);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
|
||||
{
|
||||
int ret, i;
|
||||
struct kretprobe_trace_entry field;
|
||||
struct kretprobe_trace_entry_head field;
|
||||
struct trace_probe *tp = (struct trace_probe *)event_call->data;
|
||||
|
||||
DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);
|
||||
DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0);
|
||||
DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
|
||||
/* Set argument names as fields */
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
|
||||
for (i = 0; i < tp->nr_args; i++) {
|
||||
ret = trace_define_field(event_call, tp->args[i].type->name,
|
||||
tp->args[i].name,
|
||||
sizeof(field) + tp->args[i].offset,
|
||||
tp->args[i].type->size,
|
||||
tp->args[i].type->is_signed,
|
||||
FILTER_OTHER);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1176,8 +1297,8 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
|
||||
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);
|
||||
|
||||
for (i = 0; i < tp->nr_args; i++) {
|
||||
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%%lx",
|
||||
tp->args[i].name);
|
||||
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
|
||||
tp->args[i].name, tp->args[i].type->fmt);
|
||||
}
|
||||
|
||||
pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
|
||||
@ -1219,12 +1340,13 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
|
||||
{
|
||||
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
|
||||
struct ftrace_event_call *call = &tp->call;
|
||||
struct kprobe_trace_entry *entry;
|
||||
struct kprobe_trace_entry_head *entry;
|
||||
u8 *data;
|
||||
int size, __size, i;
|
||||
unsigned long irq_flags;
|
||||
int rctx;
|
||||
|
||||
__size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
|
||||
__size = sizeof(*entry) + tp->size;
|
||||
size = ALIGN(__size + sizeof(u32), sizeof(u64));
|
||||
size -= sizeof(u32);
|
||||
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
|
||||
@ -1235,10 +1357,10 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->nargs = tp->nr_args;
|
||||
entry->ip = (unsigned long)kp->addr;
|
||||
data = (u8 *)&entry[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
|
||||
call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
|
||||
|
||||
perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs);
|
||||
}
|
||||
@ -1249,12 +1371,13 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
|
||||
{
|
||||
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
|
||||
struct ftrace_event_call *call = &tp->call;
|
||||
struct kretprobe_trace_entry *entry;
|
||||
struct kretprobe_trace_entry_head *entry;
|
||||
u8 *data;
|
||||
int size, __size, i;
|
||||
unsigned long irq_flags;
|
||||
int rctx;
|
||||
|
||||
__size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
|
||||
__size = sizeof(*entry) + tp->size;
|
||||
size = ALIGN(__size + sizeof(u32), sizeof(u64));
|
||||
size -= sizeof(u32);
|
||||
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
|
||||
@ -1265,11 +1388,11 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->nargs = tp->nr_args;
|
||||
entry->func = (unsigned long)tp->rp.kp.addr;
|
||||
entry->ret_ip = (unsigned long)ri->ret_addr;
|
||||
data = (u8 *)&entry[1];
|
||||
for (i = 0; i < tp->nr_args; i++)
|
||||
entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
|
||||
call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
|
||||
|
||||
perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1,
|
||||
irq_flags, regs);
|
||||
|
@ -34,12 +34,6 @@
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
||||
/*
|
||||
* For now, let us restrict the no. of symbols traced simultaneously to number
|
||||
* of available hardware breakpoint registers.
|
||||
*/
|
||||
#define KSYM_TRACER_MAX HBP_NUM
|
||||
|
||||
#define KSYM_TRACER_OP_LEN 3 /* rw- */
|
||||
|
||||
struct trace_ksym {
|
||||
@ -53,7 +47,6 @@ struct trace_ksym {
|
||||
|
||||
static struct trace_array *ksym_trace_array;
|
||||
|
||||
static unsigned int ksym_filter_entry_count;
|
||||
static unsigned int ksym_tracing_enabled;
|
||||
|
||||
static HLIST_HEAD(ksym_filter_head);
|
||||
@ -181,13 +174,6 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
|
||||
struct trace_ksym *entry;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (ksym_filter_entry_count >= KSYM_TRACER_MAX) {
|
||||
printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No"
|
||||
" new requests for tracing can be accepted now.\n",
|
||||
KSYM_TRACER_MAX);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
@ -203,13 +189,17 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
|
||||
|
||||
if (IS_ERR(entry->ksym_hbp)) {
|
||||
ret = PTR_ERR(entry->ksym_hbp);
|
||||
printk(KERN_INFO "ksym_tracer request failed. Try again"
|
||||
" later!!\n");
|
||||
if (ret == -ENOSPC) {
|
||||
printk(KERN_ERR "ksym_tracer: Maximum limit reached."
|
||||
" No new requests for tracing can be accepted now.\n");
|
||||
} else {
|
||||
printk(KERN_INFO "ksym_tracer request failed. Try again"
|
||||
" later!!\n");
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head);
|
||||
ksym_filter_entry_count++;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -265,7 +255,6 @@ static void __ksym_trace_reset(void)
|
||||
hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head,
|
||||
ksym_hlist) {
|
||||
unregister_wide_hw_breakpoint(entry->ksym_hbp);
|
||||
ksym_filter_entry_count--;
|
||||
hlist_del_rcu(&(entry->ksym_hlist));
|
||||
synchronize_rcu();
|
||||
kfree(entry);
|
||||
@ -338,7 +327,6 @@ static ssize_t ksym_trace_filter_write(struct file *file,
|
||||
goto out_unlock;
|
||||
}
|
||||
/* Error or "symbol:---" case: drop it */
|
||||
ksym_filter_entry_count--;
|
||||
hlist_del_rcu(&(entry->ksym_hlist));
|
||||
synchronize_rcu();
|
||||
kfree(entry);
|
||||
|
@ -17,7 +17,6 @@ static inline int trace_valid_entry(struct trace_entry *entry)
|
||||
case TRACE_BRANCH:
|
||||
case TRACE_GRAPH_ENT:
|
||||
case TRACE_GRAPH_RET:
|
||||
case TRACE_HW_BRANCHES:
|
||||
case TRACE_KSYM:
|
||||
return 1;
|
||||
}
|
||||
@ -755,62 +754,6 @@ trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr)
|
||||
}
|
||||
#endif /* CONFIG_BRANCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_HW_BRANCH_TRACER
|
||||
int
|
||||
trace_selftest_startup_hw_branches(struct tracer *trace,
|
||||
struct trace_array *tr)
|
||||
{
|
||||
struct trace_iterator *iter;
|
||||
struct tracer tracer;
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
if (!trace->open) {
|
||||
printk(KERN_CONT "missing open function...");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = tracer_init(trace, tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hw-branch tracer needs to collect the trace from the various
|
||||
* cpu trace buffers - before tracing is stopped.
|
||||
*/
|
||||
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (!iter)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&tracer, trace, sizeof(tracer));
|
||||
|
||||
iter->trace = &tracer;
|
||||
iter->tr = tr;
|
||||
iter->pos = -1;
|
||||
mutex_init(&iter->mutex);
|
||||
|
||||
trace->open(iter);
|
||||
|
||||
mutex_destroy(&iter->mutex);
|
||||
kfree(iter);
|
||||
|
||||
tracing_stop();
|
||||
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT "no entries found..");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_HW_BRANCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_KSYM_TRACER
|
||||
static int ksym_selftest_dummy;
|
||||
|
||||
|
41
mm/mlock.c
41
mm/mlock.c
@ -607,44 +607,3 @@ void user_shm_unlock(size_t size, struct user_struct *user)
|
||||
spin_unlock(&shmlock_user_lock);
|
||||
free_uid(user);
|
||||
}
|
||||
|
||||
int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim,
|
||||
size_t size)
|
||||
{
|
||||
unsigned long lim, vm, pgsz;
|
||||
int error = -ENOMEM;
|
||||
|
||||
pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
lim = ACCESS_ONCE(rlim[RLIMIT_AS].rlim_cur) >> PAGE_SHIFT;
|
||||
vm = mm->total_vm + pgsz;
|
||||
if (lim < vm)
|
||||
goto out;
|
||||
|
||||
lim = ACCESS_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur) >> PAGE_SHIFT;
|
||||
vm = mm->locked_vm + pgsz;
|
||||
if (lim < vm)
|
||||
goto out;
|
||||
|
||||
mm->total_vm += pgsz;
|
||||
mm->locked_vm += pgsz;
|
||||
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&mm->mmap_sem);
|
||||
return error;
|
||||
}
|
||||
|
||||
void refund_locked_memory(struct mm_struct *mm, size_t size)
|
||||
{
|
||||
unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
mm->total_vm -= pgsz;
|
||||
mm->locked_vm -= pgsz;
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
perf-annotate(1)
|
||||
==============
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
|
@ -1,5 +1,5 @@
|
||||
perf-bench(1)
|
||||
============
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
@ -19,12 +19,12 @@ COMMON OPTIONS
|
||||
-f::
|
||||
--format=::
|
||||
Specify format style.
|
||||
Current available format styles are,
|
||||
Current available format styles are:
|
||||
|
||||
'default'::
|
||||
Default style. This is mainly for human reading.
|
||||
---------------------
|
||||
% perf bench sched pipe # with no style specify
|
||||
% perf bench sched pipe # with no style specified
|
||||
(executing 1000000 pipe operations between two tasks)
|
||||
Total time:5.855 sec
|
||||
5.855061 usecs/op
|
||||
@ -79,7 +79,7 @@ options (20 sender and receiver processes per group)
|
||||
|
||||
Total time:0.308 sec
|
||||
|
||||
% perf bench sched messaging -t -g 20 # be multi-thread,with 20 groups
|
||||
% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups
|
||||
(20 sender and receiver threads per group)
|
||||
(20 groups == 800 threads run)
|
||||
|
||||
|
@ -8,7 +8,7 @@ perf-buildid-cache - Manage build-id cache.
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf buildid-list <options>'
|
||||
'perf buildid-cache <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -30,4 +30,4 @@ OPTIONS
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
|
||||
|
@ -1,5 +1,5 @@
|
||||
perf-diff(1)
|
||||
==============
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
|
35
tools/perf/Documentation/perf-inject.txt
Normal file
35
tools/perf/Documentation/perf-inject.txt
Normal file
@ -0,0 +1,35 @@
|
||||
perf-inject(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-inject - Filter to augment the events stream with additional information
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf inject <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
perf-inject reads a perf-record event stream and repipes it to stdout. At any
|
||||
point the processing code can inject other events into the event stream - in
|
||||
this case build-ids (-b option) are read and injected as needed into the event
|
||||
stream.
|
||||
|
||||
Build-ids are just the first user of perf-inject - potentially anything that
|
||||
needs userspace processing to augment the events stream with additional
|
||||
information could make use of this facility.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-b::
|
||||
--build-ids=::
|
||||
Inject build-ids into the output stream
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
|
@ -1,5 +1,5 @@
|
||||
perf-kmem(1)
|
||||
==============
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
|
68
tools/perf/Documentation/perf-kvm.txt
Normal file
68
tools/perf/Documentation/perf-kvm.txt
Normal file
@ -0,0 +1,68 @@
|
||||
perf-kvm(1)
|
||||
===========
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-kvm - Tool to trace/measure kvm guest os
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf kvm' [--host] [--guest] [--guestmount=<path>
|
||||
[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
|
||||
{top|record|report|diff|buildid-list}
|
||||
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
|
||||
| --guestvmlinux=<path>] {top|record|report|diff|buildid-list}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There are a couple of variants of perf kvm:
|
||||
|
||||
'perf kvm [options] top <command>' to generates and displays
|
||||
a performance counter profile of guest os in realtime
|
||||
of an arbitrary workload.
|
||||
|
||||
'perf kvm record <command>' to record the performance couinter profile
|
||||
of an arbitrary workload and save it into a perf data file. If both
|
||||
--host and --guest are input, the perf data file name is perf.data.kvm.
|
||||
If there is no --host but --guest, the file name is perf.data.guest.
|
||||
If there is no --guest but --host, the file name is perf.data.host.
|
||||
|
||||
'perf kvm report' to display the performance counter profile information
|
||||
recorded via perf kvm record.
|
||||
|
||||
'perf kvm diff' to displays the performance difference amongst two perf.data
|
||||
files captured via perf record.
|
||||
|
||||
'perf kvm buildid-list' to display the buildids found in a perf data file,
|
||||
so that other tools can be used to fetch packages with matching symbol tables
|
||||
for use by perf report.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--host=::
|
||||
Collect host side performance profile.
|
||||
--guest=::
|
||||
Collect guest side performance profile.
|
||||
--guestmount=<path>::
|
||||
Guest os root file system mount directory. Users mounts guest os
|
||||
root directories under <path> by a specific filesystem access method,
|
||||
typically, sshfs. For example, start 2 guest os. The one's pid is 8888
|
||||
and the other's is 9999.
|
||||
#mkdir ~/guestmount; cd ~/guestmount
|
||||
#sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/
|
||||
#sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/
|
||||
#perf kvm --host --guest --guestmount=~/guestmount top
|
||||
--guestkallsyms=<path>::
|
||||
Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest
|
||||
kernel symbols. Users copy it out from guest os.
|
||||
--guestmodules=<path>::
|
||||
Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest
|
||||
kernel module information. Users copy it out from guest os.
|
||||
--guestvmlinux=<path>::
|
||||
Guest os kernel vmlinux.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
|
||||
linkperf:perf-diff[1], linkperf:perf-buildid-list[1]
|
@ -15,6 +15,35 @@ DESCRIPTION
|
||||
This command displays the symbolic event types which can be selected in the
|
||||
various perf commands with the -e option.
|
||||
|
||||
RAW HARDWARE EVENT DESCRIPTOR
|
||||
-----------------------------
|
||||
Even when an event is not available in a symbolic form within perf right now,
|
||||
it can be encoded in a per processor specific way.
|
||||
|
||||
For instance For x86 CPUs NNN represents the raw register encoding with the
|
||||
layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide] Figure 30-1 Layout
|
||||
of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344,
|
||||
Figure 13-7 Performance Event-Select Register (PerfEvtSeln)).
|
||||
|
||||
Example:
|
||||
|
||||
If the Intel docs for a QM720 Core i7 describe an event as:
|
||||
|
||||
Event Umask Event Mask
|
||||
Num. Value Mnemonic Description Comment
|
||||
|
||||
A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and
|
||||
delivered by loop stream detector invert to count
|
||||
cycles
|
||||
|
||||
raw encoding of 0x1A8 can be used:
|
||||
|
||||
perf stat -e r1a8 -a sleep 1
|
||||
perf record -e r1a8 ...
|
||||
|
||||
You should refer to the processor specific documentation for getting these
|
||||
details. Some of them are referenced in the SEE ALSO section below.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
None
|
||||
@ -22,4 +51,6 @@ None
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-top[1],
|
||||
linkperf:perf-record[1]
|
||||
linkperf:perf-record[1],
|
||||
http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
|
||||
http://support.amd.com/us/Processor_TechDocs/24593.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
|
||||
|
@ -57,6 +57,14 @@ OPTIONS
|
||||
--force::
|
||||
Forcibly add events with existing name.
|
||||
|
||||
-n::
|
||||
--dry-run::
|
||||
Dry run. With this option, --add and --del doesn't execute actual
|
||||
adding and removal operations.
|
||||
|
||||
--max-probes::
|
||||
Set the maximum number of probe points for an event. Default is 128.
|
||||
|
||||
PROBE SYNTAX
|
||||
------------
|
||||
Probe points are defined by following syntax.
|
||||
@ -74,13 +82,22 @@ Probe points are defined by following syntax.
|
||||
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
|
||||
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
|
||||
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
|
||||
'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
|
||||
'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
|
||||
|
||||
PROBE ARGUMENT
|
||||
--------------
|
||||
Each probe argument follows below syntax.
|
||||
|
||||
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
|
||||
|
||||
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
|
||||
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo.
|
||||
|
||||
LINE SYNTAX
|
||||
-----------
|
||||
Line range is descripted by following syntax.
|
||||
|
||||
"FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]"
|
||||
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]"
|
||||
|
||||
FUNC specifies the function name of showing lines. 'RLN' is the start line
|
||||
number from function entry line, and 'RLN2' is the end line number. As same as
|
||||
|
@ -58,7 +58,7 @@ OPTIONS
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Overwrite existing data file.
|
||||
Overwrite existing data file. (deprecated)
|
||||
|
||||
-c::
|
||||
--count=::
|
||||
@ -69,8 +69,8 @@ OPTIONS
|
||||
Output file name.
|
||||
|
||||
-i::
|
||||
--inherit::
|
||||
Child tasks inherit counters.
|
||||
--no-inherit::
|
||||
Child tasks do not inherit counters.
|
||||
-F::
|
||||
--freq=::
|
||||
Profile at this frequency.
|
||||
@ -101,7 +101,7 @@ OPTIONS
|
||||
|
||||
-R::
|
||||
--raw-samples::
|
||||
Collect raw sample records from all opened counters (typically for tracepoint counters).
|
||||
Collect raw sample records from all opened counters (default for tracepoint counters).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
@ -12,7 +12,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There's four variants of perf sched:
|
||||
There are four variants of perf sched:
|
||||
|
||||
'perf sched record <command>' to record the scheduling events
|
||||
of an arbitrary workload.
|
||||
@ -27,7 +27,7 @@ There's four variants of perf sched:
|
||||
via perf sched record. (this is done by starting up mockup threads
|
||||
that mimic the workload based on the events in the trace. These
|
||||
threads can then replay the timings (CPU runtime and sleep patterns)
|
||||
of the workload as it occured when it was recorded - and can repeat
|
||||
of the workload as it occurred when it was recorded - and can repeat
|
||||
it a number of times, measuring its performance.)
|
||||
|
||||
OPTIONS
|
||||
|
@ -31,8 +31,8 @@ OPTIONS
|
||||
hexadecimal event descriptor.
|
||||
|
||||
-i::
|
||||
--inherit::
|
||||
child tasks inherit counters
|
||||
--no-inherit::
|
||||
child tasks do not inherit counters
|
||||
-p::
|
||||
--pid=<pid>::
|
||||
stat events on existing pid
|
||||
|
22
tools/perf/Documentation/perf-test.txt
Normal file
22
tools/perf/Documentation/perf-test.txt
Normal file
@ -0,0 +1,22 @@
|
||||
perf-test(1)
|
||||
============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-test - Runs sanity tests.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf test <options>'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command does assorted sanity tests, initially thru linked routines but
|
||||
also will look for a directory with more tests in the form of scripts.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
@ -49,12 +49,10 @@ available as calls back into the perf executable (see below).
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -c 1 -f -a -M -R -e sched:sched_wakeup
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above options: -c 1 says to sample every event, -a to enable
|
||||
system-wide collection, -M to multiplex the output, and -R to collect
|
||||
raw samples.
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
|
@ -1,5 +1,5 @@
|
||||
perf-trace-python(1)
|
||||
==================
|
||||
====================
|
||||
|
||||
NAME
|
||||
----
|
||||
@ -93,7 +93,7 @@ don't care how it exited, so we'll use 'perf record' to record only
|
||||
the sys_enter events:
|
||||
|
||||
----
|
||||
# perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter
|
||||
# perf record -a -e raw_syscalls:sys_enter
|
||||
|
||||
^C[ perf record: Woken up 1 times to write data ]
|
||||
[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
|
||||
@ -182,7 +182,7 @@ mean either that the record step recorded event types that it wasn't
|
||||
really interested in, or the script was run against a trace file that
|
||||
doesn't correspond to the script.
|
||||
|
||||
The script generated by -g option option simply prints a line for each
|
||||
The script generated by -g option simply prints a line for each
|
||||
event found in the trace stream i.e. it basically just dumps the event
|
||||
and its parameter values to stdout. The print_header() function is
|
||||
simply a utility function used for that purpose. Let's rename the
|
||||
@ -359,7 +359,7 @@ your script:
|
||||
# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
|
||||
|
||||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter
|
||||
perf record -a -e raw_syscalls:sys_enter
|
||||
----
|
||||
|
||||
The 'report' script is also a shell script with the same base name as
|
||||
@ -449,12 +449,10 @@ available as calls back into the perf executable (see below).
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -c 1 -f -a -M -R -e sched:sched_wakeup
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above options: -c 1 says to sample every event, -a to enable
|
||||
system-wide collection, -M to multiplex the output, and -R to collect
|
||||
raw samples.
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
@ -584,7 +582,7 @@ files:
|
||||
flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
|
||||
symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
|
||||
|
||||
The *autodict* function returns a special special kind of Python
|
||||
The *autodict* function returns a special kind of Python
|
||||
dictionary that implements Perl's 'autovivifying' hashes in Python
|
||||
i.e. with autovivifying hashes, you can assign nested hash values
|
||||
without having to go to the trouble of creating intermediate levels if
|
||||
|
@ -1,5 +1,5 @@
|
||||
perf-trace(1)
|
||||
==============
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
|
@ -1,3 +1,7 @@
|
||||
ifeq ("$(origin O)", "command line")
|
||||
OUTPUT := $(O)/
|
||||
endif
|
||||
|
||||
# The default target of this Makefile is...
|
||||
all::
|
||||
|
||||
@ -150,10 +154,17 @@ all::
|
||||
# Define LDFLAGS=-static to build a static binary.
|
||||
#
|
||||
# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
|
||||
#
|
||||
# Define NO_DWARF if you do not want debug-info analysis feature at all.
|
||||
|
||||
PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN
|
||||
-include PERF-VERSION-FILE
|
||||
$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null)
|
||||
$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null)
|
||||
$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null)
|
||||
$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
|
||||
|
||||
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
|
||||
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
|
||||
-include $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
@ -162,6 +173,22 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
|
||||
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
|
||||
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
|
||||
|
||||
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
|
||||
-e s/arm.*/arm/ -e s/sa110/arm/ \
|
||||
-e s/s390x/s390/ -e s/parisc64/parisc/ \
|
||||
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
|
||||
-e s/sh[234].*/sh/ )
|
||||
|
||||
# Additional ARCH settings for x86
|
||||
ifeq ($(ARCH),i386)
|
||||
ARCH := x86
|
||||
endif
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ARCH := x86
|
||||
endif
|
||||
|
||||
$(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null)
|
||||
|
||||
# CFLAGS and LDFLAGS are for the users to override from the command line.
|
||||
|
||||
#
|
||||
@ -274,7 +301,7 @@ endif
|
||||
# Those must not be GNU-specific; they are shared with perl/ which may
|
||||
# be built by a different compiler. (Note that this is an artifact now
|
||||
# but it still might be nice to keep that distinction.)
|
||||
BASIC_CFLAGS = -Iutil/include
|
||||
BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include
|
||||
BASIC_LDFLAGS =
|
||||
|
||||
# Guard against environment variables
|
||||
@ -308,7 +335,7 @@ PROGRAMS += $(EXTRA_PROGRAMS)
|
||||
#
|
||||
# Single 'perf' binary right now:
|
||||
#
|
||||
PROGRAMS += perf
|
||||
PROGRAMS += $(OUTPUT)perf
|
||||
|
||||
# List built-in command $C whose implementation cmd_$C() is not in
|
||||
# builtin-$C.o but is linked in as part of some other command.
|
||||
@ -318,7 +345,7 @@ PROGRAMS += perf
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
|
||||
|
||||
# what 'all' will build but not install in perfexecdir
|
||||
OTHER_PROGRAMS = perf$X
|
||||
OTHER_PROGRAMS = $(OUTPUT)perf$X
|
||||
|
||||
# Set paths to tools early so that they can be used for version tests.
|
||||
ifndef SHELL_PATH
|
||||
@ -330,7 +357,7 @@ endif
|
||||
|
||||
export PERL_PATH
|
||||
|
||||
LIB_FILE=libperf.a
|
||||
LIB_FILE=$(OUTPUT)libperf.a
|
||||
|
||||
LIB_H += ../../include/linux/perf_event.h
|
||||
LIB_H += ../../include/linux/rbtree.h
|
||||
@ -350,12 +377,13 @@ LIB_H += util/include/linux/rbtree.h
|
||||
LIB_H += util/include/linux/string.h
|
||||
LIB_H += util/include/linux/types.h
|
||||
LIB_H += util/include/asm/asm-offsets.h
|
||||
LIB_H += util/include/asm/bitops.h
|
||||
LIB_H += util/include/asm/bug.h
|
||||
LIB_H += util/include/asm/byteorder.h
|
||||
LIB_H += util/include/asm/hweight.h
|
||||
LIB_H += util/include/asm/swab.h
|
||||
LIB_H += util/include/asm/system.h
|
||||
LIB_H += util/include/asm/uaccess.h
|
||||
LIB_H += util/include/dwarf-regs.h
|
||||
LIB_H += perf.h
|
||||
LIB_H += util/cache.h
|
||||
LIB_H += util/callchain.h
|
||||
@ -375,7 +403,6 @@ LIB_H += util/header.h
|
||||
LIB_H += util/help.h
|
||||
LIB_H += util/session.h
|
||||
LIB_H += util/strbuf.h
|
||||
LIB_H += util/string.h
|
||||
LIB_H += util/strlist.h
|
||||
LIB_H += util/svghelper.h
|
||||
LIB_H += util/run-command.h
|
||||
@ -389,79 +416,83 @@ LIB_H += util/thread.h
|
||||
LIB_H += util/trace-event.h
|
||||
LIB_H += util/probe-finder.h
|
||||
LIB_H += util/probe-event.h
|
||||
LIB_H += util/pstack.h
|
||||
LIB_H += util/cpumap.h
|
||||
|
||||
LIB_OBJS += util/abspath.o
|
||||
LIB_OBJS += util/alias.o
|
||||
LIB_OBJS += util/build-id.o
|
||||
LIB_OBJS += util/config.o
|
||||
LIB_OBJS += util/ctype.o
|
||||
LIB_OBJS += util/debugfs.o
|
||||
LIB_OBJS += util/environment.o
|
||||
LIB_OBJS += util/event.o
|
||||
LIB_OBJS += util/exec_cmd.o
|
||||
LIB_OBJS += util/help.o
|
||||
LIB_OBJS += util/levenshtein.o
|
||||
LIB_OBJS += util/parse-options.o
|
||||
LIB_OBJS += util/parse-events.o
|
||||
LIB_OBJS += util/path.o
|
||||
LIB_OBJS += util/rbtree.o
|
||||
LIB_OBJS += util/bitmap.o
|
||||
LIB_OBJS += util/hweight.o
|
||||
LIB_OBJS += util/find_next_bit.o
|
||||
LIB_OBJS += util/run-command.o
|
||||
LIB_OBJS += util/quote.o
|
||||
LIB_OBJS += util/strbuf.o
|
||||
LIB_OBJS += util/string.o
|
||||
LIB_OBJS += util/strlist.o
|
||||
LIB_OBJS += util/usage.o
|
||||
LIB_OBJS += util/wrapper.o
|
||||
LIB_OBJS += util/sigchain.o
|
||||
LIB_OBJS += util/symbol.o
|
||||
LIB_OBJS += util/color.o
|
||||
LIB_OBJS += util/pager.o
|
||||
LIB_OBJS += util/header.o
|
||||
LIB_OBJS += util/callchain.o
|
||||
LIB_OBJS += util/values.o
|
||||
LIB_OBJS += util/debug.o
|
||||
LIB_OBJS += util/map.o
|
||||
LIB_OBJS += util/session.o
|
||||
LIB_OBJS += util/thread.o
|
||||
LIB_OBJS += util/trace-event-parse.o
|
||||
LIB_OBJS += util/trace-event-read.o
|
||||
LIB_OBJS += util/trace-event-info.o
|
||||
LIB_OBJS += util/trace-event-scripting.o
|
||||
LIB_OBJS += util/svghelper.o
|
||||
LIB_OBJS += util/sort.o
|
||||
LIB_OBJS += util/hist.o
|
||||
LIB_OBJS += util/probe-event.o
|
||||
LIB_OBJS += util/util.o
|
||||
LIB_OBJS += util/cpumap.o
|
||||
LIB_OBJS += $(OUTPUT)util/abspath.o
|
||||
LIB_OBJS += $(OUTPUT)util/alias.o
|
||||
LIB_OBJS += $(OUTPUT)util/build-id.o
|
||||
LIB_OBJS += $(OUTPUT)util/config.o
|
||||
LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
LIB_OBJS += $(OUTPUT)util/event.o
|
||||
LIB_OBJS += $(OUTPUT)util/exec_cmd.o
|
||||
LIB_OBJS += $(OUTPUT)util/help.o
|
||||
LIB_OBJS += $(OUTPUT)util/levenshtein.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-options.o
|
||||
LIB_OBJS += $(OUTPUT)util/parse-events.o
|
||||
LIB_OBJS += $(OUTPUT)util/path.o
|
||||
LIB_OBJS += $(OUTPUT)util/rbtree.o
|
||||
LIB_OBJS += $(OUTPUT)util/bitmap.o
|
||||
LIB_OBJS += $(OUTPUT)util/hweight.o
|
||||
LIB_OBJS += $(OUTPUT)util/run-command.o
|
||||
LIB_OBJS += $(OUTPUT)util/quote.o
|
||||
LIB_OBJS += $(OUTPUT)util/strbuf.o
|
||||
LIB_OBJS += $(OUTPUT)util/string.o
|
||||
LIB_OBJS += $(OUTPUT)util/strlist.o
|
||||
LIB_OBJS += $(OUTPUT)util/usage.o
|
||||
LIB_OBJS += $(OUTPUT)util/wrapper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sigchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/symbol.o
|
||||
LIB_OBJS += $(OUTPUT)util/color.o
|
||||
LIB_OBJS += $(OUTPUT)util/pager.o
|
||||
LIB_OBJS += $(OUTPUT)util/header.o
|
||||
LIB_OBJS += $(OUTPUT)util/callchain.o
|
||||
LIB_OBJS += $(OUTPUT)util/values.o
|
||||
LIB_OBJS += $(OUTPUT)util/debug.o
|
||||
LIB_OBJS += $(OUTPUT)util/map.o
|
||||
LIB_OBJS += $(OUTPUT)util/pstack.o
|
||||
LIB_OBJS += $(OUTPUT)util/session.o
|
||||
LIB_OBJS += $(OUTPUT)util/thread.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
|
||||
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
|
||||
LIB_OBJS += $(OUTPUT)util/svghelper.o
|
||||
LIB_OBJS += $(OUTPUT)util/sort.o
|
||||
LIB_OBJS += $(OUTPUT)util/hist.o
|
||||
LIB_OBJS += $(OUTPUT)util/probe-event.o
|
||||
LIB_OBJS += $(OUTPUT)util/util.o
|
||||
LIB_OBJS += $(OUTPUT)util/cpumap.o
|
||||
|
||||
BUILTIN_OBJS += builtin-annotate.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
|
||||
|
||||
BUILTIN_OBJS += builtin-bench.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
|
||||
|
||||
# Benchmark modules
|
||||
BUILTIN_OBJS += bench/sched-messaging.o
|
||||
BUILTIN_OBJS += bench/sched-pipe.o
|
||||
BUILTIN_OBJS += bench/mem-memcpy.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
|
||||
|
||||
BUILTIN_OBJS += builtin-diff.o
|
||||
BUILTIN_OBJS += builtin-help.o
|
||||
BUILTIN_OBJS += builtin-sched.o
|
||||
BUILTIN_OBJS += builtin-buildid-list.o
|
||||
BUILTIN_OBJS += builtin-buildid-cache.o
|
||||
BUILTIN_OBJS += builtin-list.o
|
||||
BUILTIN_OBJS += builtin-record.o
|
||||
BUILTIN_OBJS += builtin-report.o
|
||||
BUILTIN_OBJS += builtin-stat.o
|
||||
BUILTIN_OBJS += builtin-timechart.o
|
||||
BUILTIN_OBJS += builtin-top.o
|
||||
BUILTIN_OBJS += builtin-trace.o
|
||||
BUILTIN_OBJS += builtin-probe.o
|
||||
BUILTIN_OBJS += builtin-kmem.o
|
||||
BUILTIN_OBJS += builtin-lock.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-help.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-sched.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-list.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-record.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-report.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-stat.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-top.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-test.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
|
||||
|
||||
PERFLIBS = $(LIB_FILE)
|
||||
|
||||
@ -476,6 +507,15 @@ PERFLIBS = $(LIB_FILE)
|
||||
-include config.mak.autogen
|
||||
-include config.mak
|
||||
|
||||
ifndef NO_DWARF
|
||||
ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
|
||||
msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
|
||||
NO_DWARF := 1
|
||||
endif # Dwarf support
|
||||
endif # NO_DWARF
|
||||
|
||||
-include arch/$(ARCH)/Makefile
|
||||
|
||||
ifeq ($(uname_S),Darwin)
|
||||
ifndef NO_FINK
|
||||
ifeq ($(shell test -d /sw/lib && echo y),y)
|
||||
@ -492,6 +532,10 @@ ifeq ($(uname_S),Darwin)
|
||||
PTHREAD_LIBS =
|
||||
endif
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
BASIC_CFLAGS += -I$(OUTPUT)
|
||||
endif
|
||||
|
||||
ifeq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
|
||||
ifneq ($(shell sh -c "(echo '\#include <gnu/libc-version.h>'; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
|
||||
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
|
||||
@ -504,14 +548,29 @@ else
|
||||
msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]);
|
||||
endif
|
||||
|
||||
ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
|
||||
msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev);
|
||||
BASIC_CFLAGS += -DNO_DWARF_SUPPORT
|
||||
ifndef NO_DWARF
|
||||
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
|
||||
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
|
||||
else
|
||||
BASIC_CFLAGS += -I/usr/include/elfutils
|
||||
BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT
|
||||
EXTLIBS += -lelf -ldw
|
||||
LIB_OBJS += util/probe-finder.o
|
||||
LIB_OBJS += $(OUTPUT)util/probe-finder.o
|
||||
endif # PERF_HAVE_DWARF_REGS
|
||||
endif # NO_DWARF
|
||||
|
||||
ifdef NO_NEWT
|
||||
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
||||
else
|
||||
ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
|
||||
msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
|
||||
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
||||
else
|
||||
# Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
|
||||
BASIC_CFLAGS += -I/usr/include/slang
|
||||
EXTLIBS += -lnewt -lslang
|
||||
LIB_OBJS += $(OUTPUT)util/newt.o
|
||||
endif
|
||||
endif # NO_NEWT
|
||||
|
||||
ifndef NO_LIBPERL
|
||||
PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null`
|
||||
@ -522,8 +581,8 @@ ifneq ($(shell sh -c "(echo '\#include <EXTERN.h>'; echo '\#include <perl.h>'; e
|
||||
BASIC_CFLAGS += -DNO_LIBPERL
|
||||
else
|
||||
ALL_LDFLAGS += $(PERL_EMBED_LDOPTS)
|
||||
LIB_OBJS += util/scripting-engines/trace-event-perl.o
|
||||
LIB_OBJS += scripts/perl/Perf-Trace-Util/Context.o
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
|
||||
ifndef NO_LIBPYTHON
|
||||
@ -531,16 +590,19 @@ PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null`
|
||||
PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null`
|
||||
endif
|
||||
|
||||
ifneq ($(shell sh -c "(echo '\#include <Python.h>'; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o /dev/null $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y)
|
||||
ifneq ($(shell sh -c "(echo '\#include <Python.h>'; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o $(BITBUCKET) $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y)
|
||||
BASIC_CFLAGS += -DNO_LIBPYTHON
|
||||
else
|
||||
ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS)
|
||||
LIB_OBJS += util/scripting-engines/trace-event-python.o
|
||||
LIB_OBJS += scripts/python/Perf-Trace-Util/Context.o
|
||||
LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o
|
||||
LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o
|
||||
endif
|
||||
|
||||
ifdef NO_DEMANGLE
|
||||
BASIC_CFLAGS += -DNO_DEMANGLE
|
||||
else ifdef HAVE_CPLUS_DEMANGLE
|
||||
EXTLIBS += -liberty
|
||||
BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
|
||||
else
|
||||
has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y")
|
||||
|
||||
@ -607,53 +669,53 @@ ifdef NO_C99_FORMAT
|
||||
endif
|
||||
ifdef SNPRINTF_RETURNS_BOGUS
|
||||
COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS
|
||||
COMPAT_OBJS += compat/snprintf.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/snprintf.o
|
||||
endif
|
||||
ifdef FREAD_READS_DIRECTORIES
|
||||
COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
|
||||
COMPAT_OBJS += compat/fopen.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/fopen.o
|
||||
endif
|
||||
ifdef NO_SYMLINK_HEAD
|
||||
BASIC_CFLAGS += -DNO_SYMLINK_HEAD
|
||||
endif
|
||||
ifdef NO_STRCASESTR
|
||||
COMPAT_CFLAGS += -DNO_STRCASESTR
|
||||
COMPAT_OBJS += compat/strcasestr.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o
|
||||
endif
|
||||
ifdef NO_STRTOUMAX
|
||||
COMPAT_CFLAGS += -DNO_STRTOUMAX
|
||||
COMPAT_OBJS += compat/strtoumax.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o
|
||||
endif
|
||||
ifdef NO_STRTOULL
|
||||
COMPAT_CFLAGS += -DNO_STRTOULL
|
||||
endif
|
||||
ifdef NO_SETENV
|
||||
COMPAT_CFLAGS += -DNO_SETENV
|
||||
COMPAT_OBJS += compat/setenv.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/setenv.o
|
||||
endif
|
||||
ifdef NO_MKDTEMP
|
||||
COMPAT_CFLAGS += -DNO_MKDTEMP
|
||||
COMPAT_OBJS += compat/mkdtemp.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o
|
||||
endif
|
||||
ifdef NO_UNSETENV
|
||||
COMPAT_CFLAGS += -DNO_UNSETENV
|
||||
COMPAT_OBJS += compat/unsetenv.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o
|
||||
endif
|
||||
ifdef NO_SYS_SELECT_H
|
||||
BASIC_CFLAGS += -DNO_SYS_SELECT_H
|
||||
endif
|
||||
ifdef NO_MMAP
|
||||
COMPAT_CFLAGS += -DNO_MMAP
|
||||
COMPAT_OBJS += compat/mmap.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/mmap.o
|
||||
else
|
||||
ifdef USE_WIN32_MMAP
|
||||
COMPAT_CFLAGS += -DUSE_WIN32_MMAP
|
||||
COMPAT_OBJS += compat/win32mmap.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o
|
||||
endif
|
||||
endif
|
||||
ifdef NO_PREAD
|
||||
COMPAT_CFLAGS += -DNO_PREAD
|
||||
COMPAT_OBJS += compat/pread.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/pread.o
|
||||
endif
|
||||
ifdef NO_FAST_WORKING_DIRECTORY
|
||||
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
|
||||
@ -675,10 +737,10 @@ else
|
||||
endif
|
||||
endif
|
||||
ifdef NO_INET_NTOP
|
||||
LIB_OBJS += compat/inet_ntop.o
|
||||
LIB_OBJS += $(OUTPUT)compat/inet_ntop.o
|
||||
endif
|
||||
ifdef NO_INET_PTON
|
||||
LIB_OBJS += compat/inet_pton.o
|
||||
LIB_OBJS += $(OUTPUT)compat/inet_pton.o
|
||||
endif
|
||||
|
||||
ifdef NO_ICONV
|
||||
@ -695,15 +757,15 @@ endif
|
||||
|
||||
ifdef PPC_SHA1
|
||||
SHA1_HEADER = "ppc/sha1.h"
|
||||
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
|
||||
LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o
|
||||
else
|
||||
ifdef ARM_SHA1
|
||||
SHA1_HEADER = "arm/sha1.h"
|
||||
LIB_OBJS += arm/sha1.o arm/sha1_arm.o
|
||||
LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o
|
||||
else
|
||||
ifdef MOZILLA_SHA1
|
||||
SHA1_HEADER = "mozilla-sha1/sha1.h"
|
||||
LIB_OBJS += mozilla-sha1/sha1.o
|
||||
LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o
|
||||
else
|
||||
SHA1_HEADER = <openssl/sha.h>
|
||||
EXTLIBS += $(LIB_4_CRYPTO)
|
||||
@ -715,15 +777,15 @@ ifdef NO_PERL_MAKEMAKER
|
||||
endif
|
||||
ifdef NO_HSTRERROR
|
||||
COMPAT_CFLAGS += -DNO_HSTRERROR
|
||||
COMPAT_OBJS += compat/hstrerror.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o
|
||||
endif
|
||||
ifdef NO_MEMMEM
|
||||
COMPAT_CFLAGS += -DNO_MEMMEM
|
||||
COMPAT_OBJS += compat/memmem.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/memmem.o
|
||||
endif
|
||||
ifdef INTERNAL_QSORT
|
||||
COMPAT_CFLAGS += -DINTERNAL_QSORT
|
||||
COMPAT_OBJS += compat/qsort.o
|
||||
COMPAT_OBJS += $(OUTPUT)compat/qsort.o
|
||||
endif
|
||||
ifdef RUNTIME_PREFIX
|
||||
COMPAT_CFLAGS += -DRUNTIME_PREFIX
|
||||
@ -803,7 +865,7 @@ export TAR INSTALL DESTDIR SHELL_PATH
|
||||
|
||||
SHELL = $(SHELL_PATH)
|
||||
|
||||
all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS
|
||||
all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS
|
||||
ifneq (,$X)
|
||||
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
|
||||
endif
|
||||
@ -815,39 +877,39 @@ please_set_SHELL_PATH_to_a_more_modern_shell:
|
||||
|
||||
shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
|
||||
strip: $(PROGRAMS) perf$X
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X
|
||||
strip: $(PROGRAMS) $(OUTPUT)perf$X
|
||||
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X
|
||||
|
||||
perf.o: perf.c common-cmds.h PERF-CFLAGS
|
||||
$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^)
|
||||
$(ALL_CFLAGS) -c $(filter %.c,$^) -o $@
|
||||
|
||||
perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \
|
||||
$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(OUTPUT)perf.o \
|
||||
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
||||
builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
|
||||
$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
'-DPERF_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DPERF_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
builtin-timechart.o: builtin-timechart.c common-cmds.h PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
|
||||
$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
|
||||
'-DPERF_MAN_PATH="$(mandir_SQ)"' \
|
||||
'-DPERF_INFO_PATH="$(infodir_SQ)"' $<
|
||||
|
||||
$(BUILT_INS): perf$X
|
||||
$(BUILT_INS): $(OUTPUT)perf$X
|
||||
$(QUIET_BUILT_IN)$(RM) $@ && \
|
||||
ln perf$X $@ 2>/dev/null || \
|
||||
ln -s perf$X $@ 2>/dev/null || \
|
||||
cp perf$X $@
|
||||
|
||||
common-cmds.h: util/generate-cmdlist.sh command-list.txt
|
||||
$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt
|
||||
|
||||
common-cmds.h: $(wildcard Documentation/perf-*.txt)
|
||||
$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt)
|
||||
$(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@
|
||||
|
||||
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
@ -859,7 +921,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
|
||||
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
|
||||
$@.sh >$@+ && \
|
||||
chmod +x $@+ && \
|
||||
mv $@+ $@
|
||||
mv $@+ $(OUTPUT)$@
|
||||
|
||||
configure: configure.ac
|
||||
$(QUIET_GEN)$(RM) $@ $<+ && \
|
||||
@ -869,60 +931,50 @@ configure: configure.ac
|
||||
$(RM) $<+
|
||||
|
||||
# These can record PERF_VERSION
|
||||
perf.o perf.spec \
|
||||
$(OUTPUT)perf.o perf.spec \
|
||||
$(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
|
||||
: PERF-VERSION-FILE
|
||||
: $(OUTPUT)PERF-VERSION-FILE
|
||||
|
||||
%.o: %.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
|
||||
%.s: %.c PERF-CFLAGS
|
||||
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
|
||||
%.o: %.S
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
|
||||
$(OUTPUT)%.o: %.S
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
|
||||
|
||||
util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
|
||||
$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
|
||||
'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \
|
||||
'-DBINDIR="$(bindir_relative_SQ)"' \
|
||||
'-DPREFIX="$(prefix_SQ)"' \
|
||||
$<
|
||||
|
||||
builtin-init-db.o: builtin-init-db.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
|
||||
$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
|
||||
|
||||
util/config.o: util/config.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
|
||||
|
||||
# some perf warning policies can't fit to lib/bitmap.c, eg: it warns about variable shadowing
|
||||
# from <string.h> that comes from kernel headers wrapping.
|
||||
KBITMAP_FLAGS=`echo $(ALL_CFLAGS) | sed s/-Wshadow// | sed s/-Wswitch-default// | sed s/-Wextra//`
|
||||
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
|
||||
util/bitmap.o: ../../lib/bitmap.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/bitmap.o -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
|
||||
util/hweight.o: ../../lib/hweight.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/hweight.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
|
||||
|
||||
util/find_next_bit.o: ../../lib/find_next_bit.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/find_next_bit.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
|
||||
$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
|
||||
util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-perl.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
|
||||
|
||||
scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o scripts/perl/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
|
||||
|
||||
util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-python.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
|
||||
|
||||
scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c PERF-CFLAGS
|
||||
$(QUIET_CC)$(CC) -o scripts/python/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
|
||||
|
||||
perf-%$X: %.o $(PERFLIBS)
|
||||
$(OUTPUT)perf-%$X: %.o $(PERFLIBS)
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
|
||||
@ -963,17 +1015,17 @@ cscope:
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
$(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
|
||||
|
||||
PERF-CFLAGS: .FORCE-PERF-CFLAGS
|
||||
$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS
|
||||
@FLAGS='$(TRACK_CFLAGS)'; \
|
||||
if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`" ; then \
|
||||
if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \
|
||||
echo 1>&2 " * new build flags or prefix"; \
|
||||
echo "$$FLAGS" >PERF-CFLAGS; \
|
||||
echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \
|
||||
fi
|
||||
|
||||
# We need to apply sq twice, once to protect from the shell
|
||||
# that runs PERF-BUILD-OPTIONS, and then again to protect it
|
||||
# that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it
|
||||
# and the first level quoting from the shell that runs "echo".
|
||||
PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS
|
||||
$(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS
|
||||
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
|
||||
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
|
||||
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
|
||||
@ -994,7 +1046,7 @@ all:: $(TEST_PROGRAMS)
|
||||
|
||||
export NO_SVN_TESTS
|
||||
|
||||
check: common-cmds.h
|
||||
check: $(OUTPUT)common-cmds.h
|
||||
if sparse; \
|
||||
then \
|
||||
for i in *.c */*.c; \
|
||||
@ -1028,10 +1080,10 @@ export perfexec_instdir
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
|
||||
$(INSTALL) perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
|
||||
$(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
|
||||
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
|
||||
@ -1045,7 +1097,7 @@ ifdef BUILT_INS
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
ifneq (,$X)
|
||||
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
|
||||
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';)
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -1129,14 +1181,14 @@ clean:
|
||||
$(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE)
|
||||
$(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X
|
||||
$(RM) $(TEST_PROGRAMS)
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
|
||||
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
|
||||
$(RM) -r autom4te.cache
|
||||
$(RM) config.log config.mak.autogen config.mak.append config.status config.cache
|
||||
$(RM) -r $(PERF_TARNAME) .doc-tmp-dir
|
||||
$(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz
|
||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||
$(MAKE) -C Documentation/ clean
|
||||
$(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS
|
||||
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS
|
||||
|
||||
.PHONY: all install clean strip
|
||||
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
|
4
tools/perf/arch/powerpc/Makefile
Normal file
4
tools/perf/arch/powerpc/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
88
tools/perf/arch/powerpc/util/dwarf-regs.c
Normal file
88
tools/perf/arch/powerpc/util/dwarf-regs.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Mapping of DWARF debug register numbers into register names.
|
||||
*
|
||||
* Copyright (C) 2010 Ian Munsie, IBM Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
};
|
||||
|
||||
#define STR(s) #s
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%gpr##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
|
||||
*/
|
||||
static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(0),
|
||||
GPR_DWARFNUM_NAME(1),
|
||||
GPR_DWARFNUM_NAME(2),
|
||||
GPR_DWARFNUM_NAME(3),
|
||||
GPR_DWARFNUM_NAME(4),
|
||||
GPR_DWARFNUM_NAME(5),
|
||||
GPR_DWARFNUM_NAME(6),
|
||||
GPR_DWARFNUM_NAME(7),
|
||||
GPR_DWARFNUM_NAME(8),
|
||||
GPR_DWARFNUM_NAME(9),
|
||||
GPR_DWARFNUM_NAME(10),
|
||||
GPR_DWARFNUM_NAME(11),
|
||||
GPR_DWARFNUM_NAME(12),
|
||||
GPR_DWARFNUM_NAME(13),
|
||||
GPR_DWARFNUM_NAME(14),
|
||||
GPR_DWARFNUM_NAME(15),
|
||||
GPR_DWARFNUM_NAME(16),
|
||||
GPR_DWARFNUM_NAME(17),
|
||||
GPR_DWARFNUM_NAME(18),
|
||||
GPR_DWARFNUM_NAME(19),
|
||||
GPR_DWARFNUM_NAME(20),
|
||||
GPR_DWARFNUM_NAME(21),
|
||||
GPR_DWARFNUM_NAME(22),
|
||||
GPR_DWARFNUM_NAME(23),
|
||||
GPR_DWARFNUM_NAME(24),
|
||||
GPR_DWARFNUM_NAME(25),
|
||||
GPR_DWARFNUM_NAME(26),
|
||||
GPR_DWARFNUM_NAME(27),
|
||||
GPR_DWARFNUM_NAME(28),
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
GPR_DWARFNUM_NAME(30),
|
||||
GPR_DWARFNUM_NAME(31),
|
||||
REG_DWARFNUM_NAME("%msr", 66),
|
||||
REG_DWARFNUM_NAME("%ctr", 109),
|
||||
REG_DWARFNUM_NAME("%link", 108),
|
||||
REG_DWARFNUM_NAME("%xer", 101),
|
||||
REG_DWARFNUM_NAME("%dar", 119),
|
||||
REG_DWARFNUM_NAME("%dsisr", 118),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* get_arch_regstr() - lookup register name from it's DWARF register number
|
||||
* @n: the DWARF register number
|
||||
*
|
||||
* get_arch_regstr() returns the name of the register in struct
|
||||
* regdwarfnum_table from it's DWARF register number. If the register is not
|
||||
* found in the table, this returns NULL;
|
||||
*/
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (roff->dwarfnum == n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
4
tools/perf/arch/x86/Makefile
Normal file
4
tools/perf/arch/x86/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
|
||||
endif
|
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
75
tools/perf/arch/x86/util/dwarf-regs.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
|
||||
* Extracted from probe-finder.c
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <libio.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
/*
|
||||
* Generic dwarf analysis helpers
|
||||
*/
|
||||
|
||||
#define X86_32_MAX_REGS 8
|
||||
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%cx",
|
||||
"%dx",
|
||||
"%bx",
|
||||
"$stack", /* Stack address instead of %sp */
|
||||
"%bp",
|
||||
"%si",
|
||||
"%di",
|
||||
};
|
||||
|
||||
#define X86_64_MAX_REGS 16
|
||||
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
|
||||
"%ax",
|
||||
"%dx",
|
||||
"%cx",
|
||||
"%bx",
|
||||
"%si",
|
||||
"%di",
|
||||
"%bp",
|
||||
"%sp",
|
||||
"%r8",
|
||||
"%r9",
|
||||
"%r10",
|
||||
"%r11",
|
||||
"%r12",
|
||||
"%r13",
|
||||
"%r14",
|
||||
"%r15",
|
||||
};
|
||||
|
||||
/* TODO: switching by dwarf address size */
|
||||
#ifdef __x86_64__
|
||||
#define ARCH_MAX_REGS X86_64_MAX_REGS
|
||||
#define arch_regs_table x86_64_regs_table
|
||||
#else
|
||||
#define ARCH_MAX_REGS X86_32_MAX_REGS
|
||||
#define arch_regs_table x86_32_regs_table
|
||||
#endif
|
||||
|
||||
/* Return architecture dependent register string (for kprobe-tracer) */
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/string.h"
|
||||
#include "../util/header.h"
|
||||
#include "bench.h"
|
||||
|
||||
@ -24,7 +23,7 @@
|
||||
|
||||
static const char *length_str = "1MB";
|
||||
static const char *routine = "default";
|
||||
static int use_clock = 0;
|
||||
static bool use_clock = false;
|
||||
static int clock_fd;
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -31,9 +31,9 @@
|
||||
|
||||
#define DATASIZE 100
|
||||
|
||||
static int use_pipes = 0;
|
||||
static bool use_pipes = false;
|
||||
static unsigned int loops = 100;
|
||||
static unsigned int thread_mode = 0;
|
||||
static bool thread_mode = false;
|
||||
static unsigned int num_groups = 10;
|
||||
|
||||
struct sender_context {
|
||||
@ -256,10 +256,8 @@ static const struct option options[] = {
|
||||
"Use pipe() instead of socketpair()"),
|
||||
OPT_BOOLEAN('t', "thread", &thread_mode,
|
||||
"Be multi thread instead of multi process"),
|
||||
OPT_INTEGER('g', "group", &num_groups,
|
||||
"Specify number of groups"),
|
||||
OPT_INTEGER('l', "loop", &loops,
|
||||
"Specify number of loops"),
|
||||
OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
|
||||
OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -93,7 +93,7 @@ int bench_sched_pipe(int argc, const char **argv,
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
printf("# Extecuted %d pipe operations between two tasks\n\n",
|
||||
printf("# Executed %d pipe operations between two tasks\n\n",
|
||||
loops);
|
||||
|
||||
result_usec = diff.tv_sec * 1000000;
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "util/cache.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/symbol.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/debug.h"
|
||||
@ -29,80 +28,16 @@
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
|
||||
static int force;
|
||||
static bool force;
|
||||
|
||||
static int full_paths;
|
||||
static bool full_paths;
|
||||
|
||||
static int print_line;
|
||||
|
||||
struct sym_hist {
|
||||
u64 sum;
|
||||
u64 ip[0];
|
||||
};
|
||||
|
||||
struct sym_ext {
|
||||
struct rb_node node;
|
||||
double percent;
|
||||
char *path;
|
||||
};
|
||||
|
||||
struct sym_priv {
|
||||
struct sym_hist *hist;
|
||||
struct sym_ext *ext;
|
||||
};
|
||||
static bool print_line;
|
||||
|
||||
static const char *sym_hist_filter;
|
||||
|
||||
static int sym__alloc_hist(struct symbol *self)
|
||||
static int hists__add_entry(struct hists *self, struct addr_location *al)
|
||||
{
|
||||
struct sym_priv *priv = symbol__priv(self);
|
||||
const int size = (sizeof(*priv->hist) +
|
||||
(self->end - self->start) * sizeof(u64));
|
||||
|
||||
priv->hist = zalloc(size);
|
||||
return priv->hist == NULL ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* collect histogram counts
|
||||
*/
|
||||
static int annotate__hist_hit(struct hist_entry *he, u64 ip)
|
||||
{
|
||||
unsigned int sym_size, offset;
|
||||
struct symbol *sym = he->sym;
|
||||
struct sym_priv *priv;
|
||||
struct sym_hist *h;
|
||||
|
||||
he->count++;
|
||||
|
||||
if (!sym || !he->map)
|
||||
return 0;
|
||||
|
||||
priv = symbol__priv(sym);
|
||||
if (priv->hist == NULL && sym__alloc_hist(sym) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
sym_size = sym->end - sym->start;
|
||||
offset = ip - sym->start;
|
||||
|
||||
pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip));
|
||||
|
||||
if (offset >= sym_size)
|
||||
return 0;
|
||||
|
||||
h = priv->hist;
|
||||
h->sum++;
|
||||
h->ip[offset]++;
|
||||
|
||||
pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start,
|
||||
he->sym->name, ip, ip - he->sym->start, h->ip[offset]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_session__add_hist_entry(struct perf_session *self,
|
||||
struct addr_location *al, u64 count)
|
||||
{
|
||||
bool hit;
|
||||
struct hist_entry *he;
|
||||
|
||||
if (sym_hist_filter != NULL &&
|
||||
@ -116,11 +51,11 @@ static int perf_session__add_hist_entry(struct perf_session *self,
|
||||
return 0;
|
||||
}
|
||||
|
||||
he = __perf_session__add_hist_entry(&self->hists, al, NULL, count, &hit);
|
||||
he = __hists__add_entry(self, al, NULL, 1);
|
||||
if (he == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return annotate__hist_hit(he, al->addr);
|
||||
return hist_entry__inc_addr_samples(he, al->addr);
|
||||
}
|
||||
|
||||
static int process_sample_event(event_t *event, struct perf_session *session)
|
||||
@ -136,7 +71,7 @@ static int process_sample_event(event_t *event, struct perf_session *session)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
|
||||
if (!al.filtered && hists__add_entry(&session->hists, &al)) {
|
||||
pr_warning("problem incrementing symbol count, "
|
||||
"skipping event\n");
|
||||
return -1;
|
||||
@ -145,106 +80,11 @@ static int process_sample_event(event_t *event, struct perf_session *session)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct objdump_line {
|
||||
struct list_head node;
|
||||
s64 offset;
|
||||
char *line;
|
||||
};
|
||||
|
||||
static struct objdump_line *objdump_line__new(s64 offset, char *line)
|
||||
{
|
||||
struct objdump_line *self = malloc(sizeof(*self));
|
||||
|
||||
if (self != NULL) {
|
||||
self->offset = offset;
|
||||
self->line = line;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void objdump_line__free(struct objdump_line *self)
|
||||
{
|
||||
free(self->line);
|
||||
free(self);
|
||||
}
|
||||
|
||||
static void objdump__add_line(struct list_head *head, struct objdump_line *line)
|
||||
{
|
||||
list_add_tail(&line->node, head);
|
||||
}
|
||||
|
||||
static struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
|
||||
struct objdump_line *pos)
|
||||
{
|
||||
list_for_each_entry_continue(pos, head, node)
|
||||
if (pos->offset >= 0)
|
||||
return pos;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_line(FILE *file, struct hist_entry *he,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct symbol *sym = he->sym;
|
||||
struct objdump_line *objdump_line;
|
||||
char *line = NULL, *tmp, *tmp2;
|
||||
size_t line_len;
|
||||
s64 line_ip, offset = -1;
|
||||
char *c;
|
||||
|
||||
if (getline(&line, &line_len, file) < 0)
|
||||
return -1;
|
||||
|
||||
if (!line)
|
||||
return -1;
|
||||
|
||||
c = strchr(line, '\n');
|
||||
if (c)
|
||||
*c = 0;
|
||||
|
||||
line_ip = -1;
|
||||
|
||||
/*
|
||||
* Strip leading spaces:
|
||||
*/
|
||||
tmp = line;
|
||||
while (*tmp) {
|
||||
if (*tmp != ' ')
|
||||
break;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
if (*tmp) {
|
||||
/*
|
||||
* Parse hexa addresses followed by ':'
|
||||
*/
|
||||
line_ip = strtoull(tmp, &tmp2, 16);
|
||||
if (*tmp2 != ':')
|
||||
line_ip = -1;
|
||||
}
|
||||
|
||||
if (line_ip != -1) {
|
||||
u64 start = map__rip_2objdump(he->map, sym->start);
|
||||
offset = line_ip - start;
|
||||
}
|
||||
|
||||
objdump_line = objdump_line__new(offset, line);
|
||||
if (objdump_line == NULL) {
|
||||
free(line);
|
||||
return -1;
|
||||
}
|
||||
objdump__add_line(head, objdump_line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int objdump_line__print(struct objdump_line *self,
|
||||
struct list_head *head,
|
||||
struct hist_entry *he, u64 len)
|
||||
{
|
||||
struct symbol *sym = he->sym;
|
||||
struct symbol *sym = he->ms.sym;
|
||||
static const char *prev_line;
|
||||
static const char *prev_color;
|
||||
|
||||
@ -327,7 +167,7 @@ static void insert_source_line(struct sym_ext *sym_ext)
|
||||
|
||||
static void free_source_line(struct hist_entry *he, int len)
|
||||
{
|
||||
struct sym_priv *priv = symbol__priv(he->sym);
|
||||
struct sym_priv *priv = symbol__priv(he->ms.sym);
|
||||
struct sym_ext *sym_ext = priv->ext;
|
||||
int i;
|
||||
|
||||
@ -346,7 +186,7 @@ static void free_source_line(struct hist_entry *he, int len)
|
||||
static void
|
||||
get_source_line(struct hist_entry *he, int len, const char *filename)
|
||||
{
|
||||
struct symbol *sym = he->sym;
|
||||
struct symbol *sym = he->ms.sym;
|
||||
u64 start;
|
||||
int i;
|
||||
char cmd[PATH_MAX * 2];
|
||||
@ -361,7 +201,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename)
|
||||
if (!priv->ext)
|
||||
return;
|
||||
|
||||
start = he->map->unmap_ip(he->map, sym->start);
|
||||
start = he->ms.map->unmap_ip(he->ms.map, sym->start);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char *path = NULL;
|
||||
@ -425,7 +265,7 @@ static void print_summary(const char *filename)
|
||||
|
||||
static void hist_entry__print_hits(struct hist_entry *self)
|
||||
{
|
||||
struct symbol *sym = self->sym;
|
||||
struct symbol *sym = self->ms.sym;
|
||||
struct sym_priv *priv = symbol__priv(sym);
|
||||
struct sym_hist *h = priv->hist;
|
||||
u64 len = sym->end - sym->start, offset;
|
||||
@ -439,23 +279,17 @@ static void hist_entry__print_hits(struct hist_entry *self)
|
||||
|
||||
static void annotate_sym(struct hist_entry *he)
|
||||
{
|
||||
struct map *map = he->map;
|
||||
struct map *map = he->ms.map;
|
||||
struct dso *dso = map->dso;
|
||||
struct symbol *sym = he->sym;
|
||||
struct symbol *sym = he->ms.sym;
|
||||
const char *filename = dso->long_name, *d_filename;
|
||||
u64 len;
|
||||
char command[PATH_MAX*2];
|
||||
FILE *file;
|
||||
LIST_HEAD(head);
|
||||
struct objdump_line *pos, *n;
|
||||
|
||||
if (!filename)
|
||||
if (hist_entry__annotate(he, &head) < 0)
|
||||
return;
|
||||
|
||||
pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
|
||||
filename, sym->name, map->unmap_ip(map, sym->start),
|
||||
map->unmap_ip(map, sym->end));
|
||||
|
||||
if (full_paths)
|
||||
d_filename = filename;
|
||||
else
|
||||
@ -472,29 +306,6 @@ static void annotate_sym(struct hist_entry *he)
|
||||
printf(" Percent | Source code & Disassembly of %s\n", d_filename);
|
||||
printf("------------------------------------------------\n");
|
||||
|
||||
if (verbose >= 2)
|
||||
printf("annotating [%p] %30s : [%p] %30s\n",
|
||||
dso, dso->long_name, sym, sym->name);
|
||||
|
||||
sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
|
||||
map__rip_2objdump(map, sym->start),
|
||||
map__rip_2objdump(map, sym->end),
|
||||
filename, filename);
|
||||
|
||||
if (verbose >= 3)
|
||||
printf("doing: %s\n", command);
|
||||
|
||||
file = popen(command, "r");
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
while (!feof(file)) {
|
||||
if (parse_line(file, he, &head) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
pclose(file);
|
||||
|
||||
if (verbose)
|
||||
hist_entry__print_hits(he);
|
||||
|
||||
@ -508,25 +319,25 @@ static void annotate_sym(struct hist_entry *he)
|
||||
free_source_line(he, len);
|
||||
}
|
||||
|
||||
static void perf_session__find_annotations(struct perf_session *self)
|
||||
static void hists__find_annotations(struct hists *self)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
|
||||
for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
|
||||
struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
|
||||
struct sym_priv *priv;
|
||||
|
||||
if (he->sym == NULL)
|
||||
if (he->ms.sym == NULL)
|
||||
continue;
|
||||
|
||||
priv = symbol__priv(he->sym);
|
||||
priv = symbol__priv(he->ms.sym);
|
||||
if (priv->hist == NULL)
|
||||
continue;
|
||||
|
||||
annotate_sym(he);
|
||||
/*
|
||||
* Since we have a hist_entry per IP for the same symbol, free
|
||||
* he->sym->hist to signal we already processed this symbol.
|
||||
* he->ms.sym->hist to signal we already processed this symbol.
|
||||
*/
|
||||
free(priv->hist);
|
||||
priv->hist = NULL;
|
||||
@ -545,7 +356,7 @@ static int __cmd_annotate(void)
|
||||
int ret;
|
||||
struct perf_session *session;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, force);
|
||||
session = perf_session__new(input_name, O_RDONLY, force, false);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -554,7 +365,7 @@ static int __cmd_annotate(void)
|
||||
goto out_delete;
|
||||
|
||||
if (dump_trace) {
|
||||
event__print_totals();
|
||||
perf_session__fprintf_nr_events(session, stdout);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
@ -562,11 +373,11 @@ static int __cmd_annotate(void)
|
||||
perf_session__fprintf(session, stdout);
|
||||
|
||||
if (verbose > 2)
|
||||
dsos__fprintf(stdout);
|
||||
perf_session__fprintf_dsos(session, stdout);
|
||||
|
||||
perf_session__collapse_resort(&session->hists);
|
||||
perf_session__output_resort(&session->hists, session->event_total[0]);
|
||||
perf_session__find_annotations(session);
|
||||
hists__collapse_resort(&session->hists);
|
||||
hists__output_resort(&session->hists);
|
||||
hists__find_annotations(&session->hists);
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
|
||||
@ -581,10 +392,12 @@ static const char * const annotate_usage[] = {
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
|
||||
"symbol to annotate"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('v', "verbose", &verbose,
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user