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:
Linus Torvalds 2009-12-11 20:47:30 -08:00
commit 6f696eb17b
40 changed files with 889 additions and 576 deletions

View file

@ -187,8 +187,8 @@ config HAVE_MMIOTRACE_SUPPORT
def_bool y def_bool y
config X86_DECODER_SELFTEST config X86_DECODER_SELFTEST
bool "x86 instruction decoder selftest" bool "x86 instruction decoder selftest"
depends on DEBUG_KERNEL depends on DEBUG_KERNEL && KPROBES
---help--- ---help---
Perform x86 instruction decoder selftests at build time. Perform x86 instruction decoder selftests at build time.
This option is useful for checking the sanity of x86 instruction This option is useful for checking the sanity of x86 instruction

View file

@ -1632,6 +1632,7 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_events *cpuc)
data.period = event->hw.last_period; data.period = event->hw.last_period;
data.addr = 0; data.addr = 0;
data.raw = NULL;
regs.ip = 0; regs.ip = 0;
/* /*
@ -1749,6 +1750,7 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
u64 val; u64 val;
data.addr = 0; data.addr = 0;
data.raw = NULL;
cpuc = &__get_cpu_var(cpu_hw_events); cpuc = &__get_cpu_var(cpu_hw_events);
@ -1794,6 +1796,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
u64 ack, status; u64 ack, status;
data.addr = 0; data.addr = 0;
data.raw = NULL;
cpuc = &__get_cpu_var(cpu_hw_events); cpuc = &__get_cpu_var(cpu_hw_events);
@ -1857,6 +1860,7 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
u64 val; u64 val;
data.addr = 0; data.addr = 0;
data.raw = NULL;
cpuc = &__get_cpu_var(cpu_hw_events); cpuc = &__get_cpu_var(cpu_hw_events);
@ -2062,12 +2066,6 @@ static __init int p6_pmu_init(void)
x86_pmu = p6_pmu; 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; return 0;
} }
@ -2159,6 +2157,16 @@ static __init int amd_pmu_init(void)
return 0; 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) void __init init_hw_perf_events(void)
{ {
int err; int err;
@ -2180,6 +2188,8 @@ void __init init_hw_perf_events(void)
return; return;
} }
pmu_check_apic();
pr_cont("%s PMU driver.\n", x86_pmu.name); pr_cont("%s PMU driver.\n", x86_pmu.name);
if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) { 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_irq_entry);
static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_nmi_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 static void
@ -2303,8 +2313,9 @@ static void backtrace_warning(void *data, char *msg)
static int backtrace_stack(void *data, char *name) static int backtrace_stack(void *data, char *name)
{ {
per_cpu(in_nmi_frame, smp_processor_id()) = per_cpu(in_ignored_frame, smp_processor_id()) =
x86_is_stack_id(NMI_STACK, name); x86_is_stack_id(NMI_STACK, name) ||
x86_is_stack_id(DEBUG_STACK, name);
return 0; return 0;
} }
@ -2313,7 +2324,7 @@ static void backtrace_address(void *data, unsigned long addr, int reliable)
{ {
struct perf_callchain_entry *entry = data; 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; return;
if (reliable) if (reliable)

View file

@ -103,6 +103,35 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
return NULL; 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: * x86-64 can have up to three kernel stacks:
* process stack * process stack
@ -175,7 +204,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
irq_stack = irq_stack_end - irq_stack = irq_stack_end -
(IRQ_STACK_SIZE - 64) / sizeof(*irq_stack); (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) if (ops->stack(data, "IRQ") < 0)
break; break;
bp = print_context_stack(tinfo, stack, bp, 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: * pointer (index -1 to end) in the IRQ stack:
*/ */
stack = (unsigned long *) (irq_stack_end[-1]); stack = (unsigned long *) (irq_stack_end[-1]);
bp = fixup_bp_irq_link(bp, stack, irq_stack,
irq_stack_end);
irq_stack_end = NULL; irq_stack_end = NULL;
ops->stack(data, "EOI"); ops->stack(data, "EOI");
continue; continue;

View file

@ -1076,10 +1076,10 @@ ENTRY(\sym)
TRACE_IRQS_OFF TRACE_IRQS_OFF
movq %rsp,%rdi /* pt_regs pointer */ movq %rsp,%rdi /* pt_regs pointer */
xorl %esi,%esi /* no error code */ xorl %esi,%esi /* no error code */
PER_CPU(init_tss, %rbp) PER_CPU(init_tss, %r12)
subq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%rbp) subq $EXCEPTION_STKSZ, TSS_ist + (\ist - 1) * 8(%r12)
call \do_sym 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 */ jmp paranoid_exit /* %ebx: no swapgs flag */
CFI_ENDPROC CFI_ENDPROC
END(\sym) END(\sym)

View file

@ -362,8 +362,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
return ret; return ret;
} }
if (bp->callback) ret = arch_store_info(bp);
ret = arch_store_info(bp);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -519,7 +518,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
break; break;
} }
(bp->callback)(bp, args->regs); perf_bp_event(bp, args->regs);
rcu_read_unlock(); rcu_read_unlock();
} }

View file

@ -555,7 +555,9 @@ static int genregs_set(struct task_struct *target,
return ret; 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; int i;
struct thread_struct *thread = &(current->thread); struct thread_struct *thread = &(current->thread);
@ -593,13 +595,13 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
return dr7; return dr7;
} }
static struct perf_event * static int
ptrace_modify_breakpoint(struct perf_event *bp, int len, int type, ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
struct task_struct *tsk, int disabled) struct task_struct *tsk, int disabled)
{ {
int err; int err;
int gen_len, gen_type; int gen_len, gen_type;
DEFINE_BREAKPOINT_ATTR(attr); struct perf_event_attr attr;
/* /*
* We shoud have at least an inactive breakpoint at this * 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 * written the address register first
*/ */
if (!bp) if (!bp)
return ERR_PTR(-EINVAL); return -EINVAL;
err = arch_bp_generic_fields(len, type, &gen_len, &gen_type); err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
if (err) if (err)
return ERR_PTR(err); return err;
attr = bp->attr; attr = bp->attr;
attr.bp_len = gen_len; attr.bp_len = gen_len;
attr.bp_type = gen_type; attr.bp_type = gen_type;
attr.disabled = disabled; 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) if (!second_pass)
continue; continue;
thread->ptrace_bps[i] = NULL; rc = ptrace_modify_breakpoint(bp, len, type,
bp = ptrace_modify_breakpoint(bp, len, type,
tsk, 1); tsk, 1);
if (IS_ERR(bp)) { if (rc)
rc = PTR_ERR(bp);
thread->ptrace_bps[i] = NULL;
break; break;
}
thread->ptrace_bps[i] = bp;
} }
continue; continue;
} }
bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0); rc = ptrace_modify_breakpoint(bp, len, type, tsk, 0);
if (rc)
/* Incorrect bp, or we have a bug in bp API */
if (IS_ERR(bp)) {
rc = PTR_ERR(bp);
thread->ptrace_bps[i] = NULL;
break; break;
}
thread->ptrace_bps[i] = bp;
} }
/* /*
* Make a second pass to free the remaining unused breakpoints * 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 perf_event *bp;
struct thread_struct *t = &tsk->thread; struct thread_struct *t = &tsk->thread;
DEFINE_BREAKPOINT_ATTR(attr); struct perf_event_attr attr;
if (!t->ptrace_bps[nr]) { if (!t->ptrace_bps[nr]) {
hw_breakpoint_init(&attr);
/* /*
* Put stub len and type to register (reserve) an inactive but * Put stub len and type to register (reserve) an inactive but
* correct bp * correct bp
@ -734,26 +726,32 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
attr.disabled = 1; attr.disabled = 1;
bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); 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 { } else {
int err;
bp = t->ptrace_bps[nr]; bp = t->ptrace_bps[nr];
t->ptrace_bps[nr] = NULL;
attr = bp->attr; attr = bp->attr;
attr.bp_addr = addr; 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; return 0;
} }

View file

@ -5,7 +5,7 @@
inat_tables_script = $(srctree)/arch/x86/tools/gen-insn-attr-x86.awk inat_tables_script = $(srctree)/arch/x86/tools/gen-insn-attr-x86.awk
inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt
quiet_cmd_inat_tables = GEN $@ 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) $(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
$(call cmd,inat_tables) $(call cmd,inat_tables)
@ -20,7 +20,7 @@ lib-y := delay.o
lib-y += thunk_$(BITS).o lib-y += thunk_$(BITS).o
lib-y += usercopy_$(BITS).o getuser.o putuser.o lib-y += usercopy_$(BITS).o getuser.o putuser.o
lib-y += memcpy_$(BITS).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 obj-y += msr-reg.o msr-reg-export.o

View file

@ -113,7 +113,7 @@ int main(int argc, char **argv)
char line[BUFSIZE], sym[BUFSIZE] = "<unknown>"; char line[BUFSIZE], sym[BUFSIZE] = "<unknown>";
unsigned char insn_buf[16]; unsigned char insn_buf[16];
struct insn insn; struct insn insn;
int insns = 0, c; int insns = 0;
int warnings = 0; int warnings = 0;
parse_args(argc, argv); parse_args(argc, argv);

View file

@ -20,19 +20,18 @@ enum {
#ifdef CONFIG_HAVE_HW_BREAKPOINT #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) static inline void hw_breakpoint_init(struct perf_event_attr *attr)
{ {
memset(attr, 0, sizeof(*attr));
attr->type = PERF_TYPE_BREAKPOINT; attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(*attr); 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->pinned = 1;
attr->sample_period = 1;
} }
static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 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 * extern struct perf_event *
register_user_hw_breakpoint(struct perf_event_attr *attr, register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered, perf_overflow_handler_t triggered,
struct task_struct *tsk); struct task_struct *tsk);
/* FIXME: only change from the attr, and don't unregister */ /* FIXME: only change from the attr, and don't unregister */
extern struct perf_event * extern int
modify_user_hw_breakpoint(struct perf_event *bp, modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk);
/* /*
* Kernel breakpoints are not associated with any particular thread. * Kernel breakpoints are not associated with any particular thread.
*/ */
extern struct perf_event * extern struct perf_event *
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
perf_callback_t triggered, perf_overflow_handler_t triggered,
int cpu); int cpu);
extern struct perf_event ** extern struct perf_event **
register_wide_hw_breakpoint(struct perf_event_attr *attr, 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);
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 * static inline struct perf_event *
register_user_hw_breakpoint(struct perf_event_attr *attr, register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered, perf_overflow_handler_t triggered,
struct task_struct *tsk) { return NULL; } struct task_struct *tsk) { return NULL; }
static inline struct perf_event * static inline int
modify_user_hw_breakpoint(struct perf_event *bp, modify_user_hw_breakpoint(struct perf_event *bp,
struct perf_event_attr *attr, struct perf_event_attr *attr) { return NULL; }
perf_callback_t triggered,
struct task_struct *tsk) { return NULL; }
static inline struct perf_event * static inline struct perf_event *
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
perf_callback_t triggered, perf_overflow_handler_t triggered,
int cpu) { return NULL; } int cpu) { return NULL; }
static inline struct perf_event ** static inline struct perf_event **
register_wide_hw_breakpoint(struct perf_event_attr *attr, 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 static inline int
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
static inline int static inline int

View file

@ -18,10 +18,6 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#ifdef CONFIG_HAVE_HW_BREAKPOINT
#include <asm/hw_breakpoint.h>
#endif
/* /*
* User-space ABI bits: * User-space ABI bits:
*/ */
@ -215,12 +211,12 @@ struct perf_event_attr {
__u32 wakeup_watermark; /* bytes before wakeup */ __u32 wakeup_watermark; /* bytes before wakeup */
}; };
union { struct { /* Hardware breakpoint info */
struct { /* Hardware breakpoint info */ __u64 bp_addr;
__u64 bp_addr; __u32 bp_type;
__u32 bp_type; __u32 bp_len;
__u32 bp_len; __u64 __bp_reserved_1;
}; __u64 __bp_reserved_2;
}; };
__u32 __reserved_2; __u32 __reserved_2;
@ -451,6 +447,10 @@ enum perf_callchain_context {
# include <asm/perf_event.h> # include <asm/perf_event.h>
#endif #endif
#ifdef CONFIG_HAVE_HW_BREAKPOINT
#include <asm/hw_breakpoint.h>
#endif
#include <linux/list.h> #include <linux/list.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/rculist.h> #include <linux/rculist.h>
@ -565,10 +565,12 @@ struct perf_pending_entry {
void (*func)(struct perf_pending_entry *); void (*func)(struct perf_pending_entry *);
}; };
typedef void (*perf_callback_t)(struct perf_event *, void *);
struct perf_sample_data; 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: * struct perf_event - performance event kernel representation:
*/ */
@ -660,18 +662,12 @@ struct perf_event {
struct pid_namespace *ns; struct pid_namespace *ns;
u64 id; u64 id;
void (*overflow_handler)(struct perf_event *event, perf_overflow_handler_t overflow_handler;
int nmi, struct perf_sample_data *data,
struct pt_regs *regs);
#ifdef CONFIG_EVENT_PROFILE #ifdef CONFIG_EVENT_PROFILE
struct event_filter *filter; struct event_filter *filter;
#endif #endif
perf_callback_t callback;
perf_callback_t event_callback;
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
}; };
@ -781,7 +777,7 @@ extern struct perf_event *
perf_event_create_kernel_counter(struct perf_event_attr *attr, perf_event_create_kernel_counter(struct perf_event_attr *attr,
int cpu, int cpu,
pid_t pid, pid_t pid,
perf_callback_t callback); perf_overflow_handler_t callback);
extern u64 perf_event_read_value(struct perf_event *event, extern u64 perf_event_read_value(struct perf_event *event,
u64 *enabled, u64 *running); u64 *enabled, u64 *running);
@ -876,6 +872,8 @@ extern void perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len); const void *buf, unsigned int len);
extern int perf_swevent_get_recursion_context(void); extern int perf_swevent_get_recursion_context(void);
extern void perf_swevent_put_recursion_context(int rctx); 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 #else
static inline void static inline void
perf_event_task_sched_in(struct task_struct *task, int cpu) { } 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 void perf_event_init(void) { }
static inline int perf_swevent_get_recursion_context(void) { return -1; } 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_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 #endif
#define perf_output_put(handle, x) \ #define perf_output_put(handle, x) \

View file

@ -1840,7 +1840,8 @@ static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask)
extern int sched_clock_stable; extern int sched_clock_stable;
#endif #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 void sched_clock_init(void);
extern u64 sched_clock_cpu(int cpu); extern u64 sched_clock_cpu(int cpu);

View file

@ -52,7 +52,7 @@
static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned);
/* Number of pinned task breakpoints in a cpu */ /* 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 */ /* 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);
@ -73,7 +73,7 @@ static DEFINE_MUTEX(nr_bp_mutex);
static unsigned int max_task_bp_pinned(int cpu) static unsigned int max_task_bp_pinned(int cpu)
{ {
int i; 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--) { for (i = HBP_NUM -1; i >= 0; i--) {
if (tsk_pinned[i] > 0) if (tsk_pinned[i] > 0)
@ -83,50 +83,16 @@ static unsigned int max_task_bp_pinned(int cpu)
return 0; return 0;
} }
/* static int task_bp_pinned(struct task_struct *tsk)
* 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)
{ {
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; struct perf_event_context *ctx = tsk->perf_event_ctxp;
unsigned int *tsk_pinned;
struct list_head *list; struct list_head *list;
struct perf_event *bp;
unsigned long flags; unsigned long flags;
int count = 0;
if (WARN_ONCE(!ctx, "No perf context for this task")) if (WARN_ONCE(!ctx, "No perf context for this task"))
return; return 0;
list = &ctx->event_list; 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); spin_unlock_irqrestore(&ctx->lock, flags);
if (WARN_ONCE(count < 0, "No breakpoint counter found in the counter list")) return count;
return; }
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) { if (enable) {
tsk_pinned[count]++; tsk_pinned[count]++;
if (count > 0) 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: * - If attached to a single cpu, check:
* *
* (per_cpu(nr_bp_flexible, cpu) || (per_cpu(nr_cpu_bp_pinned, cpu) * (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 * -> If there are already non-pinned counters in this cpu, it means
* there is already a free slot for them. * 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: * - If attached to every cpus, check:
* *
* (per_cpu(nr_bp_flexible, *) || (max(per_cpu(nr_cpu_bp_pinned, *)) * (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 * -> 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 * 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: * - If attached to a single cpu, check:
* *
* ((per_cpu(nr_bp_flexible, cpu) > 1) + per_cpu(nr_cpu_bp_pinned, cpu) * ((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 * -> Same checks as before. But now the nr_bp_flexible, if any, must keep
* one register at least (or they will never be fed). * 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: * - If attached to every cpus, check:
* *
* ((per_cpu(nr_bp_flexible, *) > 1) + max(per_cpu(nr_cpu_bp_pinned, *)) * ((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) 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); 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 */ /* Flexible counters need to keep at least one slot */
if (slots.pinned + (!!slots.flexible) == HBP_NUM) { 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; 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 * This is a quick hack that will be removed soon, once we remove
* the tmp breakpoints from ptrace * 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); ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
return ret; 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 * register_user_hw_breakpoint - register a hardware breakpoint for user space
* @attr: breakpoint attributes * @attr: breakpoint attributes
@ -297,7 +306,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
*/ */
struct perf_event * struct perf_event *
register_user_hw_breakpoint(struct perf_event_attr *attr, register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered, perf_overflow_handler_t triggered,
struct task_struct *tsk) struct task_struct *tsk)
{ {
return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered); 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 * @triggered: callback to trigger when we hit the breakpoint
* @tsk: pointer to 'task_struct' of the process to which the address belongs * @tsk: pointer to 'task_struct' of the process to which the address belongs
*/ */
struct perf_event * int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk)
{ {
/* u64 old_addr = bp->attr.bp_addr;
* FIXME: do it without unregistering int old_type = bp->attr.bp_type;
* - We don't want to lose our slot int old_len = bp->attr.bp_len;
* - If the new bp is incorrect, don't lose the older one int err = 0;
*/
unregister_hw_breakpoint(bp);
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); EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
@ -348,7 +378,7 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
*/ */
struct perf_event ** struct perf_event **
register_wide_hw_breakpoint(struct perf_event_attr *attr, 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; struct perf_event **cpu_events, **pevent, *bp;
long err; long err;

View file

@ -36,7 +36,7 @@
/* /*
* Each CPU has a list of per CPU events: * 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; int perf_max_events __read_mostly = 1;
static int perf_reserved_percpu __read_mostly; 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, * 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. * 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 perf_event_context *ctx = event->ctx;
struct task_struct *task = ctx->task; 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 * perf_event_for_each_child or perf_event_for_each as described
* for perf_event_disable. * 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 perf_event_context *ctx = event->ctx;
struct task_struct *task = ctx->task; struct task_struct *task = ctx->task;
@ -1579,7 +1579,6 @@ static void
__perf_event_init_context(struct perf_event_context *ctx, __perf_event_init_context(struct perf_event_context *ctx,
struct task_struct *task) struct task_struct *task)
{ {
memset(ctx, 0, sizeof(*ctx));
spin_lock_init(&ctx->lock); spin_lock_init(&ctx->lock);
mutex_init(&ctx->mutex); mutex_init(&ctx->mutex);
INIT_LIST_HEAD(&ctx->group_list); 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) { if (!ctx) {
ctx = kmalloc(sizeof(struct perf_event_context), GFP_KERNEL); ctx = kzalloc(sizeof(struct perf_event_context), GFP_KERNEL);
err = -ENOMEM; err = -ENOMEM;
if (!ctx) if (!ctx)
goto errout; goto errout;
@ -4011,6 +4010,7 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
event->pmu->read(event); event->pmu->read(event);
data.addr = 0; data.addr = 0;
data.raw = NULL;
data.period = event->hw.last_period; data.period = event->hw.last_period;
regs = get_irq_regs(); regs = get_irq_regs();
/* /*
@ -4080,8 +4080,7 @@ static void cpu_clock_perf_event_update(struct perf_event *event)
u64 now; u64 now;
now = cpu_clock(cpu); now = cpu_clock(cpu);
prev = atomic64_read(&event->hw.prev_count); prev = atomic64_xchg(&event->hw.prev_count, now);
atomic64_set(&event->hw.prev_count, now);
atomic64_add(now - prev, &event->count); 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) static const struct pmu *bp_perf_event_init(struct perf_event *bp)
{ {
int err; int err;
/*
* The breakpoint is already filled if we haven't created the counter err = register_perf_hw_breakpoint(bp);
* 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);
if (err) if (err)
return ERR_PTR(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 perf_sample_data sample;
struct pt_regs *regs = data; struct pt_regs *regs = data;
sample.raw = NULL;
sample.addr = bp->attr.bp_addr; sample.addr = bp->attr.bp_addr;
if (!perf_exclude_event(bp, regs)) 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_context *ctx,
struct perf_event *group_leader, struct perf_event *group_leader,
struct perf_event *parent_event, struct perf_event *parent_event,
perf_callback_t callback, perf_overflow_handler_t overflow_handler,
gfp_t gfpflags) gfp_t gfpflags)
{ {
const struct pmu *pmu; const struct pmu *pmu;
@ -4433,10 +4426,10 @@ perf_event_alloc(struct perf_event_attr *attr,
event->state = PERF_EVENT_STATE_INACTIVE; event->state = PERF_EVENT_STATE_INACTIVE;
if (!callback && parent_event) if (!overflow_handler && parent_event)
callback = parent_event->callback; overflow_handler = parent_event->overflow_handler;
event->callback = callback; event->overflow_handler = overflow_handler;
if (attr->disabled) if (attr->disabled)
event->state = PERF_EVENT_STATE_OFF; event->state = PERF_EVENT_STATE_OFF;
@ -4776,7 +4769,8 @@ err_put_context:
*/ */
struct perf_event * struct perf_event *
perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, 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 *event;
struct perf_event_context *ctx; 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, event = perf_event_alloc(attr, cpu, ctx, NULL,
NULL, callback, GFP_KERNEL); NULL, overflow_handler, GFP_KERNEL);
if (IS_ERR(event)) { if (IS_ERR(event)) {
err = PTR_ERR(event); err = PTR_ERR(event);
goto err_put_context; goto err_put_context;
@ -5090,7 +5084,7 @@ again:
*/ */
int perf_event_init_task(struct task_struct *child) 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_context *cloned_ctx;
struct perf_event *event; struct perf_event *event;
struct task_struct *parent = current; struct task_struct *parent = current;
@ -5105,20 +5099,6 @@ int perf_event_init_task(struct task_struct *child)
if (likely(!parent->perf_event_ctxp)) if (likely(!parent->perf_event_ctxp))
return 0; 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 * If the parent's context is a clone, pin it so it won't get
* swapped under us. * swapped under us.
@ -5149,6 +5129,26 @@ int perf_event_init_task(struct task_struct *child)
continue; 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, ret = inherit_group(event, parent, parent_ctx,
child, child_ctx); child, child_ctx);
if (ret) { if (ret) {
@ -5177,6 +5177,7 @@ int perf_event_init_task(struct task_struct *child)
get_ctx(child_ctx->parent_ctx); get_ctx(child_ctx->parent_ctx);
} }
exit:
mutex_unlock(&parent_ctx->mutex); mutex_unlock(&parent_ctx->mutex);
perf_unpin_context(parent_ctx); perf_unpin_context(parent_ctx);

View file

@ -606,23 +606,22 @@ static int create_trace_probe(int argc, char **argv)
*/ */
struct trace_probe *tp; struct trace_probe *tp;
int i, ret = 0; int i, ret = 0;
int is_return = 0; int is_return = 0, is_delete = 0;
char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL; char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL;
unsigned long offset = 0; unsigned long offset = 0;
void *addr = NULL; void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN]; char buf[MAX_EVENT_NAME_LEN];
if (argc < 2) { /* argc must be >= 1 */
pr_info("Probe point is not specified.\n");
return -EINVAL;
}
if (argv[0][0] == 'p') if (argv[0][0] == 'p')
is_return = 0; is_return = 0;
else if (argv[0][0] == 'r') else if (argv[0][0] == 'r')
is_return = 1; is_return = 1;
else if (argv[0][0] == '-')
is_delete = 1;
else { 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; return -EINVAL;
} }
@ -642,7 +641,29 @@ static int create_trace_probe(int argc, char **argv)
return -EINVAL; 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 (isdigit(argv[1][0])) {
if (is_return) { if (is_return) {
pr_info("Return probe point must be a symbol.\n"); 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; argc -= 2; argv += 2;
/* setup a probe */ /* setup a probe */
if (!group)
group = KPROBE_EVENT_SYSTEM;
if (!event) { if (!event) {
/* Make a new event name */ /* Make a new event name */
if (symbol) 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; struct trace_probe *tp = (struct trace_probe *)event_call->data;
ret = trace_define_common_fields(event_call); ret = trace_define_common_fields(event_call);
if (!ret) if (ret)
return ret; return ret;
DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); 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; struct trace_probe *tp = (struct trace_probe *)event_call->data;
ret = trace_define_common_fields(event_call); ret = trace_define_common_fields(event_call);
if (!ret) if (ret)
return ret; return ret;
DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0); DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);

View file

@ -79,11 +79,12 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
} }
#endif /* CONFIG_PROFILE_KSYM_TRACER */ #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 ring_buffer_event *event;
struct ksym_trace_entry *entry; struct ksym_trace_entry *entry;
struct pt_regs *regs = data;
struct ring_buffer *buffer; struct ring_buffer *buffer;
int pc; int pc;

View file

@ -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" MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
" write operations on the kernel symbol"); " 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); printk(KERN_INFO "%s value is changed\n", ksym_name);
dump_stack(); 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) static int __init hw_break_module_init(void)
{ {
int ret; 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_addr = kallsyms_lookup_name(ksym_name);
attr.bp_len = HW_BREAKPOINT_LEN_4; attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;

View file

@ -8,16 +8,16 @@ perf-kmem - Tool to trace/measure kernel memory(slab) properties
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'perf kmem' {record} [<options>] 'perf kmem' {record|stat} [<options>]
DESCRIPTION DESCRIPTION
----------- -----------
There's two variants of perf kmem: There are two variants of perf kmem:
'perf kmem record <command>' to record the kmem events 'perf kmem record <command>' to record the kmem events
of an arbitrary workload. of an arbitrary workload.
'perf kmem' to report kernel memory statistics. 'perf kmem stat' to report kernel memory statistics.
OPTIONS OPTIONS
------- -------
@ -25,8 +25,11 @@ OPTIONS
--input=<file>:: --input=<file>::
Select the input file (default: perf.data) Select the input file (default: perf.data)
--stat=<caller|alloc>:: --caller::
Select per callsite or per allocation statistics Show per-callsite statistics
--alloc::
Show per-allocation statistics
-s <key[,key2...]>:: -s <key[,key2...]>::
--sort=<key[,key2...]>:: --sort=<key[,key2...]>::

View file

@ -8,10 +8,13 @@ perf-probe - Define new dynamic tracepoints
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'perf probe' [options] --add 'PROBE' [--add 'PROBE' ...] 'perf probe' [options] --add='PROBE' [...]
or or
'perf probe' [options] 'PROBE' ['PROBE' ...] 'perf probe' [options] PROBE
or
'perf probe' [options] --del='[GROUP:]EVENT' [...]
or
'perf probe' --list
DESCRIPTION DESCRIPTION
----------- -----------
@ -31,8 +34,16 @@ OPTIONS
Be more verbose (show parsed arguments, etc). Be more verbose (show parsed arguments, etc).
-a:: -a::
--add:: --add=::
Define a probe point (see PROBE SYNTAX for detail) Define a probe event (see PROBE SYNTAX for detail).
-d::
--del=::
Delete a probe event.
-l::
--list::
List up current probe events.
PROBE SYNTAX PROBE SYNTAX
------------ ------------

View file

@ -19,7 +19,7 @@ static char const *input_name = "perf.data";
static int force; static int force;
static const char *const buildid_list_usage[] = { static const char *const buildid_list_usage[] = {
"perf report [<options>]", "perf buildid-list [<options>]",
NULL NULL
}; };

View file

@ -57,11 +57,6 @@ static struct rb_root root_caller_sorted;
static unsigned long total_requested, total_allocated; static unsigned long total_requested, total_allocated;
static unsigned long nr_allocs, nr_cross_allocs; static unsigned long nr_allocs, nr_cross_allocs;
struct raw_event_sample {
u32 size;
char data[0];
};
#define PATH_SYS_NODE "/sys/devices/system/node" #define PATH_SYS_NODE "/sys/devices/system/node"
static void init_cpunode_map(void) static void init_cpunode_map(void)
@ -201,7 +196,7 @@ static void insert_caller_stat(unsigned long call_site,
} }
} }
static void process_alloc_event(struct raw_event_sample *raw, static void process_alloc_event(void *data,
struct event *event, struct event *event,
int cpu, int cpu,
u64 timestamp __used, u64 timestamp __used,
@ -214,10 +209,10 @@ static void process_alloc_event(struct raw_event_sample *raw,
int bytes_alloc; int bytes_alloc;
int node1, node2; int node1, node2;
ptr = raw_field_value(event, "ptr", raw->data); ptr = raw_field_value(event, "ptr", data);
call_site = raw_field_value(event, "call_site", raw->data); call_site = raw_field_value(event, "call_site", data);
bytes_req = raw_field_value(event, "bytes_req", raw->data); bytes_req = raw_field_value(event, "bytes_req", data);
bytes_alloc = raw_field_value(event, "bytes_alloc", raw->data); bytes_alloc = raw_field_value(event, "bytes_alloc", data);
insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu); insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
insert_caller_stat(call_site, bytes_req, bytes_alloc); insert_caller_stat(call_site, bytes_req, bytes_alloc);
@ -227,7 +222,7 @@ static void process_alloc_event(struct raw_event_sample *raw,
if (node) { if (node) {
node1 = cpunode_map[cpu]; node1 = cpunode_map[cpu];
node2 = raw_field_value(event, "node", raw->data); node2 = raw_field_value(event, "node", data);
if (node1 != node2) if (node1 != node2)
nr_cross_allocs++; nr_cross_allocs++;
} }
@ -262,7 +257,7 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr,
return NULL; return NULL;
} }
static void process_free_event(struct raw_event_sample *raw, static void process_free_event(void *data,
struct event *event, struct event *event,
int cpu, int cpu,
u64 timestamp __used, u64 timestamp __used,
@ -271,7 +266,7 @@ static void process_free_event(struct raw_event_sample *raw,
unsigned long ptr; unsigned long ptr;
struct alloc_stat *s_alloc, *s_caller; struct alloc_stat *s_alloc, *s_caller;
ptr = raw_field_value(event, "ptr", raw->data); ptr = raw_field_value(event, "ptr", data);
s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
if (!s_alloc) if (!s_alloc)
@ -289,66 +284,53 @@ static void process_free_event(struct raw_event_sample *raw,
} }
static void static void
process_raw_event(event_t *raw_event __used, void *more_data, process_raw_event(event_t *raw_event __used, void *data,
int cpu, u64 timestamp, struct thread *thread) int cpu, u64 timestamp, struct thread *thread)
{ {
struct raw_event_sample *raw = more_data;
struct event *event; struct event *event;
int type; int type;
type = trace_parse_common_type(raw->data); type = trace_parse_common_type(data);
event = trace_find_event(type); event = trace_find_event(type);
if (!strcmp(event->name, "kmalloc") || if (!strcmp(event->name, "kmalloc") ||
!strcmp(event->name, "kmem_cache_alloc")) { !strcmp(event->name, "kmem_cache_alloc")) {
process_alloc_event(raw, event, cpu, timestamp, thread, 0); process_alloc_event(data, event, cpu, timestamp, thread, 0);
return; return;
} }
if (!strcmp(event->name, "kmalloc_node") || if (!strcmp(event->name, "kmalloc_node") ||
!strcmp(event->name, "kmem_cache_alloc_node")) { !strcmp(event->name, "kmem_cache_alloc_node")) {
process_alloc_event(raw, event, cpu, timestamp, thread, 1); process_alloc_event(data, event, cpu, timestamp, thread, 1);
return; return;
} }
if (!strcmp(event->name, "kfree") || if (!strcmp(event->name, "kfree") ||
!strcmp(event->name, "kmem_cache_free")) { !strcmp(event->name, "kmem_cache_free")) {
process_free_event(raw, event, cpu, timestamp, thread); process_free_event(data, event, cpu, timestamp, thread);
return; return;
} }
} }
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 timestamp = -1; struct thread *thread;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct thread *thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) { memset(&data, 0, sizeof(data));
timestamp = *(u64 *)more_data; data.time = -1;
more_data += sizeof(u64); data.cpu = -1;
} data.period = 1;
if (sample_type & PERF_SAMPLE_CPU) { event__parse_sample(event, sample_type, &data);
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(event->ip.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -357,7 +339,8 @@ static int process_sample_event(event_t *event)
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
process_raw_event(event, more_data, cpu, timestamp, thread); process_raw_event(event, data.raw_data, data.cpu,
data.time, thread);
return 0; return 0;
} }
@ -543,7 +526,7 @@ static int __cmd_kmem(void)
} }
static const char * const kmem_usage[] = { static const char * const kmem_usage[] = {
"perf kmem [<options>] {record}", "perf kmem [<options>] {record|stat}",
NULL NULL
}; };
@ -703,18 +686,17 @@ static int parse_sort_opt(const struct option *opt __used,
return 0; return 0;
} }
static int parse_stat_opt(const struct option *opt __used, static int parse_caller_opt(const struct option *opt __used,
const char *arg, int unset __used) const char *arg __used, int unset __used)
{ {
if (!arg) caller_flag = (alloc_flag + 1);
return -1; return 0;
}
if (strcmp(arg, "alloc") == 0) static int parse_alloc_opt(const struct option *opt __used,
alloc_flag = (caller_flag + 1); const char *arg __used, int unset __used)
else if (strcmp(arg, "caller") == 0) {
caller_flag = (alloc_flag + 1); alloc_flag = (caller_flag + 1);
else
return -1;
return 0; return 0;
} }
@ -739,14 +721,17 @@ static int parse_line_opt(const struct option *opt __used,
static const struct option kmem_options[] = { static const struct option kmem_options[] = {
OPT_STRING('i', "input", &input_name, "file", OPT_STRING('i', "input", &input_name, "file",
"input file name"), "input file name"),
OPT_CALLBACK(0, "stat", NULL, "<alloc>|<caller>", OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL,
"stat selector, Pass 'alloc' or 'caller'.", "show per-callsite statistics",
parse_stat_opt), parse_caller_opt),
OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
"show per-allocation statistics",
parse_alloc_opt),
OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
"sort by keys: ptr, call_site, bytes, hit, pingpong, frag", "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
parse_sort_opt), parse_sort_opt),
OPT_CALLBACK('l', "line", NULL, "num", OPT_CALLBACK('l', "line", NULL, "num",
"show n lins", "show n lines",
parse_line_opt), parse_line_opt),
OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
OPT_END() OPT_END()
@ -790,18 +775,22 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
if (argc && !strncmp(argv[0], "rec", 3)) if (!argc)
return __cmd_record(argc, argv);
else if (argc)
usage_with_options(kmem_usage, kmem_options); usage_with_options(kmem_usage, kmem_options);
if (list_empty(&caller_sort)) if (!strncmp(argv[0], "rec", 3)) {
setup_sorting(&caller_sort, default_sort_order); return __cmd_record(argc, argv);
if (list_empty(&alloc_sort)) } else if (!strcmp(argv[0], "stat")) {
setup_sorting(&alloc_sort, default_sort_order); setup_cpunode_map();
setup_cpunode_map(); if (list_empty(&caller_sort))
setup_sorting(&caller_sort, default_sort_order);
if (list_empty(&alloc_sort))
setup_sorting(&alloc_sort, default_sort_order);
return __cmd_kmem(); return __cmd_kmem();
}
return 0;
} }

View file

@ -35,6 +35,7 @@
#include "perf.h" #include "perf.h"
#include "builtin.h" #include "builtin.h"
#include "util/util.h" #include "util/util.h"
#include "util/strlist.h"
#include "util/event.h" #include "util/event.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/parse-options.h" #include "util/parse-options.h"
@ -43,11 +44,12 @@
#include "util/probe-event.h" #include "util/probe-event.h"
/* Default vmlinux search paths */ /* Default vmlinux search paths */
#define NR_SEARCH_PATH 3 #define NR_SEARCH_PATH 4
const char *default_search_path[NR_SEARCH_PATH] = { const char *default_search_path[NR_SEARCH_PATH] = {
"/lib/modules/%s/build/vmlinux", /* Custom build kernel */ "/lib/modules/%s/build/vmlinux", /* Custom build kernel */
"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */ "/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
"/boot/vmlinux-debug-%s", /* Ubuntu */ "/boot/vmlinux-debug-%s", /* Ubuntu */
"./vmlinux", /* CWD */
}; };
#define MAX_PATH_LEN 256 #define MAX_PATH_LEN 256
@ -60,6 +62,7 @@ static struct {
int need_dwarf; int need_dwarf;
int nr_probe; int nr_probe;
struct probe_point probes[MAX_PROBES]; struct probe_point probes[MAX_PROBES];
struct strlist *dellist;
} session; } session;
static bool listing; static bool listing;
@ -79,6 +82,25 @@ static void parse_probe_event(const char *str)
pr_debug("%d arguments\n", pp->nr_args); pr_debug("%d arguments\n", pp->nr_args);
} }
static void parse_probe_event_argv(int argc, const char **argv)
{
int i, len;
char *buf;
/* Bind up rest arguments */
len = 0;
for (i = 0; i < argc; i++)
len += strlen(argv[i]) + 1;
buf = zalloc(len + 1);
if (!buf)
die("Failed to allocate memory for binding arguments.");
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
parse_probe_event(buf);
free(buf);
}
static int opt_add_probe_event(const struct option *opt __used, static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used) const char *str, int unset __used)
{ {
@ -87,6 +109,17 @@ static int opt_add_probe_event(const struct option *opt __used,
return 0; return 0;
} }
static int opt_del_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str) {
if (!session.dellist)
session.dellist = strlist__new(true, NULL);
strlist__add(session.dellist, str);
}
return 0;
}
#ifndef NO_LIBDWARF #ifndef NO_LIBDWARF
static int open_default_vmlinux(void) static int open_default_vmlinux(void)
{ {
@ -121,6 +154,7 @@ static int open_default_vmlinux(void)
static const char * const probe_usage[] = { static const char * const probe_usage[] = {
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list", "perf probe --list",
NULL NULL
}; };
@ -132,7 +166,9 @@ static const struct option options[] = {
OPT_STRING('k', "vmlinux", &session.vmlinux, "file", OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
"vmlinux/module pathname"), "vmlinux/module pathname"),
#endif #endif
OPT_BOOLEAN('l', "list", &listing, "list up current probes"), OPT_BOOLEAN('l', "list", &listing, "list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
opt_del_probe_event),
OPT_CALLBACK('a', "add", NULL, OPT_CALLBACK('a', "add", NULL,
#ifdef NO_LIBDWARF #ifdef NO_LIBDWARF
"FUNC[+OFFS|%return] [ARG ...]", "FUNC[+OFFS|%return] [ARG ...]",
@ -160,7 +196,7 @@ static const struct option options[] = {
int cmd_probe(int argc, const char **argv, const char *prefix __used) int cmd_probe(int argc, const char **argv, const char *prefix __used)
{ {
int i, j, ret; int i, ret;
#ifndef NO_LIBDWARF #ifndef NO_LIBDWARF
int fd; int fd;
#endif #endif
@ -168,40 +204,52 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, options, probe_usage, argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
for (i = 0; i < argc; i++) if (argc > 0)
parse_probe_event(argv[i]); parse_probe_event_argv(argc, argv);
if ((session.nr_probe == 0 && !listing) || if ((session.nr_probe == 0 && !session.dellist && !listing))
(session.nr_probe != 0 && listing))
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
if (listing) { if (listing) {
if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --list with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
show_perf_probe_events(); show_perf_probe_events();
return 0; return 0;
} }
if (session.dellist) {
del_trace_kprobe_events(session.dellist);
strlist__delete(session.dellist);
if (session.nr_probe == 0)
return 0;
}
if (session.need_dwarf) if (session.need_dwarf)
#ifdef NO_LIBDWARF #ifdef NO_LIBDWARF
die("Debuginfo-analysis is not supported"); die("Debuginfo-analysis is not supported");
#else /* !NO_LIBDWARF */ #else /* !NO_LIBDWARF */
pr_debug("Some probes require debuginfo.\n"); pr_debug("Some probes require debuginfo.\n");
if (session.vmlinux) if (session.vmlinux) {
pr_debug("Try to open %s.", session.vmlinux);
fd = open(session.vmlinux, O_RDONLY); fd = open(session.vmlinux, O_RDONLY);
else } else
fd = open_default_vmlinux(); fd = open_default_vmlinux();
if (fd < 0) { if (fd < 0) {
if (session.need_dwarf) if (session.need_dwarf)
die("Could not open vmlinux/module file."); die("Could not open debuginfo file.");
pr_warning("Could not open vmlinux/module file." pr_debug("Could not open vmlinux/module file."
" Try to use symbols.\n"); " Try to use symbols.\n");
goto end_dwarf; goto end_dwarf;
} }
/* Searching probe points */ /* Searching probe points */
for (j = 0; j < session.nr_probe; j++) { for (i = 0; i < session.nr_probe; i++) {
pp = &session.probes[j]; pp = &session.probes[i];
if (pp->found) if (pp->found)
continue; continue;
@ -223,8 +271,8 @@ end_dwarf:
#endif /* !NO_LIBDWARF */ #endif /* !NO_LIBDWARF */
/* Synthesize probes without dwarf */ /* Synthesize probes without dwarf */
for (j = 0; j < session.nr_probe; j++) { for (i = 0; i < session.nr_probe; i++) {
pp = &session.probes[j]; pp = &session.probes[i];
if (pp->found) /* This probe is already found. */ if (pp->found) /* This probe is already found. */
continue; continue;

View file

@ -605,44 +605,41 @@ static int validate_chain(struct ip_callchain *chain, event_t *event)
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct ip_callchain *chain = NULL;
int cpumode; int cpumode;
struct addr_location al; struct addr_location al;
struct thread *thread = threads__findnew(event->ip.pid); struct thread *thread;
if (sample_type & PERF_SAMPLE_PERIOD) { memset(&data, 0, sizeof(data));
period = *(u64 *)more_data; data.period = 1;
more_data += sizeof(u64);
} event__parse_sample(event, sample_type, &data);
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
if (sample_type & PERF_SAMPLE_CALLCHAIN) { if (sample_type & PERF_SAMPLE_CALLCHAIN) {
unsigned int i; unsigned int i;
chain = (void *)more_data; dump_printf("... chain: nr:%Lu\n", data.callchain->nr);
dump_printf("... chain: nr:%Lu\n", chain->nr); if (validate_chain(data.callchain, event) < 0) {
if (validate_chain(chain, event) < 0) {
pr_debug("call-chain problem with event, " pr_debug("call-chain problem with event, "
"skipping it.\n"); "skipping it.\n");
return 0; return 0;
} }
if (dump_trace) { if (dump_trace) {
for (i = 0; i < chain->nr; i++) for (i = 0; i < data.callchain->nr; i++)
dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); dump_printf("..... %2d: %016Lx\n",
i, data.callchain->ips[i]);
} }
} }
thread = threads__findnew(data.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -657,7 +654,7 @@ static int process_sample_event(event_t *event)
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
thread__find_addr_location(thread, cpumode, thread__find_addr_location(thread, cpumode,
MAP__FUNCTION, ip, &al, NULL); MAP__FUNCTION, data.ip, &al, NULL);
/* /*
* We have to do this here as we may have a dso with no symbol hit that * We have to do this here as we may have a dso with no symbol hit that
* has a name longer than the ones with symbols sampled. * has a name longer than the ones with symbols sampled.
@ -675,12 +672,12 @@ static int process_sample_event(event_t *event)
if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name)) if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name))
return 0; return 0;
if (hist_entry__add(&al, chain, period)) { if (hist_entry__add(&al, data.callchain, data.period)) {
pr_debug("problem incrementing symbol count, skipping event\n"); pr_debug("problem incrementing symbol count, skipping event\n");
return -1; return -1;
} }
event__stats.total += period; event__stats.total += data.period;
return 0; return 0;
} }

View file

@ -13,7 +13,6 @@
#include "util/debug.h" #include "util/debug.h"
#include "util/data_map.h" #include "util/data_map.h"
#include <sys/types.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <semaphore.h> #include <semaphore.h>
@ -141,6 +140,7 @@ struct work_atoms {
struct thread *thread; struct thread *thread;
struct rb_node node; struct rb_node node;
u64 max_lat; u64 max_lat;
u64 max_lat_at;
u64 total_lat; u64 total_lat;
u64 nb_atoms; u64 nb_atoms;
u64 total_runtime; u64 total_runtime;
@ -414,34 +414,33 @@ static u64 get_cpu_usage_nsec_parent(void)
return sum; return sum;
} }
static u64 get_cpu_usage_nsec_self(void) static int self_open_counters(void)
{ {
char filename [] = "/proc/1234567890/sched"; struct perf_event_attr attr;
unsigned long msecs, nsecs; int fd;
char *line = NULL;
u64 total = 0; memset(&attr, 0, sizeof(attr));
size_t len = 0;
ssize_t chars; attr.type = PERF_TYPE_SOFTWARE;
FILE *file; attr.config = PERF_COUNT_SW_TASK_CLOCK;
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd < 0)
die("Error: sys_perf_event_open() syscall returned"
"with %d (%s)\n", fd, strerror(errno));
return fd;
}
static u64 get_cpu_usage_nsec_self(int fd)
{
u64 runtime;
int ret; int ret;
sprintf(filename, "/proc/%d/sched", getpid()); ret = read(fd, &runtime, sizeof(runtime));
file = fopen(filename, "r"); BUG_ON(ret != sizeof(runtime));
BUG_ON(!file);
while ((chars = getline(&line, &len, file)) != -1) { return runtime;
ret = sscanf(line, "se.sum_exec_runtime : %ld.%06ld\n",
&msecs, &nsecs);
if (ret == 2) {
total = msecs*1e6 + nsecs;
break;
}
}
if (line)
free(line);
fclose(file);
return total;
} }
static void *thread_func(void *ctx) static void *thread_func(void *ctx)
@ -450,9 +449,11 @@ static void *thread_func(void *ctx)
u64 cpu_usage_0, cpu_usage_1; u64 cpu_usage_0, cpu_usage_1;
unsigned long i, ret; unsigned long i, ret;
char comm2[22]; char comm2[22];
int fd;
sprintf(comm2, ":%s", this_task->comm); sprintf(comm2, ":%s", this_task->comm);
prctl(PR_SET_NAME, comm2); prctl(PR_SET_NAME, comm2);
fd = self_open_counters();
again: again:
ret = sem_post(&this_task->ready_for_work); ret = sem_post(&this_task->ready_for_work);
@ -462,16 +463,15 @@ again:
ret = pthread_mutex_unlock(&start_work_mutex); ret = pthread_mutex_unlock(&start_work_mutex);
BUG_ON(ret); BUG_ON(ret);
cpu_usage_0 = get_cpu_usage_nsec_self(); cpu_usage_0 = get_cpu_usage_nsec_self(fd);
for (i = 0; i < this_task->nr_events; i++) { for (i = 0; i < this_task->nr_events; i++) {
this_task->curr_event = i; this_task->curr_event = i;
process_sched_event(this_task, this_task->atoms[i]); process_sched_event(this_task, this_task->atoms[i]);
} }
cpu_usage_1 = get_cpu_usage_nsec_self(); cpu_usage_1 = get_cpu_usage_nsec_self(fd);
this_task->cpu_usage = cpu_usage_1 - cpu_usage_0; this_task->cpu_usage = cpu_usage_1 - cpu_usage_0;
ret = sem_post(&this_task->work_done_sem); ret = sem_post(&this_task->work_done_sem);
BUG_ON(ret); BUG_ON(ret);
@ -628,11 +628,6 @@ static void test_calibrations(void)
printf("the sleep test took %Ld nsecs\n", T1-T0); printf("the sleep test took %Ld nsecs\n", T1-T0);
} }
struct raw_event_sample {
u32 size;
char data[0];
};
#define FILL_FIELD(ptr, field, event, data) \ #define FILL_FIELD(ptr, field, event, data) \
ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data)
@ -1019,8 +1014,10 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
delta = atom->sched_in_time - atom->wake_up_time; delta = atom->sched_in_time - atom->wake_up_time;
atoms->total_lat += delta; atoms->total_lat += delta;
if (delta > atoms->max_lat) if (delta > atoms->max_lat) {
atoms->max_lat = delta; atoms->max_lat = delta;
atoms->max_lat_at = timestamp;
}
atoms->nb_atoms++; atoms->nb_atoms++;
} }
@ -1216,10 +1213,11 @@ static void output_lat_thread(struct work_atoms *work_list)
avg = work_list->total_lat / work_list->nb_atoms; avg = work_list->total_lat / work_list->nb_atoms;
printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms |\n", printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n",
(double)work_list->total_runtime / 1e6, (double)work_list->total_runtime / 1e6,
work_list->nb_atoms, (double)avg / 1e6, work_list->nb_atoms, (double)avg / 1e6,
(double)work_list->max_lat / 1e6); (double)work_list->max_lat / 1e6,
(double)work_list->max_lat_at / 1e9);
} }
static int pid_cmp(struct work_atoms *l, struct work_atoms *r) static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
@ -1356,7 +1354,7 @@ static void sort_lat(void)
static struct trace_sched_handler *trace_handler; static struct trace_sched_handler *trace_handler;
static void static void
process_sched_wakeup_event(struct raw_event_sample *raw, process_sched_wakeup_event(void *data,
struct event *event, struct event *event,
int cpu __used, int cpu __used,
u64 timestamp __used, u64 timestamp __used,
@ -1364,13 +1362,13 @@ process_sched_wakeup_event(struct raw_event_sample *raw,
{ {
struct trace_wakeup_event wakeup_event; struct trace_wakeup_event wakeup_event;
FILL_COMMON_FIELDS(wakeup_event, event, raw->data); FILL_COMMON_FIELDS(wakeup_event, event, data);
FILL_ARRAY(wakeup_event, comm, event, raw->data); FILL_ARRAY(wakeup_event, comm, event, data);
FILL_FIELD(wakeup_event, pid, event, raw->data); FILL_FIELD(wakeup_event, pid, event, data);
FILL_FIELD(wakeup_event, prio, event, raw->data); FILL_FIELD(wakeup_event, prio, event, data);
FILL_FIELD(wakeup_event, success, event, raw->data); FILL_FIELD(wakeup_event, success, event, data);
FILL_FIELD(wakeup_event, cpu, event, raw->data); FILL_FIELD(wakeup_event, cpu, event, data);
if (trace_handler->wakeup_event) if (trace_handler->wakeup_event)
trace_handler->wakeup_event(&wakeup_event, event, cpu, timestamp, thread); trace_handler->wakeup_event(&wakeup_event, event, cpu, timestamp, thread);
@ -1469,7 +1467,7 @@ map_switch_event(struct trace_switch_event *switch_event,
static void static void
process_sched_switch_event(struct raw_event_sample *raw, process_sched_switch_event(void *data,
struct event *event, struct event *event,
int this_cpu, int this_cpu,
u64 timestamp __used, u64 timestamp __used,
@ -1477,15 +1475,15 @@ process_sched_switch_event(struct raw_event_sample *raw,
{ {
struct trace_switch_event switch_event; struct trace_switch_event switch_event;
FILL_COMMON_FIELDS(switch_event, event, raw->data); FILL_COMMON_FIELDS(switch_event, event, data);
FILL_ARRAY(switch_event, prev_comm, event, raw->data); FILL_ARRAY(switch_event, prev_comm, event, data);
FILL_FIELD(switch_event, prev_pid, event, raw->data); FILL_FIELD(switch_event, prev_pid, event, data);
FILL_FIELD(switch_event, prev_prio, event, raw->data); FILL_FIELD(switch_event, prev_prio, event, data);
FILL_FIELD(switch_event, prev_state, event, raw->data); FILL_FIELD(switch_event, prev_state, event, data);
FILL_ARRAY(switch_event, next_comm, event, raw->data); FILL_ARRAY(switch_event, next_comm, event, data);
FILL_FIELD(switch_event, next_pid, event, raw->data); FILL_FIELD(switch_event, next_pid, event, data);
FILL_FIELD(switch_event, next_prio, event, raw->data); FILL_FIELD(switch_event, next_prio, event, data);
if (curr_pid[this_cpu] != (u32)-1) { if (curr_pid[this_cpu] != (u32)-1) {
/* /*
@ -1502,7 +1500,7 @@ process_sched_switch_event(struct raw_event_sample *raw,
} }
static void static void
process_sched_runtime_event(struct raw_event_sample *raw, process_sched_runtime_event(void *data,
struct event *event, struct event *event,
int cpu __used, int cpu __used,
u64 timestamp __used, u64 timestamp __used,
@ -1510,17 +1508,17 @@ process_sched_runtime_event(struct raw_event_sample *raw,
{ {
struct trace_runtime_event runtime_event; struct trace_runtime_event runtime_event;
FILL_ARRAY(runtime_event, comm, event, raw->data); FILL_ARRAY(runtime_event, comm, event, data);
FILL_FIELD(runtime_event, pid, event, raw->data); FILL_FIELD(runtime_event, pid, event, data);
FILL_FIELD(runtime_event, runtime, event, raw->data); FILL_FIELD(runtime_event, runtime, event, data);
FILL_FIELD(runtime_event, vruntime, event, raw->data); FILL_FIELD(runtime_event, vruntime, event, data);
if (trace_handler->runtime_event) if (trace_handler->runtime_event)
trace_handler->runtime_event(&runtime_event, event, cpu, timestamp, thread); trace_handler->runtime_event(&runtime_event, event, cpu, timestamp, thread);
} }
static void static void
process_sched_fork_event(struct raw_event_sample *raw, process_sched_fork_event(void *data,
struct event *event, struct event *event,
int cpu __used, int cpu __used,
u64 timestamp __used, u64 timestamp __used,
@ -1528,12 +1526,12 @@ process_sched_fork_event(struct raw_event_sample *raw,
{ {
struct trace_fork_event fork_event; struct trace_fork_event fork_event;
FILL_COMMON_FIELDS(fork_event, event, raw->data); FILL_COMMON_FIELDS(fork_event, event, data);
FILL_ARRAY(fork_event, parent_comm, event, raw->data); FILL_ARRAY(fork_event, parent_comm, event, data);
FILL_FIELD(fork_event, parent_pid, event, raw->data); FILL_FIELD(fork_event, parent_pid, event, data);
FILL_ARRAY(fork_event, child_comm, event, raw->data); FILL_ARRAY(fork_event, child_comm, event, data);
FILL_FIELD(fork_event, child_pid, event, raw->data); FILL_FIELD(fork_event, child_pid, event, data);
if (trace_handler->fork_event) if (trace_handler->fork_event)
trace_handler->fork_event(&fork_event, event, cpu, timestamp, thread); trace_handler->fork_event(&fork_event, event, cpu, timestamp, thread);
@ -1550,7 +1548,7 @@ process_sched_exit_event(struct event *event,
} }
static void static void
process_sched_migrate_task_event(struct raw_event_sample *raw, process_sched_migrate_task_event(void *data,
struct event *event, struct event *event,
int cpu __used, int cpu __used,
u64 timestamp __used, u64 timestamp __used,
@ -1558,80 +1556,66 @@ process_sched_migrate_task_event(struct raw_event_sample *raw,
{ {
struct trace_migrate_task_event migrate_task_event; struct trace_migrate_task_event migrate_task_event;
FILL_COMMON_FIELDS(migrate_task_event, event, raw->data); FILL_COMMON_FIELDS(migrate_task_event, event, data);
FILL_ARRAY(migrate_task_event, comm, event, raw->data); FILL_ARRAY(migrate_task_event, comm, event, data);
FILL_FIELD(migrate_task_event, pid, event, raw->data); FILL_FIELD(migrate_task_event, pid, event, data);
FILL_FIELD(migrate_task_event, prio, event, raw->data); FILL_FIELD(migrate_task_event, prio, event, data);
FILL_FIELD(migrate_task_event, cpu, event, raw->data); FILL_FIELD(migrate_task_event, cpu, event, data);
if (trace_handler->migrate_task_event) if (trace_handler->migrate_task_event)
trace_handler->migrate_task_event(&migrate_task_event, event, cpu, timestamp, thread); trace_handler->migrate_task_event(&migrate_task_event, event, cpu, timestamp, thread);
} }
static void static void
process_raw_event(event_t *raw_event __used, void *more_data, process_raw_event(event_t *raw_event __used, void *data,
int cpu, u64 timestamp, struct thread *thread) int cpu, u64 timestamp, struct thread *thread)
{ {
struct raw_event_sample *raw = more_data;
struct event *event; struct event *event;
int type; int type;
type = trace_parse_common_type(raw->data);
type = trace_parse_common_type(data);
event = trace_find_event(type); event = trace_find_event(type);
if (!strcmp(event->name, "sched_switch")) if (!strcmp(event->name, "sched_switch"))
process_sched_switch_event(raw, event, cpu, timestamp, thread); process_sched_switch_event(data, event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_stat_runtime")) if (!strcmp(event->name, "sched_stat_runtime"))
process_sched_runtime_event(raw, event, cpu, timestamp, thread); process_sched_runtime_event(data, event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_wakeup")) if (!strcmp(event->name, "sched_wakeup"))
process_sched_wakeup_event(raw, event, cpu, timestamp, thread); process_sched_wakeup_event(data, event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_wakeup_new")) if (!strcmp(event->name, "sched_wakeup_new"))
process_sched_wakeup_event(raw, event, cpu, timestamp, thread); process_sched_wakeup_event(data, event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_process_fork")) if (!strcmp(event->name, "sched_process_fork"))
process_sched_fork_event(raw, event, cpu, timestamp, thread); process_sched_fork_event(data, event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_process_exit")) if (!strcmp(event->name, "sched_process_exit"))
process_sched_exit_event(event, cpu, timestamp, thread); process_sched_exit_event(event, cpu, timestamp, thread);
if (!strcmp(event->name, "sched_migrate_task")) if (!strcmp(event->name, "sched_migrate_task"))
process_sched_migrate_task_event(raw, event, cpu, timestamp, thread); process_sched_migrate_task_event(data, event, cpu, timestamp, thread);
} }
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
struct sample_data data;
struct thread *thread; struct thread *thread;
u64 ip = event->ip.ip;
u64 timestamp = -1;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
if (!(sample_type & PERF_SAMPLE_RAW)) if (!(sample_type & PERF_SAMPLE_RAW))
return 0; return 0;
thread = threads__findnew(event->ip.pid); memset(&data, 0, sizeof(data));
data.time = -1;
data.cpu = -1;
data.period = -1;
if (sample_type & PERF_SAMPLE_TIME) { event__parse_sample(event, sample_type, &data);
timestamp = *(u64 *)more_data;
more_data += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_CPU) {
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(data.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
@ -1640,10 +1624,10 @@ static int process_sample_event(event_t *event)
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (profile_cpu != -1 && profile_cpu != (int) cpu) if (profile_cpu != -1 && profile_cpu != (int)data.cpu)
return 0; return 0;
process_raw_event(event, more_data, cpu, timestamp, thread); process_raw_event(event, data.raw_data, data.cpu, data.time, thread);
return 0; return 0;
} }
@ -1724,9 +1708,9 @@ static void __cmd_lat(void)
read_events(); read_events();
sort_lat(); sort_lat();
printf("\n -----------------------------------------------------------------------------------------\n"); printf("\n ---------------------------------------------------------------------------------------------------------------\n");
printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms |\n"); printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n");
printf(" -----------------------------------------------------------------------------------------\n"); printf(" ---------------------------------------------------------------------------------------------------------------\n");
next = rb_first(&sorted_atom_root); next = rb_first(&sorted_atom_root);
@ -1902,13 +1886,18 @@ static int __cmd_record(int argc, const char **argv)
int cmd_sched(int argc, const char **argv, const char *prefix __used) int cmd_sched(int argc, const char **argv, const char *prefix __used)
{ {
symbol__init(0);
argc = parse_options(argc, argv, sched_options, sched_usage, argc = parse_options(argc, argv, sched_options, sched_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc) if (!argc)
usage_with_options(sched_usage, sched_options); usage_with_options(sched_usage, sched_options);
/*
* Aliased to 'perf trace' for now:
*/
if (!strcmp(argv[0], "trace"))
return cmd_trace(argc, argv, prefix);
symbol__init(0);
if (!strncmp(argv[0], "rec", 3)) { if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv); return __cmd_record(argc, argv);
} else if (!strncmp(argv[0], "lat", 3)) { } else if (!strncmp(argv[0], "lat", 3)) {
@ -1932,11 +1921,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used)
usage_with_options(replay_usage, replay_options); usage_with_options(replay_usage, replay_options);
} }
__cmd_replay(); __cmd_replay();
} else if (!strcmp(argv[0], "trace")) {
/*
* Aliased to 'perf trace' for now:
*/
return cmd_trace(argc, argv, prefix);
} else { } else {
usage_with_options(sched_usage, sched_options); usage_with_options(sched_usage, sched_options);
} }

View file

@ -302,12 +302,11 @@ process_exit_event(event_t *event)
} }
struct trace_entry { struct trace_entry {
u32 size;
unsigned short type; unsigned short type;
unsigned char flags; unsigned char flags;
unsigned char preempt_count; unsigned char preempt_count;
int pid; int pid;
int tgid; int lock_depth;
}; };
struct power_entry { struct power_entry {
@ -484,43 +483,22 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
static int static int
process_sample_event(event_t *event) process_sample_event(event_t *event)
{ {
int cursor = 0; struct sample_data data;
u64 addr = 0;
u64 stamp = 0;
u32 cpu = 0;
u32 pid = 0;
struct trace_entry *te; struct trace_entry *te;
if (sample_type & PERF_SAMPLE_IP) memset(&data, 0, sizeof(data));
cursor++;
event__parse_sample(event, sample_type, &data);
if (sample_type & PERF_SAMPLE_TID) {
pid = event->sample.array[cursor]>>32;
cursor++;
}
if (sample_type & PERF_SAMPLE_TIME) { if (sample_type & PERF_SAMPLE_TIME) {
stamp = event->sample.array[cursor++]; if (!first_time || first_time > data.time)
first_time = data.time;
if (!first_time || first_time > stamp) if (last_time < data.time)
first_time = stamp; last_time = data.time;
if (last_time < stamp)
last_time = stamp;
} }
if (sample_type & PERF_SAMPLE_ADDR)
addr = event->sample.array[cursor++];
if (sample_type & PERF_SAMPLE_ID)
cursor++;
if (sample_type & PERF_SAMPLE_STREAM_ID)
cursor++;
if (sample_type & PERF_SAMPLE_CPU)
cpu = event->sample.array[cursor++] & 0xFFFFFFFF;
if (sample_type & PERF_SAMPLE_PERIOD)
cursor++;
te = (void *)&event->sample.array[cursor]; te = (void *)data.raw_data;
if (sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) {
if (sample_type & PERF_SAMPLE_RAW && te->size > 0) {
char *event_str; char *event_str;
struct power_entry *pe; struct power_entry *pe;
@ -532,19 +510,19 @@ process_sample_event(event_t *event)
return 0; return 0;
if (strcmp(event_str, "power:power_start") == 0) if (strcmp(event_str, "power:power_start") == 0)
c_state_start(cpu, stamp, pe->value); c_state_start(data.cpu, data.time, pe->value);
if (strcmp(event_str, "power:power_end") == 0) if (strcmp(event_str, "power:power_end") == 0)
c_state_end(cpu, stamp); c_state_end(data.cpu, data.time);
if (strcmp(event_str, "power:power_frequency") == 0) if (strcmp(event_str, "power:power_frequency") == 0)
p_state_change(cpu, stamp, pe->value); p_state_change(data.cpu, data.time, pe->value);
if (strcmp(event_str, "sched:sched_wakeup") == 0) if (strcmp(event_str, "sched:sched_wakeup") == 0)
sched_wakeup(cpu, stamp, pid, te); sched_wakeup(data.cpu, data.time, data.pid, te);
if (strcmp(event_str, "sched:sched_switch") == 0) if (strcmp(event_str, "sched:sched_switch") == 0)
sched_switch(cpu, stamp, te); sched_switch(data.cpu, data.time, te);
} }
return 0; return 0;
} }

View file

@ -66,58 +66,40 @@ static u64 sample_type;
static int process_sample_event(event_t *event) static int process_sample_event(event_t *event)
{ {
u64 ip = event->ip.ip; struct sample_data data;
u64 timestamp = -1; struct thread *thread;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
struct thread *thread = threads__findnew(event->ip.pid);
if (sample_type & PERF_SAMPLE_TIME) { memset(&data, 0, sizeof(data));
timestamp = *(u64 *)more_data; data.time = -1;
more_data += sizeof(u64); data.cpu = -1;
} data.period = 1;
if (sample_type & PERF_SAMPLE_CPU) { event__parse_sample(event, sample_type, &data);
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
event->ip.pid, event->ip.tid, data.pid, data.tid,
(void *)(long)ip, (void *)(long)data.ip,
(long long)period); (long long)data.period);
thread = threads__findnew(event->ip.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
return -1; return -1;
} }
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (sample_type & PERF_SAMPLE_RAW) { if (sample_type & PERF_SAMPLE_RAW) {
struct {
u32 size;
char data[0];
} *raw = more_data;
/* /*
* FIXME: better resolve from pid from the struct trace_entry * FIXME: better resolve from pid from the struct trace_entry
* field, although it should be the same than this perf * field, although it should be the same than this perf
* event pid * event pid
*/ */
scripting_ops->process_event(cpu, raw->data, raw->size, scripting_ops->process_event(data.cpu, data.raw_data,
timestamp, thread->comm); data.raw_size,
data.time, thread->comm);
} }
event__stats.total += period; event__stats.total += data.period;
return 0; return 0;
} }

View file

@ -100,11 +100,11 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
} }
} }
int perf_header__read_build_ids(int input, off_t offset, off_t size) int perf_header__read_build_ids(int input, u64 offset, u64 size)
{ {
struct build_id_event bev; struct build_id_event bev;
char filename[PATH_MAX]; char filename[PATH_MAX];
off_t limit = offset + size; u64 limit = offset + size;
int err = -1; int err = -1;
while (offset < limit) { while (offset < limit) {

View file

@ -27,6 +27,6 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
int full_paths, int full_paths,
int *cwdlen, int *cwdlen,
char **cwd); char **cwd);
int perf_header__read_build_ids(int input, off_t offset, off_t file_size); int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
#endif #endif

View file

@ -310,3 +310,70 @@ int event__preprocess_sample(const event_t *self, struct addr_location *al,
al->level == 'H' ? "[hypervisor]" : "<not found>"); al->level == 'H' ? "[hypervisor]" : "<not found>");
return 0; return 0;
} }
int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
{
u64 *array = event->sample.array;
if (type & PERF_SAMPLE_IP) {
data->ip = event->ip.ip;
array++;
}
if (type & PERF_SAMPLE_TID) {
u32 *p = (u32 *)array;
data->pid = p[0];
data->tid = p[1];
array++;
}
if (type & PERF_SAMPLE_TIME) {
data->time = *array;
array++;
}
if (type & PERF_SAMPLE_ADDR) {
data->addr = *array;
array++;
}
if (type & PERF_SAMPLE_ID) {
data->id = *array;
array++;
}
if (type & PERF_SAMPLE_STREAM_ID) {
data->stream_id = *array;
array++;
}
if (type & PERF_SAMPLE_CPU) {
u32 *p = (u32 *)array;
data->cpu = *p;
array++;
}
if (type & PERF_SAMPLE_PERIOD) {
data->period = *array;
array++;
}
if (type & PERF_SAMPLE_READ) {
pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
return -1;
}
if (type & PERF_SAMPLE_CALLCHAIN) {
data->callchain = (struct ip_callchain *)array;
array += 1 + data->callchain->nr;
}
if (type & PERF_SAMPLE_RAW) {
u32 *p = (u32 *)array;
data->raw_size = *p;
p++;
data->raw_data = p;
}
return 0;
}

View file

@ -56,11 +56,25 @@ struct read_event {
u64 id; u64 id;
}; };
struct sample_event{ struct sample_event {
struct perf_event_header header; struct perf_event_header header;
u64 array[]; u64 array[];
}; };
struct sample_data {
u64 ip;
u32 pid, tid;
u64 time;
u64 addr;
u64 id;
u64 stream_id;
u32 cpu;
u64 period;
struct ip_callchain *callchain;
u32 raw_size;
void *raw_data;
};
#define BUILD_ID_SIZE 20 #define BUILD_ID_SIZE 20
struct build_id_event { struct build_id_event {
@ -155,5 +169,6 @@ int event__process_task(event_t *self);
struct addr_location; struct addr_location;
int event__preprocess_sample(const event_t *self, struct addr_location *al, int event__preprocess_sample(const event_t *self, struct addr_location *al,
symbol_filter_t filter); symbol_filter_t filter);
int event__parse_sample(event_t *event, u64 type, struct sample_data *data);
#endif /* __PERF_RECORD_H */ #endif /* __PERF_RECORD_H */

View file

@ -187,7 +187,9 @@ static int do_write(int fd, const void *buf, size_t size)
static int __dsos__write_buildid_table(struct list_head *head, int fd) static int __dsos__write_buildid_table(struct list_head *head, int fd)
{ {
#define NAME_ALIGN 64
struct dso *pos; struct dso *pos;
static const char zero_buf[NAME_ALIGN];
list_for_each_entry(pos, head, node) { list_for_each_entry(pos, head, node) {
int err; int err;
@ -197,14 +199,17 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd)
if (!pos->has_build_id) if (!pos->has_build_id)
continue; continue;
len = pos->long_name_len + 1; len = pos->long_name_len + 1;
len = ALIGN(len, 64); len = ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b)); memset(&b, 0, sizeof(b));
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
b.header.size = sizeof(b) + len; b.header.size = sizeof(b) + len;
err = do_write(fd, &b, sizeof(b)); err = do_write(fd, &b, sizeof(b));
if (err < 0) if (err < 0)
return err; return err;
err = do_write(fd, pos->long_name, len); err = do_write(fd, pos->long_name, pos->long_name_len + 1);
if (err < 0)
return err;
err = do_write(fd, zero_buf, len - pos->long_name_len - 1);
if (err < 0) if (err < 0)
return err; return err;
} }

View file

@ -197,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
if (id == config) { if (id == config) {
closedir(evt_dir); closedir(evt_dir);
closedir(sys_dir); closedir(sys_dir);
path = zalloc(sizeof(path)); path = zalloc(sizeof(*path));
path->system = malloc(MAX_EVENT_LENGTH); path->system = malloc(MAX_EVENT_LENGTH);
if (!path->system) { if (!path->system) {
free(path); free(path);
@ -467,7 +467,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
while ((evt_ent = readdir(evt_dir))) { while ((evt_ent = readdir(evt_dir))) {
char event_opt[MAX_EVOPT_LEN + 1]; char event_opt[MAX_EVOPT_LEN + 1];
int len; int len;
unsigned int rem = MAX_EVOPT_LEN;
if (!strcmp(evt_ent->d_name, ".") if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..") || !strcmp(evt_ent->d_name, "..")
@ -475,20 +474,12 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
|| !strcmp(evt_ent->d_name, "filter")) || !strcmp(evt_ent->d_name, "filter"))
continue; continue;
len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
evt_ent->d_name); evt_ent->d_name, flags ? ":" : "",
flags ?: "");
if (len < 0) if (len < 0)
return EVT_FAILED; return EVT_FAILED;
rem -= len;
if (flags) {
if (rem < strlen(flags) + 1)
return EVT_FAILED;
strcat(event_opt, ":");
strcat(event_opt, flags);
}
if (parse_events(NULL, event_opt, 0)) if (parse_events(NULL, event_opt, 0))
return EVT_FAILED; return EVT_FAILED;
} }

View file

@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr,
pos = fprintf(stderr, " "); pos = fprintf(stderr, " ");
if (opts->short_name) if (opts->short_name)
pos += fprintf(stderr, "-%c", opts->short_name); pos += fprintf(stderr, "-%c", opts->short_name);
else
pos += fprintf(stderr, " ");
if (opts->long_name && opts->short_name) if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", "); pos += fprintf(stderr, ", ");
if (opts->long_name) if (opts->long_name)

View file

@ -47,6 +47,9 @@
#define semantic_error(msg ...) die("Semantic error :" msg) #define semantic_error(msg ...) die("Semantic error :" msg)
/* If there is no space to write, returns -E2BIG. */ /* If there is no space to write, returns -E2BIG. */
static int e_snprintf(char *str, size_t size, const char *format, ...)
__attribute__((format(printf, 3, 4)));
static int e_snprintf(char *str, size_t size, const char *format, ...) static int e_snprintf(char *str, size_t size, const char *format, ...)
{ {
int ret; int ret;
@ -258,7 +261,7 @@ int synthesize_perf_probe_event(struct probe_point *pp)
ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function,
offs, pp->retprobe ? "%return" : "", line); offs, pp->retprobe ? "%return" : "", line);
else else
ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line);
if (ret <= 0) if (ret <= 0)
goto error; goto error;
len = ret; len = ret;
@ -373,14 +376,32 @@ static void clear_probe_point(struct probe_point *pp)
free(pp->args); free(pp->args);
for (i = 0; i < pp->found; i++) for (i = 0; i < pp->found; i++)
free(pp->probes[i]); free(pp->probes[i]);
memset(pp, 0, sizeof(pp)); memset(pp, 0, sizeof(*pp));
}
/* Show an event */
static void show_perf_probe_event(const char *group, const char *event,
const char *place, struct probe_point *pp)
{
int i;
char buf[128];
e_snprintf(buf, 128, "%s:%s", group, event);
printf(" %-40s (on %s", buf, place);
if (pp->nr_args > 0) {
printf(" with");
for (i = 0; i < pp->nr_args; i++)
printf(" %s", pp->args[i]);
}
printf(")\n");
} }
/* List up current perf-probe events */ /* List up current perf-probe events */
void show_perf_probe_events(void) void show_perf_probe_events(void)
{ {
unsigned int i; unsigned int i;
int fd; int fd, nr;
char *group, *event; char *group, *event;
struct probe_point pp; struct probe_point pp;
struct strlist *rawlist; struct strlist *rawlist;
@ -393,8 +414,13 @@ void show_perf_probe_events(void)
for (i = 0; i < strlist__nr_entries(rawlist); i++) { for (i = 0; i < strlist__nr_entries(rawlist); i++) {
ent = strlist__entry(rawlist, i); ent = strlist__entry(rawlist, i);
parse_trace_kprobe_event(ent->s, &group, &event, &pp); parse_trace_kprobe_event(ent->s, &group, &event, &pp);
/* Synthesize only event probe point */
nr = pp.nr_args;
pp.nr_args = 0;
synthesize_perf_probe_event(&pp); synthesize_perf_probe_event(&pp);
printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); pp.nr_args = nr;
/* Show an event */
show_perf_probe_event(group, event, pp.probes[0], &pp);
free(group); free(group);
free(event); free(event);
clear_probe_point(&pp); clear_probe_point(&pp);
@ -404,21 +430,28 @@ void show_perf_probe_events(void)
} }
/* Get current perf-probe event names */ /* Get current perf-probe event names */
static struct strlist *get_perf_event_names(int fd) static struct strlist *get_perf_event_names(int fd, bool include_group)
{ {
unsigned int i; unsigned int i;
char *group, *event; char *group, *event;
char buf[128];
struct strlist *sl, *rawlist; struct strlist *sl, *rawlist;
struct str_node *ent; struct str_node *ent;
rawlist = get_trace_kprobe_event_rawlist(fd); rawlist = get_trace_kprobe_event_rawlist(fd);
sl = strlist__new(false, NULL); sl = strlist__new(true, NULL);
for (i = 0; i < strlist__nr_entries(rawlist); i++) { for (i = 0; i < strlist__nr_entries(rawlist); i++) {
ent = strlist__entry(rawlist, i); ent = strlist__entry(rawlist, i);
parse_trace_kprobe_event(ent->s, &group, &event, NULL); parse_trace_kprobe_event(ent->s, &group, &event, NULL);
strlist__add(sl, event); if (include_group) {
if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
die("Failed to copy group:event name.");
strlist__add(sl, buf);
} else
strlist__add(sl, event);
free(group); free(group);
free(event);
} }
strlist__delete(rawlist); strlist__delete(rawlist);
@ -426,24 +459,30 @@ static struct strlist *get_perf_event_names(int fd)
return sl; return sl;
} }
static int write_trace_kprobe_event(int fd, const char *buf) static void write_trace_kprobe_event(int fd, const char *buf)
{ {
int ret; int ret;
pr_debug("Writing event: %s\n", buf);
ret = write(fd, buf, strlen(buf)); ret = write(fd, buf, strlen(buf));
if (ret <= 0) if (ret <= 0)
die("Failed to create event."); die("Failed to write event: %s", strerror(errno));
else
printf("Added new event: %s\n", buf);
return ret;
} }
static void get_new_event_name(char *buf, size_t len, const char *base, static void get_new_event_name(char *buf, size_t len, const char *base,
struct strlist *namelist) struct strlist *namelist)
{ {
int i, ret; int i, ret;
for (i = 0; i < MAX_EVENT_INDEX; i++) {
/* Try no suffix */
ret = e_snprintf(buf, len, "%s", base);
if (ret < 0)
die("snprintf() failed: %s", strerror(-ret));
if (!strlist__has_entry(namelist, buf))
return;
/* Try to add suffix */
for (i = 1; i < MAX_EVENT_INDEX; i++) {
ret = e_snprintf(buf, len, "%s_%d", base, i); ret = e_snprintf(buf, len, "%s_%d", base, i);
if (ret < 0) if (ret < 0)
die("snprintf() failed: %s", strerror(-ret)); die("snprintf() failed: %s", strerror(-ret));
@ -464,7 +503,7 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
fd = open_kprobe_events(O_RDWR, O_APPEND); fd = open_kprobe_events(O_RDWR, O_APPEND);
/* Get current event names */ /* Get current event names */
namelist = get_perf_event_names(fd); namelist = get_perf_event_names(fd, false);
for (j = 0; j < nr_probes; j++) { for (j = 0; j < nr_probes; j++) {
pp = probes + j; pp = probes + j;
@ -476,9 +515,73 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
PERFPROBE_GROUP, event, PERFPROBE_GROUP, event,
pp->probes[i]); pp->probes[i]);
write_trace_kprobe_event(fd, buf); write_trace_kprobe_event(fd, buf);
printf("Added new event:\n");
/* Get the first parameter (probe-point) */
sscanf(pp->probes[i], "%s", buf);
show_perf_probe_event(PERFPROBE_GROUP, event,
buf, pp);
/* Add added event name to namelist */ /* Add added event name to namelist */
strlist__add(namelist, event); strlist__add(namelist, event);
} }
} }
/* Show how to use the event. */
printf("\nYou can now use it on all perf tools, such as:\n\n");
printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event);
strlist__delete(namelist);
close(fd); close(fd);
} }
static void del_trace_kprobe_event(int fd, const char *group,
const char *event, struct strlist *namelist)
{
char buf[128];
if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
die("Failed to copy event.");
if (!strlist__has_entry(namelist, buf)) {
pr_warning("Warning: event \"%s\" is not found.\n", buf);
return;
}
/* Convert from perf-probe event to trace-kprobe event */
if (e_snprintf(buf, 128, "-:%s/%s", group, event) < 0)
die("Failed to copy event.");
write_trace_kprobe_event(fd, buf);
printf("Remove event: %s:%s\n", group, event);
}
void del_trace_kprobe_events(struct strlist *dellist)
{
int fd;
unsigned int i;
const char *group, *event;
char *p, *str;
struct str_node *ent;
struct strlist *namelist;
fd = open_kprobe_events(O_RDWR, O_APPEND);
/* Get current event names */
namelist = get_perf_event_names(fd, true);
for (i = 0; i < strlist__nr_entries(dellist); i++) {
ent = strlist__entry(dellist, i);
str = strdup(ent->s);
if (!str)
die("Failed to copy event.");
p = strchr(str, ':');
if (p) {
group = str;
*p = '\0';
event = p + 1;
} else {
group = PERFPROBE_GROUP;
event = str;
}
del_trace_kprobe_event(fd, group, event, namelist);
free(str);
}
strlist__delete(namelist);
close(fd);
}

View file

@ -10,6 +10,7 @@ extern void parse_trace_kprobe_event(const char *str, char **group,
char **event, struct probe_point *pp); char **event, struct probe_point *pp);
extern int synthesize_trace_kprobe_event(struct probe_point *pp); extern int synthesize_trace_kprobe_event(struct probe_point *pp);
extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes);
extern void del_trace_kprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void); extern void show_perf_probe_events(void);
/* Maximum index number of event-name postfix */ /* Maximum index number of event-name postfix */

View file

@ -106,7 +106,7 @@ static int strtailcmp(const char *s1, const char *s2)
{ {
int i1 = strlen(s1); int i1 = strlen(s1);
int i2 = strlen(s2); int i2 = strlen(s2);
while (--i1 > 0 && --i2 > 0) { while (--i1 >= 0 && --i2 >= 0) {
if (s1[i1] != s2[i2]) if (s1[i1] != s2[i2])
return s1[i1] - s2[i2]; return s1[i1] - s2[i2];
} }

View file

@ -938,8 +938,9 @@ static bool __dsos__read_build_ids(struct list_head *head)
bool dsos__read_build_ids(void) bool dsos__read_build_ids(void)
{ {
return __dsos__read_build_ids(&dsos__kernel) || bool kbuildids = __dsos__read_build_ids(&dsos__kernel),
__dsos__read_build_ids(&dsos__user); ubuildids = __dsos__read_build_ids(&dsos__user);
return kbuildids || ubuildids;
} }
/* /*

View file

@ -177,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused)
func_count++; func_count++;
} }
func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
i = 0; i = 0;
while (list) { while (list) {
@ -1477,7 +1477,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
goto out_free; goto out_free;
field = malloc_or_die(sizeof(*field)); field = malloc_or_die(sizeof(*field));
memset(field, 0, sizeof(field)); memset(field, 0, sizeof(*field));
value = arg_eval(arg); value = arg_eval(arg);
field->value = strdup(value); field->value = strdup(value);

View file

@ -32,9 +32,6 @@
void xs_init(pTHX); void xs_init(pTHX);
void boot_Perf__Trace__Context(pTHX_ CV *cv);
void boot_DynaLoader(pTHX_ CV *cv);
void xs_init(pTHX) void xs_init(pTHX)
{ {
const char *file = __FILE__; const char *file = __FILE__;
@ -573,26 +570,72 @@ struct scripting_ops perl_scripting_ops = {
.generate_script = perl_generate_script, .generate_script = perl_generate_script,
}; };
#ifdef NO_LIBPERL static void print_unsupported_msg(void)
void setup_perl_scripting(void)
{ {
fprintf(stderr, "Perl scripting not supported." fprintf(stderr, "Perl scripting not supported."
" Install libperl and rebuild perf to enable it. e.g. " " Install libperl and rebuild perf to enable it.\n"
"apt-get install libperl-dev (ubuntu), yum install " "For example:\n # apt-get install libperl-dev (ubuntu)"
"perl-ExtUtils-Embed (Fedora), etc.\n"); "\n # yum install perl-ExtUtils-Embed (Fedora)"
"\n etc.\n");
} }
#else
void setup_perl_scripting(void) static int perl_start_script_unsupported(const char *script __unused)
{
print_unsupported_msg();
return -1;
}
static int perl_stop_script_unsupported(void)
{
return 0;
}
static void perl_process_event_unsupported(int cpu __unused,
void *data __unused,
int size __unused,
unsigned long long nsecs __unused,
char *comm __unused)
{
}
static int perl_generate_script_unsupported(const char *outfile __unused)
{
print_unsupported_msg();
return -1;
}
struct scripting_ops perl_scripting_unsupported_ops = {
.name = "Perl",
.start_script = perl_start_script_unsupported,
.stop_script = perl_stop_script_unsupported,
.process_event = perl_process_event_unsupported,
.generate_script = perl_generate_script_unsupported,
};
static void register_perl_scripting(struct scripting_ops *scripting_ops)
{ {
int err; int err;
err = script_spec_register("Perl", &perl_scripting_ops); err = script_spec_register("Perl", scripting_ops);
if (err) if (err)
die("error registering Perl script extension"); die("error registering Perl script extension");
err = script_spec_register("pl", &perl_scripting_ops); err = script_spec_register("pl", scripting_ops);
if (err) if (err)
die("error registering pl script extension"); die("error registering pl script extension");
scripting_context = malloc(sizeof(struct scripting_context)); scripting_context = malloc(sizeof(struct scripting_context));
} }
#ifdef NO_LIBPERL
void setup_perl_scripting(void)
{
register_perl_scripting(&perl_scripting_unsupported_ops);
}
#else
void setup_perl_scripting(void)
{
register_perl_scripting(&perl_scripting_ops);
}
#endif #endif

View file

@ -34,9 +34,13 @@ typedef int INTERP;
#define dXSUB_SYS #define dXSUB_SYS
#define pTHX_ #define pTHX_
static inline void newXS(const char *a, void *b, const char *c) {} static inline void newXS(const char *a, void *b, const char *c) {}
static void boot_Perf__Trace__Context(pTHX_ CV *cv) {}
static void boot_DynaLoader(pTHX_ CV *cv) {}
#else #else
#include <EXTERN.h> #include <EXTERN.h>
#include <perl.h> #include <perl.h>
void boot_Perf__Trace__Context(pTHX_ CV *cv);
void boot_DynaLoader(pTHX_ CV *cv);
typedef PerlInterpreter * INTERP; typedef PerlInterpreter * INTERP;
#endif #endif

View file

@ -145,8 +145,9 @@ static void read_proc_kallsyms(void)
if (!size) if (!size)
return; return;
buf = malloc_or_die(size); buf = malloc_or_die(size + 1);
read_or_die(buf, size); read_or_die(buf, size);
buf[size] = '\0';
parse_proc_kallsyms(buf, size); parse_proc_kallsyms(buf, size);