mirror of
https://github.com/adulau/aha.git
synced 2024-12-05 00:17:23 +00:00
Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (57 commits) x86, perf events: Check if we have APIC enabled perf_event: Fix variable initialization in other codepaths perf kmem: Fix unused argument build warning perf symbols: perf_header__read_build_ids() offset'n'size should be u64 perf symbols: dsos__read_build_ids() should read both user and kernel buildids perf tools: Align long options which have no short forms perf kmem: Show usage if no option is specified sched: Mark sched_clock() as notrace perf sched: Add max delay time snapshot perf tools: Correct size given to memset perf_event: Fix perf_swevent_hrtimer() variable initialization perf sched: Fix for getting task's execution time tracing/kprobes: Fix field creation's bad error handling perf_event: Cleanup for cpu_clock_perf_event_update() perf_event: Allocate children's perf_event_ctxp at the right time perf_event: Clean up __perf_event_init_context() hw-breakpoints: Modify breakpoints without unregistering them perf probe: Update perf-probe document perf probe: Support --del option trace-kprobe: Support delete probe syntax ...
This commit is contained in:
commit
6f696eb17b
40 changed files with 889 additions and 576 deletions
|
@ -187,8 +187,8 @@ config HAVE_MMIOTRACE_SUPPORT
|
|||
def_bool y
|
||||
|
||||
config X86_DECODER_SELFTEST
|
||||
bool "x86 instruction decoder selftest"
|
||||
depends on DEBUG_KERNEL
|
||||
bool "x86 instruction decoder selftest"
|
||||
depends on DEBUG_KERNEL && KPROBES
|
||||
---help---
|
||||
Perform x86 instruction decoder selftests at build time.
|
||||
This option is useful for checking the sanity of x86 instruction
|
||||
|
|
|
@ -1632,6 +1632,7 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_events *cpuc)
|
|||
|
||||
data.period = event->hw.last_period;
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
regs.ip = 0;
|
||||
|
||||
/*
|
||||
|
@ -1749,6 +1750,7 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
|
|||
u64 val;
|
||||
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
|
||||
cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
|
@ -1794,6 +1796,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
|
|||
u64 ack, status;
|
||||
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
|
||||
cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
|
@ -1857,6 +1860,7 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
|
|||
u64 val;
|
||||
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
|
||||
cpuc = &__get_cpu_var(cpu_hw_events);
|
||||
|
||||
|
@ -2062,12 +2066,6 @@ static __init int p6_pmu_init(void)
|
|||
|
||||
x86_pmu = p6_pmu;
|
||||
|
||||
if (!cpu_has_apic) {
|
||||
pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
|
||||
pr_info("no hardware sampling interrupt available.\n");
|
||||
x86_pmu.apic = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2159,6 +2157,16 @@ static __init int amd_pmu_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void __init pmu_check_apic(void)
|
||||
{
|
||||
if (cpu_has_apic)
|
||||
return;
|
||||
|
||||
x86_pmu.apic = 0;
|
||||
pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
|
||||
pr_info("no hardware sampling interrupt available.\n");
|
||||
}
|
||||
|
||||
void __init init_hw_perf_events(void)
|
||||
{
|
||||
int err;
|
||||
|
@ -2180,6 +2188,8 @@ void __init init_hw_perf_events(void)
|
|||
return;
|
||||
}
|
||||
|
||||
pmu_check_apic();
|
||||
|
||||
pr_cont("%s PMU driver.\n", x86_pmu.name);
|
||||
|
||||
if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) {
|
||||
|
@ -2287,7 +2297,7 @@ void callchain_store(struct perf_callchain_entry *entry, u64 ip)
|
|||
|
||||
static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry);
|
||||
static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_nmi_entry);
|
||||
static DEFINE_PER_CPU(int, in_nmi_frame);
|
||||
static DEFINE_PER_CPU(int, in_ignored_frame);
|
||||
|
||||
|
||||
static void
|
||||
|
@ -2303,8 +2313,9 @@ static void backtrace_warning(void *data, char *msg)
|
|||
|
||||
static int backtrace_stack(void *data, char *name)
|
||||
{
|
||||
per_cpu(in_nmi_frame, smp_processor_id()) =
|
||||
x86_is_stack_id(NMI_STACK, name);
|
||||
per_cpu(in_ignored_frame, smp_processor_id()) =
|
||||
x86_is_stack_id(NMI_STACK, name) ||
|
||||
x86_is_stack_id(DEBUG_STACK, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2313,7 +2324,7 @@ static void backtrace_address(void *data, unsigned long addr, int reliable)
|
|||
{
|
||||
struct perf_callchain_entry *entry = data;
|
||||
|
||||
if (per_cpu(in_nmi_frame, smp_processor_id()))
|
||||
if (per_cpu(in_ignored_frame, smp_processor_id()))
|
||||
return;
|
||||
|
||||
if (reliable)
|
||||
|
|
|
@ -103,6 +103,35 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
in_irq_stack(unsigned long *stack, unsigned long *irq_stack,
|
||||
unsigned long *irq_stack_end)
|
||||
{
|
||||
return (stack >= irq_stack && stack < irq_stack_end);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are returning from the irq stack and go to the previous one.
|
||||
* If the previous stack is also in the irq stack, then bp in the first
|
||||
* frame of the irq stack points to the previous, interrupted one.
|
||||
* Otherwise we have another level of indirection: We first save
|
||||
* the bp of the previous stack, then we switch the stack to the irq one
|
||||
* and save a new bp that links to the previous one.
|
||||
* (See save_args())
|
||||
*/
|
||||
static inline unsigned long
|
||||
fixup_bp_irq_link(unsigned long bp, unsigned long *stack,
|
||||
unsigned long *irq_stack, unsigned long *irq_stack_end)
|
||||
{
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
struct stack_frame *frame = (struct stack_frame *)bp;
|
||||
|
||||
if (!in_irq_stack(stack, irq_stack, irq_stack_end))
|
||||
return (unsigned long)frame->next_frame;
|
||||
#endif
|
||||
return bp;
|
||||
}
|
||||
|
||||
/*
|
||||
* x86-64 can have up to three kernel stacks:
|
||||
* process stack
|
||||
|
@ -175,7 +204,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
|
|||
irq_stack = irq_stack_end -
|
||||
(IRQ_STACK_SIZE - 64) / sizeof(*irq_stack);
|
||||
|
||||
if (stack >= irq_stack && stack < irq_stack_end) {
|
||||
if (in_irq_stack(stack, irq_stack, irq_stack_end)) {
|
||||
if (ops->stack(data, "IRQ") < 0)
|
||||
break;
|
||||
bp = print_context_stack(tinfo, stack, bp,
|
||||
|
@ -186,6 +215,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
|
|||
* pointer (index -1 to end) in the IRQ stack:
|
||||
*/
|
||||
stack = (unsigned long *) (irq_stack_end[-1]);
|
||||
bp = fixup_bp_irq_link(bp, stack, irq_stack,
|
||||
irq_stack_end);
|
||||
irq_stack_end = NULL;
|
||||
ops->stack(data, "EOI");
|
||||
continue;
|
||||
|
|
|
@ -1076,10 +1076,10 @@ ENTRY(\sym)
|
|||
TRACE_IRQS_OFF
|
||||
movq %rsp,%rdi /* pt_regs pointer */
|
||||
xorl %esi,%esi /* no error code */
|
||||
PER_CPU(init_tss, %rbp)
|
||||
subq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%rbp)
|
||||
PER_CPU(init_tss, %r12)
|
||||
subq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%r12)
|
||||
call \do_sym
|
||||
addq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%rbp)
|
||||
addq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%r12)
|
||||
jmp paranoid_exit /* %ebx: no swapgs flag */
|
||||
CFI_ENDPROC
|
||||
END(\sym)
|
||||
|
|
|
@ -362,8 +362,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (bp->callback)
|
||||
ret = arch_store_info(bp);
|
||||
ret = arch_store_info(bp);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -519,7 +518,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
|
|||
break;
|
||||
}
|
||||
|
||||
(bp->callback)(bp, args->regs);
|
||||
perf_bp_event(bp, args->regs);
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
|
|
@ -555,7 +555,9 @@ static int genregs_set(struct task_struct *target,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ptrace_triggered(struct perf_event *bp, void *data)
|
||||
static void ptrace_triggered(struct perf_event *bp, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int i;
|
||||
struct thread_struct *thread = &(current->thread);
|
||||
|
@ -593,13 +595,13 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
|
|||
return dr7;
|
||||
}
|
||||
|
||||
static struct perf_event *
|
||||
static int
|
||||
ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
|
||||
struct task_struct *tsk, int disabled)
|
||||
{
|
||||
int err;
|
||||
int gen_len, gen_type;
|
||||
DEFINE_BREAKPOINT_ATTR(attr);
|
||||
struct perf_event_attr attr;
|
||||
|
||||
/*
|
||||
* We shoud have at least an inactive breakpoint at this
|
||||
|
@ -607,18 +609,18 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
|
|||
* written the address register first
|
||||
*/
|
||||
if (!bp)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
|
||||
err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
|
||||
attr = bp->attr;
|
||||
attr.bp_len = gen_len;
|
||||
attr.bp_type = gen_type;
|
||||
attr.disabled = disabled;
|
||||
|
||||
return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
|
||||
return modify_user_hw_breakpoint(bp, &attr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -656,28 +658,17 @@ restore:
|
|||
if (!second_pass)
|
||||
continue;
|
||||
|
||||
thread->ptrace_bps[i] = NULL;
|
||||
bp = ptrace_modify_breakpoint(bp, len, type,
|
||||
rc = ptrace_modify_breakpoint(bp, len, type,
|
||||
tsk, 1);
|
||||
if (IS_ERR(bp)) {
|
||||
rc = PTR_ERR(bp);
|
||||
thread->ptrace_bps[i] = NULL;
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
thread->ptrace_bps[i] = bp;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0);
|
||||
|
||||
/* Incorrect bp, or we have a bug in bp API */
|
||||
if (IS_ERR(bp)) {
|
||||
rc = PTR_ERR(bp);
|
||||
thread->ptrace_bps[i] = NULL;
|
||||
rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
thread->ptrace_bps[i] = bp;
|
||||
}
|
||||
/*
|
||||
* Make a second pass to free the remaining unused breakpoints
|
||||
|
@ -721,9 +712,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
|||
{
|
||||
struct perf_event *bp;
|
||||
struct thread_struct *t = &tsk->thread;
|
||||
DEFINE_BREAKPOINT_ATTR(attr);
|
||||
struct perf_event_attr attr;
|
||||
|
||||
if (!t->ptrace_bps[nr]) {
|
||||
hw_breakpoint_init(&attr);
|
||||
/*
|
||||
* Put stub len and type to register (reserve) an inactive but
|
||||
* correct bp
|
||||
|
@ -734,26 +726,32 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
|||
attr.disabled = 1;
|
||||
|
||||
bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
|
||||
|
||||
/*
|
||||
* CHECKME: the previous code returned -EIO if the addr wasn't
|
||||
* a valid task virtual addr. The new one will return -EINVAL in
|
||||
* this case.
|
||||
* -EINVAL may be what we want for in-kernel breakpoints users,
|
||||
* but -EIO looks better for ptrace, since we refuse a register
|
||||
* writing for the user. And anyway this is the previous
|
||||
* behaviour.
|
||||
*/
|
||||
if (IS_ERR(bp))
|
||||
return PTR_ERR(bp);
|
||||
|
||||
t->ptrace_bps[nr] = bp;
|
||||
} else {
|
||||
int err;
|
||||
|
||||
bp = t->ptrace_bps[nr];
|
||||
t->ptrace_bps[nr] = NULL;
|
||||
|
||||
attr = bp->attr;
|
||||
attr.bp_addr = addr;
|
||||
bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
|
||||
err = modify_user_hw_breakpoint(bp, &attr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* CHECKME: the previous code returned -EIO if the addr wasn't a
|
||||
* valid task virtual addr. The new one will return -EINVAL in this
|
||||
* case.
|
||||
* -EINVAL may be what we want for in-kernel breakpoints users, but
|
||||
* -EIO looks better for ptrace, since we refuse a register writing
|
||||
* for the user. And anyway this is the previous behaviour.
|
||||
*/
|
||||
if (IS_ERR(bp))
|
||||
return PTR_ERR(bp);
|
||||
|
||||
t->ptrace_bps[nr] = bp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
inat_tables_script = $(srctree)/arch/x86/tools/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt
|
||||
quiet_cmd_inat_tables = GEN $@
|
||||
cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@
|
||||
cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ || rm -f $@
|
||||
|
||||
$(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
$(call cmd,inat_tables)
|
||||
|
@ -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-y += insn.o inat.o
|
||||
lib-$(CONFIG_KPROBES) += insn.o inat.o
|
||||
|
||||
obj-y += msr-reg.o msr-reg-export.o
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ int main(int argc, char **argv)
|
|||
char line[BUFSIZE], sym[BUFSIZE] = "<unknown>";
|
||||
unsigned char insn_buf[16];
|
||||
struct insn insn;
|
||||
int insns = 0, c;
|
||||
int insns = 0;
|
||||
int warnings = 0;
|
||||
|
||||
parse_args(argc, argv);
|
||||
|
|
|
@ -20,19 +20,18 @@ enum {
|
|||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
|
||||
/* As it's for in-kernel or ptrace use, we want it to be pinned */
|
||||
#define DEFINE_BREAKPOINT_ATTR(name) \
|
||||
struct perf_event_attr name = { \
|
||||
.type = PERF_TYPE_BREAKPOINT, \
|
||||
.size = sizeof(name), \
|
||||
.pinned = 1, \
|
||||
};
|
||||
|
||||
static inline void hw_breakpoint_init(struct perf_event_attr *attr)
|
||||
{
|
||||
memset(attr, 0, sizeof(*attr));
|
||||
|
||||
attr->type = PERF_TYPE_BREAKPOINT;
|
||||
attr->size = sizeof(*attr);
|
||||
/*
|
||||
* As it's for in-kernel or ptrace use, we want it to be pinned
|
||||
* and to call its callback every hits.
|
||||
*/
|
||||
attr->pinned = 1;
|
||||
attr->sample_period = 1;
|
||||
}
|
||||
|
||||
static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
|
||||
|
@ -52,27 +51,24 @@ static inline int hw_breakpoint_len(struct perf_event *bp)
|
|||
|
||||
extern struct perf_event *
|
||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
perf_overflow_handler_t triggered,
|
||||
struct task_struct *tsk);
|
||||
|
||||
/* FIXME: only change from the attr, and don't unregister */
|
||||
extern struct perf_event *
|
||||
modify_user_hw_breakpoint(struct perf_event *bp,
|
||||
struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
struct task_struct *tsk);
|
||||
extern int
|
||||
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
|
||||
|
||||
/*
|
||||
* Kernel breakpoints are not associated with any particular thread.
|
||||
*/
|
||||
extern struct perf_event *
|
||||
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
perf_overflow_handler_t triggered,
|
||||
int cpu);
|
||||
|
||||
extern struct perf_event **
|
||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered);
|
||||
perf_overflow_handler_t triggered);
|
||||
|
||||
extern int register_perf_hw_breakpoint(struct perf_event *bp);
|
||||
extern int __register_perf_hw_breakpoint(struct perf_event *bp);
|
||||
|
@ -93,20 +89,18 @@ static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
|
|||
|
||||
static inline struct perf_event *
|
||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
perf_overflow_handler_t triggered,
|
||||
struct task_struct *tsk) { return NULL; }
|
||||
static inline struct perf_event *
|
||||
static inline int
|
||||
modify_user_hw_breakpoint(struct perf_event *bp,
|
||||
struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
struct task_struct *tsk) { return NULL; }
|
||||
struct perf_event_attr *attr) { return NULL; }
|
||||
static inline struct perf_event *
|
||||
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
perf_overflow_handler_t triggered,
|
||||
int cpu) { return NULL; }
|
||||
static inline struct perf_event **
|
||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered) { return NULL; }
|
||||
perf_overflow_handler_t triggered) { return NULL; }
|
||||
static inline int
|
||||
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
|
||||
static inline int
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
#include <asm/hw_breakpoint.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* User-space ABI bits:
|
||||
*/
|
||||
|
@ -215,12 +211,12 @@ struct perf_event_attr {
|
|||
__u32 wakeup_watermark; /* bytes before wakeup */
|
||||
};
|
||||
|
||||
union {
|
||||
struct { /* Hardware breakpoint info */
|
||||
__u64 bp_addr;
|
||||
__u32 bp_type;
|
||||
__u32 bp_len;
|
||||
};
|
||||
struct { /* Hardware breakpoint info */
|
||||
__u64 bp_addr;
|
||||
__u32 bp_type;
|
||||
__u32 bp_len;
|
||||
__u64 __bp_reserved_1;
|
||||
__u64 __bp_reserved_2;
|
||||
};
|
||||
|
||||
__u32 __reserved_2;
|
||||
|
@ -451,6 +447,10 @@ enum perf_callchain_context {
|
|||
# include <asm/perf_event.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
#include <asm/hw_breakpoint.h>
|
||||
#endif
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rculist.h>
|
||||
|
@ -565,10 +565,12 @@ struct perf_pending_entry {
|
|||
void (*func)(struct perf_pending_entry *);
|
||||
};
|
||||
|
||||
typedef void (*perf_callback_t)(struct perf_event *, void *);
|
||||
|
||||
struct perf_sample_data;
|
||||
|
||||
typedef void (*perf_overflow_handler_t)(struct perf_event *, int,
|
||||
struct perf_sample_data *,
|
||||
struct pt_regs *regs);
|
||||
|
||||
/**
|
||||
* struct perf_event - performance event kernel representation:
|
||||
*/
|
||||
|
@ -660,18 +662,12 @@ struct perf_event {
|
|||
struct pid_namespace *ns;
|
||||
u64 id;
|
||||
|
||||
void (*overflow_handler)(struct perf_event *event,
|
||||
int nmi, struct perf_sample_data *data,
|
||||
struct pt_regs *regs);
|
||||
perf_overflow_handler_t overflow_handler;
|
||||
|
||||
#ifdef CONFIG_EVENT_PROFILE
|
||||
struct event_filter *filter;
|
||||
#endif
|
||||
|
||||
perf_callback_t callback;
|
||||
|
||||
perf_callback_t event_callback;
|
||||
|
||||
#endif /* CONFIG_PERF_EVENTS */
|
||||
};
|
||||
|
||||
|
@ -781,7 +777,7 @@ extern struct perf_event *
|
|||
perf_event_create_kernel_counter(struct perf_event_attr *attr,
|
||||
int cpu,
|
||||
pid_t pid,
|
||||
perf_callback_t callback);
|
||||
perf_overflow_handler_t callback);
|
||||
extern u64 perf_event_read_value(struct perf_event *event,
|
||||
u64 *enabled, u64 *running);
|
||||
|
||||
|
@ -876,6 +872,8 @@ extern void perf_output_copy(struct perf_output_handle *handle,
|
|||
const void *buf, unsigned int len);
|
||||
extern int perf_swevent_get_recursion_context(void);
|
||||
extern void perf_swevent_put_recursion_context(int rctx);
|
||||
extern void perf_event_enable(struct perf_event *event);
|
||||
extern void perf_event_disable(struct perf_event *event);
|
||||
#else
|
||||
static inline void
|
||||
perf_event_task_sched_in(struct task_struct *task, int cpu) { }
|
||||
|
@ -906,7 +904,8 @@ static inline void perf_event_fork(struct task_struct *tsk) { }
|
|||
static inline void perf_event_init(void) { }
|
||||
static inline int perf_swevent_get_recursion_context(void) { return -1; }
|
||||
static inline void perf_swevent_put_recursion_context(int rctx) { }
|
||||
|
||||
static inline void perf_event_enable(struct perf_event *event) { }
|
||||
static inline void perf_event_disable(struct perf_event *event) { }
|
||||
#endif
|
||||
|
||||
#define perf_output_put(handle, x) \
|
||||
|
|
|
@ -1840,7 +1840,8 @@ static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask)
|
|||
extern int sched_clock_stable;
|
||||
#endif
|
||||
|
||||
extern unsigned long long sched_clock(void);
|
||||
/* ftrace calls sched_clock() directly */
|
||||
extern unsigned long long notrace sched_clock(void);
|
||||
|
||||
extern void sched_clock_init(void);
|
||||
extern u64 sched_clock_cpu(int cpu);
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned);
|
||||
|
||||
/* Number of pinned task breakpoints in a cpu */
|
||||
static DEFINE_PER_CPU(unsigned int, task_bp_pinned[HBP_NUM]);
|
||||
static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]);
|
||||
|
||||
/* Number of non-pinned cpu/task breakpoints in a cpu */
|
||||
static DEFINE_PER_CPU(unsigned int, nr_bp_flexible);
|
||||
|
@ -73,7 +73,7 @@ static DEFINE_MUTEX(nr_bp_mutex);
|
|||
static unsigned int max_task_bp_pinned(int cpu)
|
||||
{
|
||||
int i;
|
||||
unsigned int *tsk_pinned = per_cpu(task_bp_pinned, cpu);
|
||||
unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu);
|
||||
|
||||
for (i = HBP_NUM -1; i >= 0; i--) {
|
||||
if (tsk_pinned[i] > 0)
|
||||
|
@ -83,50 +83,16 @@ static unsigned int max_task_bp_pinned(int cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the number of pinned/un-pinned breakpoints we have in
|
||||
* a given cpu (cpu > -1) or in all of them (cpu = -1).
|
||||
*/
|
||||
static void fetch_bp_busy_slots(struct bp_busy_slots *slots, int cpu)
|
||||
static int task_bp_pinned(struct task_struct *tsk)
|
||||
{
|
||||
if (cpu >= 0) {
|
||||
slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
slots->pinned += max_task_bp_pinned(cpu);
|
||||
slots->flexible = per_cpu(nr_bp_flexible, cpu);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
unsigned int nr;
|
||||
|
||||
nr = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
nr += max_task_bp_pinned(cpu);
|
||||
|
||||
if (nr > slots->pinned)
|
||||
slots->pinned = nr;
|
||||
|
||||
nr = per_cpu(nr_bp_flexible, cpu);
|
||||
|
||||
if (nr > slots->flexible)
|
||||
slots->flexible = nr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
int count = 0;
|
||||
struct perf_event *bp;
|
||||
struct perf_event_context *ctx = tsk->perf_event_ctxp;
|
||||
unsigned int *tsk_pinned;
|
||||
struct list_head *list;
|
||||
struct perf_event *bp;
|
||||
unsigned long flags;
|
||||
int count = 0;
|
||||
|
||||
if (WARN_ONCE(!ctx, "No perf context for this task"))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
list = &ctx->event_list;
|
||||
|
||||
|
@ -143,10 +109,60 @@ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable)
|
|||
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
if (WARN_ONCE(count < 0, "No breakpoint counter found in the counter list"))
|
||||
return;
|
||||
return count;
|
||||
}
|
||||
|
||||
tsk_pinned = per_cpu(task_bp_pinned, cpu);
|
||||
/*
|
||||
* Report the number of pinned/un-pinned breakpoints we have in
|
||||
* 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)
|
||||
{
|
||||
int cpu = bp->cpu;
|
||||
struct task_struct *tsk = bp->ctx->task;
|
||||
|
||||
if (cpu >= 0) {
|
||||
slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
if (!tsk)
|
||||
slots->pinned += max_task_bp_pinned(cpu);
|
||||
else
|
||||
slots->pinned += task_bp_pinned(tsk);
|
||||
slots->flexible = per_cpu(nr_bp_flexible, cpu);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
unsigned int nr;
|
||||
|
||||
nr = per_cpu(nr_cpu_bp_pinned, cpu);
|
||||
if (!tsk)
|
||||
nr += max_task_bp_pinned(cpu);
|
||||
else
|
||||
nr += task_bp_pinned(tsk);
|
||||
|
||||
if (nr > slots->pinned)
|
||||
slots->pinned = nr;
|
||||
|
||||
nr = per_cpu(nr_bp_flexible, cpu);
|
||||
|
||||
if (nr > slots->flexible)
|
||||
slots->flexible = nr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
unsigned int *tsk_pinned;
|
||||
int count = 0;
|
||||
|
||||
count = task_bp_pinned(tsk);
|
||||
|
||||
tsk_pinned = per_cpu(nr_task_bp_pinned, cpu);
|
||||
if (enable) {
|
||||
tsk_pinned[count]++;
|
||||
if (count > 0)
|
||||
|
@ -193,7 +209,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
|||
* - If attached to a single cpu, check:
|
||||
*
|
||||
* (per_cpu(nr_bp_flexible, cpu) || (per_cpu(nr_cpu_bp_pinned, cpu)
|
||||
* + max(per_cpu(task_bp_pinned, cpu)))) < HBP_NUM
|
||||
* + max(per_cpu(nr_task_bp_pinned, cpu)))) < HBP_NUM
|
||||
*
|
||||
* -> If there are already non-pinned counters in this cpu, it means
|
||||
* there is already a free slot for them.
|
||||
|
@ -204,7 +220,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
|||
* - If attached to every cpus, check:
|
||||
*
|
||||
* (per_cpu(nr_bp_flexible, *) || (max(per_cpu(nr_cpu_bp_pinned, *))
|
||||
* + max(per_cpu(task_bp_pinned, *)))) < HBP_NUM
|
||||
* + max(per_cpu(nr_task_bp_pinned, *)))) < HBP_NUM
|
||||
*
|
||||
* -> This is roughly the same, except we check the number of per cpu
|
||||
* bp for every cpu and we keep the max one. Same for the per tasks
|
||||
|
@ -216,7 +232,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
|||
* - If attached to a single cpu, check:
|
||||
*
|
||||
* ((per_cpu(nr_bp_flexible, cpu) > 1) + per_cpu(nr_cpu_bp_pinned, cpu)
|
||||
* + max(per_cpu(task_bp_pinned, cpu))) < HBP_NUM
|
||||
* + max(per_cpu(nr_task_bp_pinned, cpu))) < HBP_NUM
|
||||
*
|
||||
* -> Same checks as before. But now the nr_bp_flexible, if any, must keep
|
||||
* one register at least (or they will never be fed).
|
||||
|
@ -224,7 +240,7 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable)
|
|||
* - If attached to every cpus, check:
|
||||
*
|
||||
* ((per_cpu(nr_bp_flexible, *) > 1) + max(per_cpu(nr_cpu_bp_pinned, *))
|
||||
* + max(per_cpu(task_bp_pinned, *))) < HBP_NUM
|
||||
* + max(per_cpu(nr_task_bp_pinned, *))) < HBP_NUM
|
||||
*/
|
||||
int reserve_bp_slot(struct perf_event *bp)
|
||||
{
|
||||
|
@ -233,7 +249,7 @@ int reserve_bp_slot(struct perf_event *bp)
|
|||
|
||||
mutex_lock(&nr_bp_mutex);
|
||||
|
||||
fetch_bp_busy_slots(&slots, bp->cpu);
|
||||
fetch_bp_busy_slots(&slots, bp);
|
||||
|
||||
/* Flexible counters need to keep at least one slot */
|
||||
if (slots.pinned + (!!slots.flexible) == HBP_NUM) {
|
||||
|
@ -259,7 +275,7 @@ void release_bp_slot(struct perf_event *bp)
|
|||
}
|
||||
|
||||
|
||||
int __register_perf_hw_breakpoint(struct perf_event *bp)
|
||||
int register_perf_hw_breakpoint(struct perf_event *bp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -276,19 +292,12 @@ int __register_perf_hw_breakpoint(struct perf_event *bp)
|
|||
* This is a quick hack that will be removed soon, once we remove
|
||||
* the tmp breakpoints from ptrace
|
||||
*/
|
||||
if (!bp->attr.disabled || bp->callback == perf_bp_event)
|
||||
if (!bp->attr.disabled || !bp->overflow_handler)
|
||||
ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int register_perf_hw_breakpoint(struct perf_event *bp)
|
||||
{
|
||||
bp->callback = perf_bp_event;
|
||||
|
||||
return __register_perf_hw_breakpoint(bp);
|
||||
}
|
||||
|
||||
/**
|
||||
* register_user_hw_breakpoint - register a hardware breakpoint for user space
|
||||
* @attr: breakpoint attributes
|
||||
|
@ -297,7 +306,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
|
|||
*/
|
||||
struct perf_event *
|
||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
perf_overflow_handler_t triggered,
|
||||
struct task_struct *tsk)
|
||||
{
|
||||
return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
|
||||
|
@ -311,19 +320,40 @@ EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
|||
* @triggered: callback to trigger when we hit the breakpoint
|
||||
* @tsk: pointer to 'task_struct' of the process to which the address belongs
|
||||
*/
|
||||
struct perf_event *
|
||||
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr,
|
||||
perf_callback_t triggered,
|
||||
struct task_struct *tsk)
|
||||
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
|
||||
{
|
||||
/*
|
||||
* FIXME: do it without unregistering
|
||||
* - We don't want to lose our slot
|
||||
* - If the new bp is incorrect, don't lose the older one
|
||||
*/
|
||||
unregister_hw_breakpoint(bp);
|
||||
u64 old_addr = bp->attr.bp_addr;
|
||||
int old_type = bp->attr.bp_type;
|
||||
int old_len = bp->attr.bp_len;
|
||||
int err = 0;
|
||||
|
||||
return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
|
||||
perf_event_disable(bp);
|
||||
|
||||
bp->attr.bp_addr = attr->bp_addr;
|
||||
bp->attr.bp_type = attr->bp_type;
|
||||
bp->attr.bp_len = attr->bp_len;
|
||||
|
||||
if (attr->disabled)
|
||||
goto end;
|
||||
|
||||
err = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
|
||||
if (!err)
|
||||
perf_event_enable(bp);
|
||||
|
||||
if (err) {
|
||||
bp->attr.bp_addr = old_addr;
|
||||
bp->attr.bp_type = old_type;
|
||||
bp->attr.bp_len = old_len;
|
||||
if (!bp->attr.disabled)
|
||||
perf_event_enable(bp);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
end:
|
||||
bp->attr.disabled = attr->disabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
|
||||
|
||||
|
@ -348,7 +378,7 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
|
|||
*/
|
||||
struct perf_event **
|
||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
||||
perf_callback_t triggered)
|
||||
perf_overflow_handler_t triggered)
|
||||
{
|
||||
struct perf_event **cpu_events, **pevent, *bp;
|
||||
long err;
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
/*
|
||||
* Each CPU has a list of per CPU events:
|
||||
*/
|
||||
DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context);
|
||||
static DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context);
|
||||
|
||||
int perf_max_events __read_mostly = 1;
|
||||
static int perf_reserved_percpu __read_mostly;
|
||||
|
@ -567,7 +567,7 @@ static void __perf_event_disable(void *info)
|
|||
* is the current context on this CPU and preemption is disabled,
|
||||
* hence we can't get into perf_event_task_sched_out for this context.
|
||||
*/
|
||||
static void perf_event_disable(struct perf_event *event)
|
||||
void perf_event_disable(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
struct task_struct *task = ctx->task;
|
||||
|
@ -971,7 +971,7 @@ static void __perf_event_enable(void *info)
|
|||
* perf_event_for_each_child or perf_event_for_each as described
|
||||
* for perf_event_disable.
|
||||
*/
|
||||
static void perf_event_enable(struct perf_event *event)
|
||||
void perf_event_enable(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
struct task_struct *task = ctx->task;
|
||||
|
@ -1579,7 +1579,6 @@ static void
|
|||
__perf_event_init_context(struct perf_event_context *ctx,
|
||||
struct task_struct *task)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
spin_lock_init(&ctx->lock);
|
||||
mutex_init(&ctx->mutex);
|
||||
INIT_LIST_HEAD(&ctx->group_list);
|
||||
|
@ -1654,7 +1653,7 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu)
|
|||
}
|
||||
|
||||
if (!ctx) {
|
||||
ctx = kmalloc(sizeof(struct perf_event_context), GFP_KERNEL);
|
||||
ctx = kzalloc(sizeof(struct perf_event_context), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!ctx)
|
||||
goto errout;
|
||||
|
@ -4011,6 +4010,7 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
|
|||
event->pmu->read(event);
|
||||
|
||||
data.addr = 0;
|
||||
data.raw = NULL;
|
||||
data.period = event->hw.last_period;
|
||||
regs = get_irq_regs();
|
||||
/*
|
||||
|
@ -4080,8 +4080,7 @@ static void cpu_clock_perf_event_update(struct perf_event *event)
|
|||
u64 now;
|
||||
|
||||
now = cpu_clock(cpu);
|
||||
prev = atomic64_read(&event->hw.prev_count);
|
||||
atomic64_set(&event->hw.prev_count, now);
|
||||
prev = atomic64_xchg(&event->hw.prev_count, now);
|
||||
atomic64_add(now - prev, &event->count);
|
||||
}
|
||||
|
||||
|
@ -4286,15 +4285,8 @@ static void bp_perf_event_destroy(struct perf_event *event)
|
|||
static const struct pmu *bp_perf_event_init(struct perf_event *bp)
|
||||
{
|
||||
int err;
|
||||
/*
|
||||
* The breakpoint is already filled if we haven't created the counter
|
||||
* through perf syscall
|
||||
* FIXME: manage to get trigerred to NULL if it comes from syscalls
|
||||
*/
|
||||
if (!bp->callback)
|
||||
err = register_perf_hw_breakpoint(bp);
|
||||
else
|
||||
err = __register_perf_hw_breakpoint(bp);
|
||||
|
||||
err = register_perf_hw_breakpoint(bp);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
|
@ -4308,6 +4300,7 @@ void perf_bp_event(struct perf_event *bp, void *data)
|
|||
struct perf_sample_data sample;
|
||||
struct pt_regs *regs = data;
|
||||
|
||||
sample.raw = NULL;
|
||||
sample.addr = bp->attr.bp_addr;
|
||||
|
||||
if (!perf_exclude_event(bp, regs))
|
||||
|
@ -4390,7 +4383,7 @@ perf_event_alloc(struct perf_event_attr *attr,
|
|||
struct perf_event_context *ctx,
|
||||
struct perf_event *group_leader,
|
||||
struct perf_event *parent_event,
|
||||
perf_callback_t callback,
|
||||
perf_overflow_handler_t overflow_handler,
|
||||
gfp_t gfpflags)
|
||||
{
|
||||
const struct pmu *pmu;
|
||||
|
@ -4433,10 +4426,10 @@ perf_event_alloc(struct perf_event_attr *attr,
|
|||
|
||||
event->state = PERF_EVENT_STATE_INACTIVE;
|
||||
|
||||
if (!callback && parent_event)
|
||||
callback = parent_event->callback;
|
||||
if (!overflow_handler && parent_event)
|
||||
overflow_handler = parent_event->overflow_handler;
|
||||
|
||||
event->callback = callback;
|
||||
event->overflow_handler = overflow_handler;
|
||||
|
||||
if (attr->disabled)
|
||||
event->state = PERF_EVENT_STATE_OFF;
|
||||
|
@ -4776,7 +4769,8 @@ err_put_context:
|
|||
*/
|
||||
struct perf_event *
|
||||
perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
|
||||
pid_t pid, perf_callback_t callback)
|
||||
pid_t pid,
|
||||
perf_overflow_handler_t overflow_handler)
|
||||
{
|
||||
struct perf_event *event;
|
||||
struct perf_event_context *ctx;
|
||||
|
@ -4793,7 +4787,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
|
|||
}
|
||||
|
||||
event = perf_event_alloc(attr, cpu, ctx, NULL,
|
||||
NULL, callback, GFP_KERNEL);
|
||||
NULL, overflow_handler, GFP_KERNEL);
|
||||
if (IS_ERR(event)) {
|
||||
err = PTR_ERR(event);
|
||||
goto err_put_context;
|
||||
|
@ -5090,7 +5084,7 @@ again:
|
|||
*/
|
||||
int perf_event_init_task(struct task_struct *child)
|
||||
{
|
||||
struct perf_event_context *child_ctx, *parent_ctx;
|
||||
struct perf_event_context *child_ctx = NULL, *parent_ctx;
|
||||
struct perf_event_context *cloned_ctx;
|
||||
struct perf_event *event;
|
||||
struct task_struct *parent = current;
|
||||
|
@ -5105,20 +5099,6 @@ int perf_event_init_task(struct task_struct *child)
|
|||
if (likely(!parent->perf_event_ctxp))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* This is executed from the parent task context, so inherit
|
||||
* events that have been marked for cloning.
|
||||
* First allocate and initialize a context for the child.
|
||||
*/
|
||||
|
||||
child_ctx = kmalloc(sizeof(struct perf_event_context), GFP_KERNEL);
|
||||
if (!child_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
__perf_event_init_context(child_ctx, child);
|
||||
child->perf_event_ctxp = child_ctx;
|
||||
get_task_struct(child);
|
||||
|
||||
/*
|
||||
* If the parent's context is a clone, pin it so it won't get
|
||||
* swapped under us.
|
||||
|
@ -5149,6 +5129,26 @@ int perf_event_init_task(struct task_struct *child)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!child->perf_event_ctxp) {
|
||||
/*
|
||||
* This is executed from the parent task context, so
|
||||
* inherit events that have been marked for cloning.
|
||||
* First allocate and initialize a context for the
|
||||
* child.
|
||||
*/
|
||||
|
||||
child_ctx = kzalloc(sizeof(struct perf_event_context),
|
||||
GFP_KERNEL);
|
||||
if (!child_ctx) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
__perf_event_init_context(child_ctx, child);
|
||||
child->perf_event_ctxp = child_ctx;
|
||||
get_task_struct(child);
|
||||
}
|
||||
|
||||
ret = inherit_group(event, parent, parent_ctx,
|
||||
child, child_ctx);
|
||||
if (ret) {
|
||||
|
@ -5177,6 +5177,7 @@ int perf_event_init_task(struct task_struct *child)
|
|||
get_ctx(child_ctx->parent_ctx);
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&parent_ctx->mutex);
|
||||
|
||||
perf_unpin_context(parent_ctx);
|
||||
|
|
|
@ -606,23 +606,22 @@ static int create_trace_probe(int argc, char **argv)
|
|||
*/
|
||||
struct trace_probe *tp;
|
||||
int i, ret = 0;
|
||||
int is_return = 0;
|
||||
int is_return = 0, is_delete = 0;
|
||||
char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL;
|
||||
unsigned long offset = 0;
|
||||
void *addr = NULL;
|
||||
char buf[MAX_EVENT_NAME_LEN];
|
||||
|
||||
if (argc < 2) {
|
||||
pr_info("Probe point is not specified.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* argc must be >= 1 */
|
||||
if (argv[0][0] == 'p')
|
||||
is_return = 0;
|
||||
else if (argv[0][0] == 'r')
|
||||
is_return = 1;
|
||||
else if (argv[0][0] == '-')
|
||||
is_delete = 1;
|
||||
else {
|
||||
pr_info("Probe definition must be started with 'p' or 'r'.\n");
|
||||
pr_info("Probe definition must be started with 'p', 'r' or"
|
||||
" '-'.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -642,7 +641,29 @@ static int create_trace_probe(int argc, char **argv)
|
|||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!group)
|
||||
group = KPROBE_EVENT_SYSTEM;
|
||||
|
||||
if (is_delete) {
|
||||
if (!event) {
|
||||
pr_info("Delete command needs an event name.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
tp = find_probe_event(event, group);
|
||||
if (!tp) {
|
||||
pr_info("Event %s/%s doesn't exist.\n", group, event);
|
||||
return -ENOENT;
|
||||
}
|
||||
/* delete an event */
|
||||
unregister_trace_probe(tp);
|
||||
free_trace_probe(tp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
pr_info("Probe point is not specified.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (isdigit(argv[1][0])) {
|
||||
if (is_return) {
|
||||
pr_info("Return probe point must be a symbol.\n");
|
||||
|
@ -671,8 +692,6 @@ static int create_trace_probe(int argc, char **argv)
|
|||
argc -= 2; argv += 2;
|
||||
|
||||
/* setup a probe */
|
||||
if (!group)
|
||||
group = KPROBE_EVENT_SYSTEM;
|
||||
if (!event) {
|
||||
/* Make a new event name */
|
||||
if (symbol)
|
||||
|
@ -1114,7 +1133,7 @@ static int kprobe_event_define_fields(struct ftrace_event_call *event_call)
|
|||
struct trace_probe *tp = (struct trace_probe *)event_call->data;
|
||||
|
||||
ret = trace_define_common_fields(event_call);
|
||||
if (!ret)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
|
||||
|
@ -1132,7 +1151,7 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
|
|||
struct trace_probe *tp = (struct trace_probe *)event_call->data;
|
||||
|
||||
ret = trace_define_common_fields(event_call);
|
||||
if (!ret)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);
|
||||
|
|
|
@ -79,11 +79,12 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
|
|||
}
|
||||
#endif /* CONFIG_PROFILE_KSYM_TRACER */
|
||||
|
||||
void ksym_hbp_handler(struct perf_event *hbp, void *data)
|
||||
void ksym_hbp_handler(struct perf_event *hbp, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct ksym_trace_entry *entry;
|
||||
struct pt_regs *regs = data;
|
||||
struct ring_buffer *buffer;
|
||||
int pc;
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO);
|
|||
MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
|
||||
" write operations on the kernel symbol");
|
||||
|
||||
static void sample_hbp_handler(struct perf_event *temp, void *data)
|
||||
static void sample_hbp_handler(struct perf_event *bp, int nmi,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
printk(KERN_INFO "%s value is changed\n", ksym_name);
|
||||
dump_stack();
|
||||
|
@ -51,8 +53,9 @@ static void sample_hbp_handler(struct perf_event *temp, void *data)
|
|||
static int __init hw_break_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
DEFINE_BREAKPOINT_ATTR(attr);
|
||||
struct perf_event_attr attr;
|
||||
|
||||
hw_breakpoint_init(&attr);
|
||||
attr.bp_addr = kallsyms_lookup_name(ksym_name);
|
||||
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
||||
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
|
||||
|
|
|
@ -8,16 +8,16 @@ perf-kmem - Tool to trace/measure kernel memory(slab) properties
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf kmem' {record} [<options>]
|
||||
'perf kmem' {record|stat} [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
There's two variants of perf kmem:
|
||||
There are two variants of perf kmem:
|
||||
|
||||
'perf kmem record <command>' to record the kmem events
|
||||
of an arbitrary workload.
|
||||
|
||||
'perf kmem' to report kernel memory statistics.
|
||||
'perf kmem stat' to report kernel memory statistics.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
@ -25,8 +25,11 @@ OPTIONS
|
|||
--input=<file>::
|
||||
Select the input file (default: perf.data)
|
||||
|
||||
--stat=<caller|alloc>::
|
||||
Select per callsite or per allocation statistics
|
||||
--caller::
|
||||
Show per-callsite statistics
|
||||
|
||||
--alloc::
|
||||
Show per-allocation statistics
|
||||
|
||||
-s <key[,key2...]>::
|
||||
--sort=<key[,key2...]>::
|
||||
|
|
|
@ -8,10 +8,13 @@ perf-probe - Define new dynamic tracepoints
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf probe' [options] --add 'PROBE' [--add 'PROBE' ...]
|
||||
'perf probe' [options] --add='PROBE' [...]
|
||||
or
|
||||
'perf probe' [options] 'PROBE' ['PROBE' ...]
|
||||
|
||||
'perf probe' [options] PROBE
|
||||