From ca037701a025334e724e5c61b3b1082940c8b981 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 2 Mar 2010 19:52:12 +0100 Subject: perf, x86: Add PEBS infrastructure This patch implements support for Intel Precise Event Based Sampling, which is an alternative counter mode in which the counter triggers a hardware assist to collect information on events. The hardware assist takes a trap like snapshot of a subset of the machine registers. This data is written to the Intel Debug-Store, which can be programmed with a data threshold at which to raise a PMI. With the PEBS hardware assist being trap like, the reported IP is always one instruction after the actual instruction that triggered the event. This implements a simple PEBS model that always takes a single PEBS event at a time. This is done so that the interaction with the rest of the system is as expected (freq adjust, period randomization, lbr, callchains, etc.). It adds an ABI element: perf_event_attr::precise, which indicates that we wish to use this (constrained, but precise) mode. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.392111285@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1d665a0b..0c03d5c 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -31,45 +31,6 @@ static u64 perf_event_mask __read_mostly; -/* The maximal number of PEBS events: */ -#define MAX_PEBS_EVENTS 4 - -/* The size of a BTS record in bytes: */ -#define BTS_RECORD_SIZE 24 - -/* The size of a per-cpu BTS buffer in bytes: */ -#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 2048) - -/* The BTS overflow threshold in bytes from the end of the buffer: */ -#define BTS_OVFL_TH (BTS_RECORD_SIZE * 128) - - -/* - * Bits in the debugctlmsr controlling branch tracing. - */ -#define X86_DEBUGCTL_TR (1 << 6) -#define X86_DEBUGCTL_BTS (1 << 7) -#define X86_DEBUGCTL_BTINT (1 << 8) -#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) -#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) - -/* - * A debug store configuration. - * - * We only support architectures that use 64bit fields. - */ -struct debug_store { - u64 bts_buffer_base; - u64 bts_index; - u64 bts_absolute_maximum; - u64 bts_interrupt_threshold; - u64 pebs_buffer_base; - u64 pebs_index; - u64 pebs_absolute_maximum; - u64 pebs_interrupt_threshold; - u64 pebs_event_reset[MAX_PEBS_EVENTS]; -}; - struct event_constraint { union { unsigned long idxmsk[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; @@ -88,17 +49,29 @@ struct amd_nb { }; struct cpu_hw_events { + /* + * Generic x86 PMC bits + */ struct perf_event *events[X86_PMC_IDX_MAX]; /* in counter order */ unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; unsigned long interrupts; int enabled; - struct debug_store *ds; int n_events; int n_added; int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ u64 tags[X86_PMC_IDX_MAX]; struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ + + /* + * Intel DebugStore bits + */ + struct debug_store *ds; + u64 pebs_enabled; + + /* + * AMD specific bits + */ struct amd_nb *amd_nb; }; @@ -112,12 +85,24 @@ struct cpu_hw_events { #define EVENT_CONSTRAINT(c, n, m) \ __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n)) +/* + * Constraint on the Event code. + */ #define INTEL_EVENT_CONSTRAINT(c, n) \ EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVTSEL_MASK) +/* + * Constraint on the Event code + UMask + fixed-mask + */ #define FIXED_EVENT_CONSTRAINT(c, n) \ EVENT_CONSTRAINT(c, (1ULL << (32+n)), INTEL_ARCH_FIXED_MASK) +/* + * Constraint on the Event code + UMask + */ +#define PEBS_EVENT_CONSTRAINT(c, n) \ + EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) + #define EVENT_CONSTRAINT_END \ EVENT_CONSTRAINT(0, 0, 0) @@ -128,6 +113,9 @@ struct cpu_hw_events { * struct x86_pmu - generic x86 pmu */ struct x86_pmu { + /* + * Generic x86 PMC bits + */ const char *name; int version; int (*handle_irq)(struct pt_regs *); @@ -146,10 +134,6 @@ struct x86_pmu { u64 event_mask; int apic; u64 max_period; - u64 intel_ctrl; - void (*enable_bts)(u64 config); - void (*disable_bts)(void); - struct event_constraint * (*get_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); @@ -162,6 +146,19 @@ struct x86_pmu { void (*cpu_starting)(int cpu); void (*cpu_dying)(int cpu); void (*cpu_dead)(int cpu); + + /* + * Intel Arch Perfmon v2+ + */ + u64 intel_ctrl; + + /* + * Intel DebugStore bits + */ + int bts, pebs; + int pebs_record_size; + void (*drain_pebs)(struct pt_regs *regs); + struct event_constraint *pebs_constraints; }; static struct x86_pmu x86_pmu __read_mostly; @@ -293,110 +290,14 @@ static void release_pmc_hardware(void) #endif } -static inline bool bts_available(void) -{ - return x86_pmu.enable_bts != NULL; -} - -static void init_debug_store_on_cpu(int cpu) -{ - struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; - - if (!ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, - (u32)((u64)(unsigned long)ds), - (u32)((u64)(unsigned long)ds >> 32)); -} - -static void fini_debug_store_on_cpu(int cpu) -{ - if (!per_cpu(cpu_hw_events, cpu).ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); -} - -static void release_bts_hardware(void) -{ - int cpu; - - if (!bts_available()) - return; - - get_online_cpus(); - - for_each_online_cpu(cpu) - fini_debug_store_on_cpu(cpu); - - for_each_possible_cpu(cpu) { - struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; - - if (!ds) - continue; - - per_cpu(cpu_hw_events, cpu).ds = NULL; - - kfree((void *)(unsigned long)ds->bts_buffer_base); - kfree(ds); - } - - put_online_cpus(); -} - -static int reserve_bts_hardware(void) -{ - int cpu, err = 0; - - if (!bts_available()) - return 0; - - get_online_cpus(); - - for_each_possible_cpu(cpu) { - struct debug_store *ds; - void *buffer; - - err = -ENOMEM; - buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); - if (unlikely(!buffer)) - break; - - ds = kzalloc(sizeof(*ds), GFP_KERNEL); - if (unlikely(!ds)) { - kfree(buffer); - break; - } - - ds->bts_buffer_base = (u64)(unsigned long)buffer; - ds->bts_index = ds->bts_buffer_base; - ds->bts_absolute_maximum = - ds->bts_buffer_base + BTS_BUFFER_SIZE; - ds->bts_interrupt_threshold = - ds->bts_absolute_maximum - BTS_OVFL_TH; - - per_cpu(cpu_hw_events, cpu).ds = ds; - err = 0; - } - - if (err) - release_bts_hardware(); - else { - for_each_online_cpu(cpu) - init_debug_store_on_cpu(cpu); - } - - put_online_cpus(); - - return err; -} +static int reserve_ds_buffers(void); +static void release_ds_buffers(void); static void hw_perf_event_destroy(struct perf_event *event) { if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { release_pmc_hardware(); - release_bts_hardware(); + release_ds_buffers(); mutex_unlock(&pmc_reserve_mutex); } } @@ -459,7 +360,7 @@ static int __hw_perf_event_init(struct perf_event *event) if (!reserve_pmc_hardware()) err = -EBUSY; else - err = reserve_bts_hardware(); + err = reserve_ds_buffers(); } if (!err) atomic_inc(&active_events); @@ -537,7 +438,7 @@ static int __hw_perf_event_init(struct perf_event *event) if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && (hwc->sample_period == 1)) { /* BTS is not supported by this architecture. */ - if (!bts_available()) + if (!x86_pmu.bts) return -EOPNOTSUPP; /* BTS is currently only allowed for user-mode. */ @@ -995,6 +896,7 @@ static void x86_pmu_unthrottle(struct perf_event *event) void perf_event_print_debug(void) { u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed; + u64 pebs; struct cpu_hw_events *cpuc; unsigned long flags; int cpu, idx; @@ -1012,12 +914,14 @@ void perf_event_print_debug(void) rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed); + rdmsrl(MSR_IA32_PEBS_ENABLE, pebs); pr_info("\n"); pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl); pr_info("CPU#%d: status: %016llx\n", cpu, status); pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow); pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); + pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs); } pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); @@ -1333,6 +1237,7 @@ undo: #include "perf_event_amd.c" #include "perf_event_p6.c" +#include "perf_event_intel_ds.c" #include "perf_event_intel.c" static int __cpuinit @@ -1465,6 +1370,32 @@ static const struct pmu pmu = { }; /* + * validate that we can schedule this event + */ +static int validate_event(struct perf_event *event) +{ + struct cpu_hw_events *fake_cpuc; + struct event_constraint *c; + int ret = 0; + + fake_cpuc = kmalloc(sizeof(*fake_cpuc), GFP_KERNEL | __GFP_ZERO); + if (!fake_cpuc) + return -ENOMEM; + + c = x86_pmu.get_event_constraints(fake_cpuc, event); + + if (!c || !c->weight) + ret = -ENOSPC; + + if (x86_pmu.put_event_constraints) + x86_pmu.put_event_constraints(fake_cpuc, event); + + kfree(fake_cpuc); + + return ret; +} + +/* * validate a single event group * * validation include: @@ -1529,6 +1460,8 @@ const struct pmu *hw_perf_event_init(struct perf_event *event) if (event->group_leader != event) err = validate_group(event); + else + err = validate_event(event); event->pmu = tmp; } diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 84bfde6..1144641 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -470,42 +470,6 @@ static u64 intel_pmu_raw_event(u64 hw_event) return hw_event & CORE_EVNTSEL_MASK; } -static void intel_pmu_enable_bts(u64 config) -{ - unsigned long debugctlmsr; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr |= X86_DEBUGCTL_TR; - debugctlmsr |= X86_DEBUGCTL_BTS; - debugctlmsr |= X86_DEBUGCTL_BTINT; - - if (!(config & ARCH_PERFMON_EVENTSEL_OS)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; - - if (!(config & ARCH_PERFMON_EVENTSEL_USR)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; - - update_debugctlmsr(debugctlmsr); -} - -static void intel_pmu_disable_bts(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - unsigned long debugctlmsr; - - if (!cpuc->ds) - return; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr &= - ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | - X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); - - update_debugctlmsr(debugctlmsr); -} - static void intel_pmu_disable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); @@ -514,6 +478,8 @@ static void intel_pmu_disable_all(void) if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) intel_pmu_disable_bts(); + + intel_pmu_pebs_disable_all(); } static void intel_pmu_enable_all(void) @@ -531,6 +497,8 @@ static void intel_pmu_enable_all(void) intel_pmu_enable_bts(event->hw.config); } + + intel_pmu_pebs_enable_all(); } static inline u64 intel_pmu_get_status(void) @@ -547,8 +515,7 @@ static inline void intel_pmu_ack_status(u64 ack) wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); } -static inline void -intel_pmu_disable_fixed(struct hw_perf_event *hwc) +static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) { int idx = hwc->idx - X86_PMC_IDX_FIXED; u64 ctrl_val, mask; @@ -560,68 +527,7 @@ intel_pmu_disable_fixed(struct hw_perf_event *hwc) (void)checking_wrmsrl(hwc->config_base, ctrl_val); } -static void intel_pmu_drain_bts_buffer(void) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct debug_store *ds = cpuc->ds; - struct bts_record { - u64 from; - u64 to; - u64 flags; - }; - struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; - struct bts_record *at, *top; - struct perf_output_handle handle; - struct perf_event_header header; - struct perf_sample_data data; - struct pt_regs regs; - - if (!event) - return; - - if (!ds) - return; - - at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; - top = (struct bts_record *)(unsigned long)ds->bts_index; - - if (top <= at) - return; - - ds->bts_index = ds->bts_buffer_base; - - perf_sample_data_init(&data, 0); - - data.period = event->hw.last_period; - regs.ip = 0; - - /* - * Prepare a generic sample, i.e. fill in the invariant fields. - * We will overwrite the from and to address before we output - * the sample. - */ - perf_prepare_sample(&header, &data, event, ®s); - - if (perf_output_begin(&handle, event, - header.size * (top - at), 1, 1)) - return; - - for (; at < top; at++) { - data.ip = at->from; - data.addr = at->to; - - perf_output_sample(&handle, &header, &data, event); - } - - perf_output_end(&handle); - - /* There's new data available. */ - event->hw.interrupts++; - event->pending_kill = POLL_IN; -} - -static inline void -intel_pmu_disable_event(struct perf_event *event) +static void intel_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -637,10 +543,12 @@ intel_pmu_disable_event(struct perf_event *event) } x86_pmu_disable_event(event); + + if (unlikely(event->attr.precise)) + intel_pmu_pebs_disable(hwc); } -static inline void -intel_pmu_enable_fixed(struct hw_perf_event *hwc) +static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) { int idx = hwc->idx - X86_PMC_IDX_FIXED; u64 ctrl_val, bits, mask; @@ -689,6 +597,9 @@ static void intel_pmu_enable_event(struct perf_event *event) return; } + if (unlikely(event->attr.precise)) + intel_pmu_pebs_enable(hwc); + __x86_pmu_enable_event(hwc); } @@ -762,6 +673,13 @@ again: inc_irq_stat(apic_perf_irqs); ack = status; + + /* + * PEBS overflow sets bit 62 in the global status register + */ + if (__test_and_clear_bit(62, (unsigned long *)&status)) + x86_pmu.drain_pebs(regs); + for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { struct perf_event *event = cpuc->events[bit]; @@ -791,22 +709,18 @@ done: return 1; } -static struct event_constraint bts_constraint = - EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); - static struct event_constraint * -intel_special_constraints(struct perf_event *event) +intel_bts_constraints(struct perf_event *event) { - unsigned int hw_event; - - hw_event = event->hw.config & INTEL_ARCH_EVENT_MASK; + struct hw_perf_event *hwc = &event->hw; + unsigned int hw_event, bts_event; - if (unlikely((hw_event == - x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && - (event->hw.sample_period == 1))) { + hw_event = hwc->config & INTEL_ARCH_EVENT_MASK; + bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); + if (unlikely(hw_event == bts_event && hwc->sample_period == 1)) return &bts_constraint; - } + return NULL; } @@ -815,7 +729,11 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event { struct event_constraint *c; - c = intel_special_constraints(event); + c = intel_bts_constraints(event); + if (c) + return c; + + c = intel_pebs_constraints(event); if (c) return c; @@ -864,8 +782,6 @@ static __initconst struct x86_pmu intel_pmu = { * the generic event period: */ .max_period = (1ULL << 31) - 1, - .enable_bts = intel_pmu_enable_bts, - .disable_bts = intel_pmu_disable_bts, .get_event_constraints = intel_get_event_constraints, .cpu_starting = init_debug_store_on_cpu, @@ -915,6 +831,8 @@ static __init int intel_pmu_init(void) if (version > 1) x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); + intel_ds_init(); + /* * Install the hw-cache-events table: */ diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c new file mode 100644 index 0000000..0d994ef --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -0,0 +1,557 @@ +#ifdef CONFIG_CPU_SUP_INTEL + +/* The maximal number of PEBS events: */ +#define MAX_PEBS_EVENTS 4 + +/* The size of a BTS record in bytes: */ +#define BTS_RECORD_SIZE 24 + +#define BTS_BUFFER_SIZE (PAGE_SIZE << 4) +#define PEBS_BUFFER_SIZE PAGE_SIZE + +/* + * pebs_record_32 for p4 and core not supported + +struct pebs_record_32 { + u32 flags, ip; + u32 ax, bc, cx, dx; + u32 si, di, bp, sp; +}; + + */ + +struct pebs_record_core { + u64 flags, ip; + u64 ax, bx, cx, dx; + u64 si, di, bp, sp; + u64 r8, r9, r10, r11; + u64 r12, r13, r14, r15; +}; + +struct pebs_record_nhm { + u64 flags, ip; + u64 ax, bx, cx, dx; + u64 si, di, bp, sp; + u64 r8, r9, r10, r11; + u64 r12, r13, r14, r15; + u64 status, dla, dse, lat; +}; + +/* + * Bits in the debugctlmsr controlling branch tracing. + */ +#define X86_DEBUGCTL_TR (1 << 6) +#define X86_DEBUGCTL_BTS (1 << 7) +#define X86_DEBUGCTL_BTINT (1 << 8) +#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) +#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) + +/* + * A debug store configuration. + * + * We only support architectures that use 64bit fields. + */ +struct debug_store { + u64 bts_buffer_base; + u64 bts_index; + u64 bts_absolute_maximum; + u64 bts_interrupt_threshold; + u64 pebs_buffer_base; + u64 pebs_index; + u64 pebs_absolute_maximum; + u64 pebs_interrupt_threshold; + u64 pebs_event_reset[MAX_PEBS_EVENTS]; +}; + +static void init_debug_store_on_cpu(int cpu) +{ + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + + if (!ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, + (u32)((u64)(unsigned long)ds), + (u32)((u64)(unsigned long)ds >> 32)); +} + +static void fini_debug_store_on_cpu(int cpu) +{ + if (!per_cpu(cpu_hw_events, cpu).ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); +} + +static void release_ds_buffers(void) +{ + int cpu; + + if (!x86_pmu.bts && !x86_pmu.pebs) + return; + + get_online_cpus(); + + for_each_online_cpu(cpu) + fini_debug_store_on_cpu(cpu); + + for_each_possible_cpu(cpu) { + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + + if (!ds) + continue; + + per_cpu(cpu_hw_events, cpu).ds = NULL; + + kfree((void *)(unsigned long)ds->pebs_buffer_base); + kfree((void *)(unsigned long)ds->bts_buffer_base); + kfree(ds); + } + + put_online_cpus(); +} + +static int reserve_ds_buffers(void) +{ + int cpu, err = 0; + + if (!x86_pmu.bts && !x86_pmu.pebs) + return 0; + + get_online_cpus(); + + for_each_possible_cpu(cpu) { + struct debug_store *ds; + void *buffer; + int max, thresh; + + err = -ENOMEM; + ds = kzalloc(sizeof(*ds), GFP_KERNEL); + if (unlikely(!ds)) { + kfree(buffer); + break; + } + per_cpu(cpu_hw_events, cpu).ds = ds; + + if (x86_pmu.bts) { + buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); + if (unlikely(!buffer)) + break; + + max = BTS_BUFFER_SIZE / BTS_RECORD_SIZE; + thresh = max / 16; + + ds->bts_buffer_base = (u64)(unsigned long)buffer; + ds->bts_index = ds->bts_buffer_base; + ds->bts_absolute_maximum = ds->bts_buffer_base + + max * BTS_RECORD_SIZE; + ds->bts_interrupt_threshold = ds->bts_absolute_maximum - + thresh * BTS_RECORD_SIZE; + } + + if (x86_pmu.pebs) { + buffer = kzalloc(PEBS_BUFFER_SIZE, GFP_KERNEL); + if (unlikely(!buffer)) + break; + + max = PEBS_BUFFER_SIZE / x86_pmu.pebs_record_size; + + ds->pebs_buffer_base = (u64)(unsigned long)buffer; + ds->pebs_index = ds->pebs_buffer_base; + ds->pebs_absolute_maximum = ds->pebs_buffer_base + + max * x86_pmu.pebs_record_size; + /* + * Always use single record PEBS + */ + ds->pebs_interrupt_threshold = ds->pebs_buffer_base + + x86_pmu.pebs_record_size; + } + + err = 0; + } + + if (err) + release_ds_buffers(); + else { + for_each_online_cpu(cpu) + init_debug_store_on_cpu(cpu); + } + + put_online_cpus(); + + return err; +} + +/* + * BTS + */ + +static struct event_constraint bts_constraint = + EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); + +static void intel_pmu_enable_bts(u64 config) +{ + unsigned long debugctlmsr; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr |= X86_DEBUGCTL_TR; + debugctlmsr |= X86_DEBUGCTL_BTS; + debugctlmsr |= X86_DEBUGCTL_BTINT; + + if (!(config & ARCH_PERFMON_EVENTSEL_OS)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; + + if (!(config & ARCH_PERFMON_EVENTSEL_USR)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; + + update_debugctlmsr(debugctlmsr); +} + +static void intel_pmu_disable_bts(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + unsigned long debugctlmsr; + + if (!cpuc->ds) + return; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr &= + ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | + X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); + + update_debugctlmsr(debugctlmsr); +} + +static void intel_pmu_drain_bts_buffer(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct debug_store *ds = cpuc->ds; + struct bts_record { + u64 from; + u64 to; + u64 flags; + }; + struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; + struct bts_record *at, *top; + struct perf_output_handle handle; + struct perf_event_header header; + struct perf_sample_data data; + struct pt_regs regs; + + if (!event) + return; + + if (!ds) + return; + + at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; + top = (struct bts_record *)(unsigned long)ds->bts_index; + + if (top <= at) + return; + + ds->bts_index = ds->bts_buffer_base; + + perf_sample_data_init(&data, 0); + data.period = event->hw.last_period; + regs.ip = 0; + + /* + * Prepare a generic sample, i.e. fill in the invariant fields. + * We will overwrite the from and to address before we output + * the sample. + */ + perf_prepare_sample(&header, &data, event, ®s); + + if (perf_output_begin(&handle, event, header.size * (top - at), 1, 1)) + return; + + for (; at < top; at++) { + data.ip = at->from; + data.addr = at->to; + + perf_output_sample(&handle, &header, &data, event); + } + + perf_output_end(&handle); + + /* There's new data available. */ + event->hw.interrupts++; + event->pending_kill = POLL_IN; +} + +/* + * PEBS + */ + +static struct event_constraint intel_core_pebs_events[] = { + PEBS_EVENT_CONSTRAINT(0x00c0, 0x1), /* INSTR_RETIRED.ANY */ + PEBS_EVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ + PEBS_EVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ + PEBS_EVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */ + PEBS_EVENT_CONSTRAINT(0x01cb, 0x1), /* MEM_LOAD_RETIRED.L1D_MISS */ + PEBS_EVENT_CONSTRAINT(0x02cb, 0x1), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */ + PEBS_EVENT_CONSTRAINT(0x04cb, 0x1), /* MEM_LOAD_RETIRED.L2_MISS */ + PEBS_EVENT_CONSTRAINT(0x08cb, 0x1), /* MEM_LOAD_RETIRED.L2_LINE_MISS */ + PEBS_EVENT_CONSTRAINT(0x10cb, 0x1), /* MEM_LOAD_RETIRED.DTLB_MISS */ + EVENT_CONSTRAINT_END +}; + +static struct event_constraint intel_nehalem_pebs_events[] = { + PEBS_EVENT_CONSTRAINT(0x00c0, 0xf), /* INSTR_RETIRED.ANY */ + PEBS_EVENT_CONSTRAINT(0xfec1, 0xf), /* X87_OPS_RETIRED.ANY */ + PEBS_EVENT_CONSTRAINT(0x00c5, 0xf), /* BR_INST_RETIRED.MISPRED */ + PEBS_EVENT_CONSTRAINT(0x1fc7, 0xf), /* SIMD_INST_RETURED.ANY */ + PEBS_EVENT_CONSTRAINT(0x01cb, 0xf), /* MEM_LOAD_RETIRED.L1D_MISS */ + PEBS_EVENT_CONSTRAINT(0x02cb, 0xf), /* MEM_LOAD_RETIRED.L1D_LINE_MISS */ + PEBS_EVENT_CONSTRAINT(0x04cb, 0xf), /* MEM_LOAD_RETIRED.L2_MISS */ + PEBS_EVENT_CONSTRAINT(0x08cb, 0xf), /* MEM_LOAD_RETIRED.L2_LINE_MISS */ + PEBS_EVENT_CONSTRAINT(0x10cb, 0xf), /* MEM_LOAD_RETIRED.DTLB_MISS */ + EVENT_CONSTRAINT_END +}; + +static struct event_constraint * +intel_pebs_constraints(struct perf_event *event) +{ + struct event_constraint *c; + + if (!event->attr.precise) + return NULL; + + if (x86_pmu.pebs_constraints) { + for_each_event_constraint(c, x86_pmu.pebs_constraints) { + if ((event->hw.config & c->cmask) == c->code) + return c; + } + } + + return &emptyconstraint; +} + +static void intel_pmu_pebs_enable(struct hw_perf_event *hwc) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val = cpuc->pebs_enabled; + + hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; + + val |= 1ULL << hwc->idx; + wrmsrl(MSR_IA32_PEBS_ENABLE, val); +} + +static void intel_pmu_pebs_disable(struct hw_perf_event *hwc) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val = cpuc->pebs_enabled; + + val &= ~(1ULL << hwc->idx); + wrmsrl(MSR_IA32_PEBS_ENABLE, val); + + hwc->config |= ARCH_PERFMON_EVENTSEL_INT; +} + +static void intel_pmu_pebs_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->pebs_enabled) + wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); +} + +static void intel_pmu_pebs_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->pebs_enabled) + wrmsrl(MSR_IA32_PEBS_ENABLE, 0); +} + +static int intel_pmu_save_and_restart(struct perf_event *event); +static void intel_pmu_disable_event(struct perf_event *event); + +static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct debug_store *ds = cpuc->ds; + struct perf_event *event = cpuc->events[0]; /* PMC0 only */ + struct pebs_record_core *at, *top; + struct perf_sample_data data; + struct pt_regs regs; + int n; + + if (!event || !ds || !x86_pmu.pebs) + return; + + intel_pmu_pebs_disable_all(); + + at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base; + top = (struct pebs_record_core *)(unsigned long)ds->pebs_index; + + if (top <= at) + goto out; + + ds->pebs_index = ds->pebs_buffer_base; + + if (!intel_pmu_save_and_restart(event)) + goto out; + + perf_sample_data_init(&data, 0); + data.period = event->hw.last_period; + + n = top - at; + + /* + * Should not happen, we program the threshold at 1 and do not + * set a reset value. + */ + WARN_ON_ONCE(n > 1); + + /* + * We use the interrupt regs as a base because the PEBS record + * does not contain a full regs set, specifically it seems to + * lack segment descriptors, which get used by things like + * user_mode(). + * + * In the simple case fix up only the IP and BP,SP regs, for + * PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly. + * A possible PERF_SAMPLE_REGS will have to transfer all regs. + */ + regs = *iregs; + regs.ip = at->ip; + regs.bp = at->bp; + regs.sp = at->sp; + + if (perf_event_overflow(event, 1, &data, ®s)) + intel_pmu_disable_event(event); + +out: + intel_pmu_pebs_enable_all(); +} + +static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct debug_store *ds = cpuc->ds; + struct pebs_record_nhm *at, *top; + struct perf_sample_data data; + struct perf_event *event = NULL; + struct pt_regs regs; + int bit, n; + + if (!ds || !x86_pmu.pebs) + return; + + intel_pmu_pebs_disable_all(); + + at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; + top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; + + if (top <= at) + goto out; + + ds->pebs_index = ds->pebs_buffer_base; + + n = top - at; + + /* + * Should not happen, we program the threshold at 1 and do not + * set a reset value. + */ + WARN_ON_ONCE(n > MAX_PEBS_EVENTS); + + for ( ; at < top; at++) { + for_each_bit(bit, (unsigned long *)&at->status, MAX_PEBS_EVENTS) { + if (!cpuc->events[bit]->attr.precise) + continue; + + event = cpuc->events[bit]; + } + + if (!event) + continue; + + if (!intel_pmu_save_and_restart(event)) + continue; + + perf_sample_data_init(&data, 0); + data.period = event->hw.last_period; + + /* + * See the comment in intel_pmu_drain_pebs_core() + */ + regs = *iregs; + regs.ip = at->ip; + regs.bp = at->bp; + regs.sp = at->sp; + + if (perf_event_overflow(event, 1, &data, ®s)) + intel_pmu_disable_event(event); + } +out: + intel_pmu_pebs_enable_all(); +} + +/* + * BTS, PEBS probe and setup + */ + +static void intel_ds_init(void) +{ + /* + * No support for 32bit formats + */ + if (!boot_cpu_has(X86_FEATURE_DTES64)) + return; + + x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); + x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS); + if (x86_pmu.pebs) { + int format = 0; + + if (x86_pmu.version > 1) { + u64 capabilities; + /* + * v2+ has a PEBS format field + */ + rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); + format = (capabilities >> 8) & 0xf; + } + + switch (format) { + case 0: + printk(KERN_CONT "PEBS v0, "); + x86_pmu.pebs_record_size = sizeof(struct pebs_record_core); + x86_pmu.drain_pebs = intel_pmu_drain_pebs_core; + x86_pmu.pebs_constraints = intel_core_pebs_events; + break; + + case 1: + printk(KERN_CONT "PEBS v1, "); + x86_pmu.pebs_record_size = sizeof(struct pebs_record_nhm); + x86_pmu.drain_pebs = intel_pmu_drain_pebs_nhm; + x86_pmu.pebs_constraints = intel_nehalem_pebs_events; + break; + + default: + printk(KERN_CONT "PEBS unknown format: %d, ", format); + x86_pmu.pebs = 0; + break; + } + } +} + +#else /* CONFIG_CPU_SUP_INTEL */ + +static int reseve_ds_buffers(void) +{ + return 0; +} + +static void release_ds_buffers(void) +{ +} + +#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 80acbf3..42307b5 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -203,8 +203,9 @@ struct perf_event_attr { enable_on_exec : 1, /* next exec enables */ task : 1, /* trace fork/exit */ watermark : 1, /* wakeup_watermark */ + precise : 1, /* OoO invariant counter */ - __reserved_1 : 49; + __reserved_1 : 48; union { __u32 wakeup_events; /* wakeup every n events */ -- cgit v0.10.2 From 69fef0d2e2c2c049ef4207a52e78b50d527bd85a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Mar 2010 13:57:24 +0100 Subject: perf: Add attr->precise support to raw event parsing Minimal userspace interface to the new 'precise' events flag. Can be used like "perf top -e r00c0p" which will use PEBS to sample retired instructions. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.468665803@chello.nl> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 05d0c5c..a201445 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -656,6 +656,10 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { + if (str[n+1] == 'p') { + attr->precise = 1; + n++; + } *strp = str + n + 1; attr->type = PERF_TYPE_RAW; attr->config = config; -- cgit v0.10.2 From caff2befffe899e63df5cc760b7ed01cfd902685 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 3 Mar 2010 12:02:30 +0100 Subject: perf, x86: Implement simple LBR support Implement simple suport Intel Last-Branch-Record, it supports all hardware that implements FREEZE_LBRS_ON_PMI, but does not (yet) implement the LBR config register. The Intel LBR is a FIFO of From,To addresses describing the last few branches the hardware took. This patch does not add perf interface to the LBR, but merely provides an interface for internal use. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.544191154@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 0c03d5c..1badff6 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -48,6 +48,8 @@ struct amd_nb { struct event_constraint event_constraints[X86_PMC_IDX_MAX]; }; +#define MAX_LBR_ENTRIES 16 + struct cpu_hw_events { /* * Generic x86 PMC bits @@ -70,6 +72,14 @@ struct cpu_hw_events { u64 pebs_enabled; /* + * Intel LBR bits + */ + int lbr_users; + void *lbr_context; + struct perf_branch_stack lbr_stack; + struct perf_branch_entry lbr_entries[MAX_LBR_ENTRIES]; + + /* * AMD specific bits */ struct amd_nb *amd_nb; @@ -159,6 +169,13 @@ struct x86_pmu { int pebs_record_size; void (*drain_pebs)(struct pt_regs *regs); struct event_constraint *pebs_constraints; + + /* + * Intel LBR + */ + unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ + int lbr_nr; /* hardware stack size */ + int lbr_format; /* hardware format */ }; static struct x86_pmu x86_pmu __read_mostly; @@ -1237,6 +1254,7 @@ undo: #include "perf_event_amd.c" #include "perf_event_p6.c" +#include "perf_event_intel_lbr.c" #include "perf_event_intel_ds.c" #include "perf_event_intel.c" diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 1144641..44f6ed4 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -480,6 +480,7 @@ static void intel_pmu_disable_all(void) intel_pmu_disable_bts(); intel_pmu_pebs_disable_all(); + intel_pmu_lbr_disable_all(); } static void intel_pmu_enable_all(void) @@ -499,6 +500,7 @@ static void intel_pmu_enable_all(void) } intel_pmu_pebs_enable_all(); + intel_pmu_lbr_enable_all(); } static inline u64 intel_pmu_get_status(void) @@ -674,6 +676,8 @@ again: inc_irq_stat(apic_perf_irqs); ack = status; + intel_pmu_lbr_read(); + /* * PEBS overflow sets bit 62 in the global status register */ @@ -848,6 +852,8 @@ static __init int intel_pmu_init(void) memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + intel_pmu_lbr_init_core(); + x86_pmu.event_constraints = intel_core2_event_constraints; pr_cont("Core2 events, "); break; @@ -857,13 +863,18 @@ static __init int intel_pmu_init(void) memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + intel_pmu_lbr_init_nhm(); + x86_pmu.event_constraints = intel_nehalem_event_constraints; pr_cont("Nehalem/Corei7 events, "); break; + case 28: /* Atom */ memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + intel_pmu_lbr_init_atom(); + x86_pmu.event_constraints = intel_gen_event_constraints; pr_cont("Atom events, "); break; @@ -873,6 +884,8 @@ static __init int intel_pmu_init(void) memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + intel_pmu_lbr_init_nhm(); + x86_pmu.event_constraints = intel_westmere_event_constraints; pr_cont("Westmere events, "); break; diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c new file mode 100644 index 0000000..ea3e99e --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -0,0 +1,228 @@ +#ifdef CONFIG_CPU_SUP_INTEL + +enum { + LBR_FORMAT_32 = 0x00, + LBR_FORMAT_LIP = 0x01, + LBR_FORMAT_EIP = 0x02, + LBR_FORMAT_EIP_FLAGS = 0x03, +}; + +/* + * We only support LBR implementations that have FREEZE_LBRS_ON_PMI + * otherwise it becomes near impossible to get a reliable stack. + */ + +#define X86_DEBUGCTL_LBR (1 << 0) +#define X86_DEBUGCTL_FREEZE_LBRS_ON_PMI (1 << 11) + +static void __intel_pmu_lbr_enable(void) +{ + u64 debugctl; + + rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); + debugctl |= (X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI); + wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); +} + +static void __intel_pmu_lbr_disable(void) +{ + u64 debugctl; + + rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); + debugctl &= ~(X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI); + wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); +} + +static void intel_pmu_lbr_reset_32(void) +{ + int i; + + for (i = 0; i < x86_pmu.lbr_nr; i++) + wrmsrl(x86_pmu.lbr_from + i, 0); +} + +static void intel_pmu_lbr_reset_64(void) +{ + int i; + + for (i = 0; i < x86_pmu.lbr_nr; i++) { + wrmsrl(x86_pmu.lbr_from + i, 0); + wrmsrl(x86_pmu.lbr_to + i, 0); + } +} + +static void intel_pmu_lbr_reset(void) +{ + if (x86_pmu.lbr_format == LBR_FORMAT_32) + intel_pmu_lbr_reset_32(); + else + intel_pmu_lbr_reset_64(); +} + +static void intel_pmu_lbr_enable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (!x86_pmu.lbr_nr) + return; + + WARN_ON(cpuc->enabled); + + /* + * Reset the LBR stack if this is the first LBR user or + * we changed task context so as to avoid data leaks. + */ + + if (!cpuc->lbr_users || + (event->ctx->task && cpuc->lbr_context != event->ctx)) { + intel_pmu_lbr_reset(); + cpuc->lbr_context = event->ctx; + } + + cpuc->lbr_users++; +} + +static void intel_pmu_lbr_disable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (!x86_pmu.lbr_nr) + return; + + cpuc->lbr_users--; + + BUG_ON(cpuc->lbr_users < 0); + WARN_ON(cpuc->enabled); +} + +static void intel_pmu_lbr_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->lbr_users) + __intel_pmu_lbr_enable(); +} + +static void intel_pmu_lbr_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->lbr_users) + __intel_pmu_lbr_disable(); +} + +static inline u64 intel_pmu_lbr_tos(void) +{ + u64 tos; + + rdmsrl(x86_pmu.lbr_tos, tos); + + return tos; +} + +static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) +{ + unsigned long mask = x86_pmu.lbr_nr - 1; + u64 tos = intel_pmu_lbr_tos(); + int i; + + for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) { + unsigned long lbr_idx = (tos - i) & mask; + union { + struct { + u32 from; + u32 to; + }; + u64 lbr; + } msr_lastbranch; + + rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr); + + cpuc->lbr_entries[i].from = msr_lastbranch.from; + cpuc->lbr_entries[i].to = msr_lastbranch.to; + cpuc->lbr_entries[i].flags = 0; + } + cpuc->lbr_stack.nr = i; +} + +#define LBR_FROM_FLAG_MISPRED (1ULL << 63) + +/* + * Due to lack of segmentation in Linux the effective address (offset) + * is the same as the linear address, allowing us to merge the LIP and EIP + * LBR formats. + */ +static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) +{ + unsigned long mask = x86_pmu.lbr_nr - 1; + u64 tos = intel_pmu_lbr_tos(); + int i; + + for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) { + unsigned long lbr_idx = (tos - i) & mask; + u64 from, to, flags = 0; + + rdmsrl(x86_pmu.lbr_from + lbr_idx, from); + rdmsrl(x86_pmu.lbr_to + lbr_idx, to); + + if (x86_pmu.lbr_format == LBR_FORMAT_EIP_FLAGS) { + flags = !!(from & LBR_FROM_FLAG_MISPRED); + from = (u64)((((s64)from) << 1) >> 1); + } + + cpuc->lbr_entries[i].from = from; + cpuc->lbr_entries[i].to = to; + cpuc->lbr_entries[i].flags = flags; + } + cpuc->lbr_stack.nr = i; +} + +static void intel_pmu_lbr_read(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (!cpuc->lbr_users) + return; + + if (x86_pmu.lbr_format == LBR_FORMAT_32) + intel_pmu_lbr_read_32(cpuc); + else + intel_pmu_lbr_read_64(cpuc); +} + +static int intel_pmu_lbr_format(void) +{ + u64 capabilities; + + rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); + return capabilities & 0x1f; +} + +static void intel_pmu_lbr_init_core(void) +{ + x86_pmu.lbr_format = intel_pmu_lbr_format(); + x86_pmu.lbr_nr = 4; + x86_pmu.lbr_tos = 0x01c9; + x86_pmu.lbr_from = 0x40; + x86_pmu.lbr_to = 0x60; +} + +static void intel_pmu_lbr_init_nhm(void) +{ + x86_pmu.lbr_format = intel_pmu_lbr_format(); + x86_pmu.lbr_nr = 16; + x86_pmu.lbr_tos = 0x01c9; + x86_pmu.lbr_from = 0x680; + x86_pmu.lbr_to = 0x6c0; +} + +static void intel_pmu_lbr_init_atom(void) +{ + x86_pmu.lbr_format = intel_pmu_lbr_format(); + x86_pmu.lbr_nr = 8; + x86_pmu.lbr_tos = 0x01c9; + x86_pmu.lbr_from = 0x40; + x86_pmu.lbr_to = 0x60; +} + +#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 42307b5..ab4fd9e 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -467,6 +467,17 @@ struct perf_raw_record { void *data; }; +struct perf_branch_entry { + __u64 from; + __u64 to; + __u64 flags; +}; + +struct perf_branch_stack { + __u64 nr; + struct perf_branch_entry entries[0]; +}; + struct task_struct; /** -- cgit v0.10.2 From ef21f683a045a79b6aa86ad81e5fdfc0d5ddd250 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 3 Mar 2010 13:12:23 +0100 Subject: perf, x86: use LBR for PEBS IP+1 fixup Use the LBR to fix up the PEBS IP+1 issue. As said, PEBS reports the next instruction, here we use the LBR to find the last branch and from that construct the actual IP. If the IP matches the LBR-TO, we use LBR-FROM, otherwise we use the LBR-TO address as the beginning of the last basic block and decode forward. Once we find a match to the current IP, we use the previous location. This patch introduces a new ABI element: PERF_RECORD_MISC_EXACT, which conveys that the reported IP (PERF_SAMPLE_IP) is the exact instruction that caused the event (barring CPU errata). The fixup can fail due to various reasons: 1) LBR contains invalid data (quite possible) 2) part of the basic block got paged out 3) the reported IP isn't part of the basic block (see 1) Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: "Zhang, Yanmin" Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.619375431@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index db6109a..a9038c9 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -136,6 +136,25 @@ extern void perf_events_lapic_init(void); #define PERF_EVENT_INDEX_OFFSET 0 +/* + * Abuse bit 3 of the cpu eflags register to indicate proper PEBS IP fixups. + * This flag is otherwise unused and ABI specified to be 0, so nobody should + * care what we do with it. + */ +#define PERF_EFLAGS_EXACT (1UL << 3) + +#define perf_misc_flags(regs) \ +({ int misc = 0; \ + if (user_mode(regs)) \ + misc |= PERF_RECORD_MISC_USER; \ + else \ + misc |= PERF_RECORD_MISC_KERNEL; \ + if (regs->flags & PERF_EFLAGS_EXACT) \ + misc |= PERF_RECORD_MISC_EXACT; \ + misc; }) + +#define perf_instruction_pointer(regs) ((regs)->ip) + #else static inline void init_hw_perf_events(void) { } static inline void perf_events_lapic_init(void) { } diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1badff6..5cb4e8d 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -29,6 +29,41 @@ #include #include +/* + * best effort, GUP based copy_from_user() that assumes IRQ or NMI context + */ +static unsigned long +copy_from_user_nmi(void *to, const void __user *from, unsigned long n) +{ + unsigned long offset, addr = (unsigned long)from; + int type = in_nmi() ? KM_NMI : KM_IRQ0; + unsigned long size, len = 0; + struct page *page; + void *map; + int ret; + + do { + ret = __get_user_pages_fast(addr, 1, 0, &page); + if (!ret) + break; + + offset = addr & (PAGE_SIZE - 1); + size = min(PAGE_SIZE - offset, n - len); + + map = kmap_atomic(page, type); + memcpy(to, map+offset, size); + kunmap_atomic(map, type); + put_page(page); + + len += size; + to += size; + addr += size; + + } while (len < n); + + return len; +} + static u64 perf_event_mask __read_mostly; struct event_constraint { @@ -1550,41 +1585,6 @@ perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) dump_trace(NULL, regs, NULL, regs->bp, &backtrace_ops, entry); } -/* - * best effort, GUP based copy_from_user() that assumes IRQ or NMI context - */ -static unsigned long -copy_from_user_nmi(void *to, const void __user *from, unsigned long n) -{ - unsigned long offset, addr = (unsigned long)from; - int type = in_nmi() ? KM_NMI : KM_IRQ0; - unsigned long size, len = 0; - struct page *page; - void *map; - int ret; - - do { - ret = __get_user_pages_fast(addr, 1, 0, &page); - if (!ret) - break; - - offset = addr & (PAGE_SIZE - 1); - size = min(PAGE_SIZE - offset, n - len); - - map = kmap_atomic(page, type); - memcpy(to, map+offset, size); - kunmap_atomic(map, type); - put_page(page); - - len += size; - to += size; - addr += size; - - } while (len < n); - - return len; -} - static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) { unsigned long bytes; diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 44f6ed4..7eb78be 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -547,7 +547,7 @@ static void intel_pmu_disable_event(struct perf_event *event) x86_pmu_disable_event(event); if (unlikely(event->attr.precise)) - intel_pmu_pebs_disable(hwc); + intel_pmu_pebs_disable(event); } static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) @@ -600,7 +600,7 @@ static void intel_pmu_enable_event(struct perf_event *event) } if (unlikely(event->attr.precise)) - intel_pmu_pebs_enable(hwc); + intel_pmu_pebs_enable(event); __x86_pmu_enable_event(hwc); } diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 0d994ef..50e6ff3 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -331,26 +331,32 @@ intel_pebs_constraints(struct perf_event *event) return &emptyconstraint; } -static void intel_pmu_pebs_enable(struct hw_perf_event *hwc) +static void intel_pmu_pebs_enable(struct perf_event *event) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; u64 val = cpuc->pebs_enabled; hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; val |= 1ULL << hwc->idx; wrmsrl(MSR_IA32_PEBS_ENABLE, val); + + intel_pmu_lbr_enable(event); } -static void intel_pmu_pebs_disable(struct hw_perf_event *hwc) +static void intel_pmu_pebs_disable(struct perf_event *event) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; u64 val = cpuc->pebs_enabled; val &= ~(1ULL << hwc->idx); wrmsrl(MSR_IA32_PEBS_ENABLE, val); hwc->config |= ARCH_PERFMON_EVENTSEL_INT; + + intel_pmu_lbr_disable(event); } static void intel_pmu_pebs_enable_all(void) @@ -369,6 +375,70 @@ static void intel_pmu_pebs_disable_all(void) wrmsrl(MSR_IA32_PEBS_ENABLE, 0); } +#include + +#define MAX_INSN_SIZE 16 + +static inline bool kernel_ip(unsigned long ip) +{ +#ifdef CONFIG_X86_32 + return ip > PAGE_OFFSET; +#else + return (long)ip < 0; +#endif +} + +static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + unsigned long from = cpuc->lbr_entries[0].from; + unsigned long old_to, to = cpuc->lbr_entries[0].to; + unsigned long ip = regs->ip; + + if (!cpuc->lbr_stack.nr || !from || !to) + return 0; + + if (ip < to) + return 0; + + /* + * We sampled a branch insn, rewind using the LBR stack + */ + if (ip == to) { + regs->ip = from; + return 1; + } + + do { + struct insn insn; + u8 buf[MAX_INSN_SIZE]; + void *kaddr; + + old_to = to; + if (!kernel_ip(ip)) { + int bytes, size = min_t(int, MAX_INSN_SIZE, ip - to); + + bytes = copy_from_user_nmi(buf, (void __user *)to, size); + if (bytes != size) + return 0; + + kaddr = buf; + } else + kaddr = (void *)to; + + kernel_insn_init(&insn, kaddr); + insn_get_length(&insn); + to += insn.length; + } while (to < ip); + + if (to == ip) { + regs->ip = old_to; + return 1; + } + + return 0; +} + static int intel_pmu_save_and_restart(struct perf_event *event); static void intel_pmu_disable_event(struct perf_event *event); @@ -424,6 +494,11 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) regs.bp = at->bp; regs.sp = at->sp; + if (intel_pmu_pebs_fixup_ip(®s)) + regs.flags |= PERF_EFLAGS_EXACT; + else + regs.flags &= ~PERF_EFLAGS_EXACT; + if (perf_event_overflow(event, 1, &data, ®s)) intel_pmu_disable_event(event); @@ -487,6 +562,11 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) regs.bp = at->bp; regs.sp = at->sp; + if (intel_pmu_pebs_fixup_ip(®s)) + regs.flags |= PERF_EFLAGS_EXACT; + else + regs.flags &= ~PERF_EFLAGS_EXACT; + if (perf_event_overflow(event, 1, &data, ®s)) intel_pmu_disable_event(event); } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index ab4fd9e..be85f7c 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -294,6 +294,12 @@ struct perf_event_mmap_page { #define PERF_RECORD_MISC_USER (2 << 0) #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) +#define PERF_RECORD_MISC_EXACT (1 << 14) +/* + * Reserve the last bit to indicate some extended misc field + */ +#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) + struct perf_event_header { __u32 type; __u16 misc; -- cgit v0.10.2 From 1676b8a077c352085d52578fb4f29350b58b6e74 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Mar 2010 14:19:36 +0100 Subject: perf-top: Show the percentage of successfull PEBS-fixups Use the PERF_RECORD_MISC_EXACT information to measure the success rate of the PEBS fix-up. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.694233760@chello.nl> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 31f2e59..c051833 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -410,6 +410,7 @@ static double sym_weight(const struct sym_entry *sym) static long samples; static long userspace_samples; +static long exact_samples; static const char CONSOLE_CLEAR[] = ""; static void __list_insert_active_sym(struct sym_entry *syme) @@ -450,6 +451,7 @@ static void print_sym_table(void) int counter, snap = !display_weighted ? sym_counter : 0; float samples_per_sec = samples/delay_secs; float ksamples_per_sec = (samples-userspace_samples)/delay_secs; + float esamples_percent = (100.0*exact_samples)/samples; float sum_ksamples = 0.0; struct sym_entry *syme, *n; struct rb_root tmp = RB_ROOT; @@ -457,7 +459,7 @@ static void print_sym_table(void) int sym_width = 0, dso_width = 0, max_dso_width; const int win_width = winsize.ws_col - 1; - samples = userspace_samples = 0; + samples = userspace_samples = exact_samples = 0; /* Sort the active symbols */ pthread_mutex_lock(&active_symbols_lock); @@ -488,9 +490,10 @@ static void print_sym_table(void) puts(CONSOLE_CLEAR); printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", + printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% exact: %4.1f%% [", samples_per_sec, - 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); + 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)), + esamples_percent); if (nr_counters == 1 || !display_weighted) { printf("%Ld", (u64)attrs[0].sample_period); @@ -954,6 +957,9 @@ static void event__process_sample(const event_t *self, return; } + if (self->header.misc & PERF_RECORD_MISC_EXACT) + exact_samples++; + if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || al.filtered) return; -- cgit v0.10.2 From 8db909a7e3c888b5d45aef7650d74ccebe3ce725 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 3 Mar 2010 17:07:40 +0100 Subject: perf, x86: Clean up IA32_PERF_CAPABILITIES usage Saner PERF_CAPABILITIES support, which also exposes pebs_trap. Use that latter to make PEBS's use of LBR conditional since a fault-like pebs should already report the correct IP. ( As of this writing there is no known hardware that implements !pebs_trap ) Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.770650663@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 5cb4e8d..7b5430b 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -154,6 +154,17 @@ struct cpu_hw_events { #define for_each_event_constraint(e, c) \ for ((e) = (c); (e)->cmask; (e)++) +union perf_capabilities { + struct { + u64 lbr_format : 6; + u64 pebs_trap : 1; + u64 pebs_arch_reg : 1; + u64 pebs_format : 4; + u64 smm_freeze : 1; + }; + u64 capabilities; +}; + /* * struct x86_pmu - generic x86 pmu */ @@ -195,7 +206,8 @@ struct x86_pmu { /* * Intel Arch Perfmon v2+ */ - u64 intel_ctrl; + u64 intel_ctrl; + union perf_capabilities intel_cap; /* * Intel DebugStore bits @@ -210,7 +222,6 @@ struct x86_pmu { */ unsigned long lbr_tos, lbr_from, lbr_to; /* MSR base regs */ int lbr_nr; /* hardware stack size */ - int lbr_format; /* hardware format */ }; static struct x86_pmu x86_pmu __read_mostly; diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 7eb78be..246c072 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -835,6 +835,16 @@ static __init int intel_pmu_init(void) if (version > 1) x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); + /* + * v2 and above have a perf capabilities MSR + */ + if (version > 1) { + u64 capabilities; + + rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); + x86_pmu.intel_cap.capabilities = capabilities; + } + intel_ds_init(); /* diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 50e6ff3..5e40294 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -342,7 +342,8 @@ static void intel_pmu_pebs_enable(struct perf_event *event) val |= 1ULL << hwc->idx; wrmsrl(MSR_IA32_PEBS_ENABLE, val); - intel_pmu_lbr_enable(event); + if (x86_pmu.intel_cap.pebs_trap) + intel_pmu_lbr_enable(event); } static void intel_pmu_pebs_disable(struct perf_event *event) @@ -356,7 +357,8 @@ static void intel_pmu_pebs_disable(struct perf_event *event) hwc->config |= ARCH_PERFMON_EVENTSEL_INT; - intel_pmu_lbr_disable(event); + if (x86_pmu.intel_cap.pebs_trap) + intel_pmu_lbr_disable(event); } static void intel_pmu_pebs_enable_all(void) @@ -395,6 +397,12 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) unsigned long old_to, to = cpuc->lbr_entries[0].to; unsigned long ip = regs->ip; + /* + * We don't need to fixup if the PEBS assist is fault like + */ + if (!x86_pmu.intel_cap.pebs_trap) + return 1; + if (!cpuc->lbr_stack.nr || !from || !to) return 0; @@ -589,34 +597,26 @@ static void intel_ds_init(void) x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); x86_pmu.pebs = boot_cpu_has(X86_FEATURE_PEBS); if (x86_pmu.pebs) { - int format = 0; - - if (x86_pmu.version > 1) { - u64 capabilities; - /* - * v2+ has a PEBS format field - */ - rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); - format = (capabilities >> 8) & 0xf; - } + char pebs_type = x86_pmu.intel_cap.pebs_trap ? '+' : '-'; + int format = x86_pmu.intel_cap.pebs_format; switch (format) { case 0: - printk(KERN_CONT "PEBS v0, "); + printk(KERN_CONT "PEBS fmt0%c, ", pebs_type); x86_pmu.pebs_record_size = sizeof(struct pebs_record_core); x86_pmu.drain_pebs = intel_pmu_drain_pebs_core; x86_pmu.pebs_constraints = intel_core_pebs_events; break; case 1: - printk(KERN_CONT "PEBS v1, "); + printk(KERN_CONT "PEBS fmt1%c, ", pebs_type); x86_pmu.pebs_record_size = sizeof(struct pebs_record_nhm); x86_pmu.drain_pebs = intel_pmu_drain_pebs_nhm; x86_pmu.pebs_constraints = intel_nehalem_pebs_events; break; default: - printk(KERN_CONT "PEBS unknown format: %d, ", format); + printk(KERN_CONT "no PEBS fmt%d%c, ", format, pebs_type); x86_pmu.pebs = 0; break; } diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index ea3e99e..4f3a124 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -53,7 +53,7 @@ static void intel_pmu_lbr_reset_64(void) static void intel_pmu_lbr_reset(void) { - if (x86_pmu.lbr_format == LBR_FORMAT_32) + if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32) intel_pmu_lbr_reset_32(); else intel_pmu_lbr_reset_64(); @@ -155,6 +155,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) { unsigned long mask = x86_pmu.lbr_nr - 1; + int lbr_format = x86_pmu.intel_cap.lbr_format; u64 tos = intel_pmu_lbr_tos(); int i; @@ -165,7 +166,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) rdmsrl(x86_pmu.lbr_from + lbr_idx, from); rdmsrl(x86_pmu.lbr_to + lbr_idx, to); - if (x86_pmu.lbr_format == LBR_FORMAT_EIP_FLAGS) { + if (lbr_format == LBR_FORMAT_EIP_FLAGS) { flags = !!(from & LBR_FROM_FLAG_MISPRED); from = (u64)((((s64)from) << 1) >> 1); } @@ -184,23 +185,14 @@ static void intel_pmu_lbr_read(void) if (!cpuc->lbr_users) return; - if (x86_pmu.lbr_format == LBR_FORMAT_32) + if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32) intel_pmu_lbr_read_32(cpuc); else intel_pmu_lbr_read_64(cpuc); } -static int intel_pmu_lbr_format(void) -{ - u64 capabilities; - - rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); - return capabilities & 0x1f; -} - static void intel_pmu_lbr_init_core(void) { - x86_pmu.lbr_format = intel_pmu_lbr_format(); x86_pmu.lbr_nr = 4; x86_pmu.lbr_tos = 0x01c9; x86_pmu.lbr_from = 0x40; @@ -209,7 +201,6 @@ static void intel_pmu_lbr_init_core(void) static void intel_pmu_lbr_init_nhm(void) { - x86_pmu.lbr_format = intel_pmu_lbr_format(); x86_pmu.lbr_nr = 16; x86_pmu.lbr_tos = 0x01c9; x86_pmu.lbr_from = 0x680; @@ -218,7 +209,6 @@ static void intel_pmu_lbr_init_nhm(void) static void intel_pmu_lbr_init_atom(void) { - x86_pmu.lbr_format = intel_pmu_lbr_format(); x86_pmu.lbr_nr = 8; x86_pmu.lbr_tos = 0x01c9; x86_pmu.lbr_from = 0x40; -- cgit v0.10.2 From 7e1a40dda619b0483fbe0740494ed2c2a1f05289 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Mar 2010 12:38:03 +0100 Subject: perf, x86: Expose the full PEBS record using PERF_SAMPLE_RAW Expose the full PEBS record using PERF_SAMPLE_RAW Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.847218224@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 5e40294..ef56f05 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -457,6 +457,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) struct perf_event *event = cpuc->events[0]; /* PMC0 only */ struct pebs_record_core *at, *top; struct perf_sample_data data; + struct perf_raw_record raw; struct pt_regs regs; int n; @@ -479,6 +480,12 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; + if (event->attr.sample_type & PERF_SAMPLE_RAW) { + raw.size = x86_pmu.pebs_record_size; + raw.data = at; + data.raw = &raw; + } + n = top - at; /* @@ -521,6 +528,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) struct pebs_record_nhm *at, *top; struct perf_sample_data data; struct perf_event *event = NULL; + struct perf_raw_record raw; struct pt_regs regs; int bit, n; @@ -562,6 +570,12 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; + if (event->attr.sample_type & PERF_SAMPLE_RAW) { + raw.size = x86_pmu.pebs_record_size; + raw.data = at; + data.raw = &raw; + } + /* * See the comment in intel_pmu_drain_pebs_core() */ -- cgit v0.10.2 From 30a813ae035d3e220a89609adce878e045c49547 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Mar 2010 13:49:21 +0100 Subject: x86: Move MAX_INSN_SIZE into asm/insn.h Since there's now two users for this, place it in a common header. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100304140100.923774125@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h index 96c2e0a..88c765e 100644 --- a/arch/x86/include/asm/insn.h +++ b/arch/x86/include/asm/insn.h @@ -68,6 +68,8 @@ struct insn { const insn_byte_t *next_byte; }; +#define MAX_INSN_SIZE 16 + #define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) #define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) #define X86_MODRM_RM(modrm) ((modrm) & 0x07) diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h index 4ffa345..5478825 100644 --- a/arch/x86/include/asm/kprobes.h +++ b/arch/x86/include/asm/kprobes.h @@ -24,6 +24,7 @@ #include #include #include +#include #define __ARCH_WANT_KPROBES_INSN_SLOT @@ -36,7 +37,6 @@ typedef u8 kprobe_opcode_t; #define RELATIVEJUMP_SIZE 5 #define RELATIVECALL_OPCODE 0xe8 #define RELATIVE_ADDR_SIZE 4 -#define MAX_INSN_SIZE 16 #define MAX_STACK_SIZE 64 #define MIN_STACK_SIZE(ADDR) \ (((MAX_STACK_SIZE) < (((unsigned long)current_thread_info()) + \ diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index ef56f05..72453ac 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -379,8 +379,6 @@ static void intel_pmu_pebs_disable_all(void) #include -#define MAX_INSN_SIZE 16 - static inline bool kernel_ip(unsigned long ip) { #ifdef CONFIG_X86_32 -- cgit v0.10.2 From 3adaebd69557615c1bf0365ce5e32d93ac7d82af Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 5 Mar 2010 12:09:29 +0100 Subject: perf, x86: Fix silly bug in data store buffer allocation Fix up the ds allocation error path, where we could free @buffer before we used it. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100305154128.813452402@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 72453ac..a67fff1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -127,10 +127,8 @@ static int reserve_ds_buffers(void) err = -ENOMEM; ds = kzalloc(sizeof(*ds), GFP_KERNEL); - if (unlikely(!ds)) { - kfree(buffer); + if (unlikely(!ds)) break; - } per_cpu(cpu_hw_events, cpu).ds = ds; if (x86_pmu.bts) { -- cgit v0.10.2 From 3c44780b220e876b01e39d4028cd6f4205fbf5d6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 4 Mar 2010 21:49:01 +0100 Subject: perf, x86: Disable PEBS on clovertown chips This CPU has just too many handycaps to be really useful. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100305154128.890278662@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 7b5430b..335ee1d 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -197,6 +197,7 @@ struct x86_pmu { void (*put_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); struct event_constraint *event_constraints; + void (*quirks)(void); void (*cpu_prepare)(int cpu); void (*cpu_starting)(int cpu); @@ -1373,6 +1374,9 @@ void __init init_hw_perf_events(void) pr_cont("%s PMU driver.\n", x86_pmu.name); + if (x86_pmu.quirks) + x86_pmu.quirks(); + if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) { WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!", x86_pmu.num_events, X86_PMC_MAX_GENERIC); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 246c072..224c952 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -792,6 +792,32 @@ static __initconst struct x86_pmu intel_pmu = { .cpu_dying = fini_debug_store_on_cpu, }; +static void intel_clovertown_quirks(void) +{ + /* + * PEBS is unreliable due to: + * + * AJ67 - PEBS may experience CPL leaks + * AJ68 - PEBS PMI may be delayed by one event + * AJ69 - GLOBAL_STATUS[62] will only be set when DEBUGCTL[12] + * AJ106 - FREEZE_LBRS_ON_PMI doesn't work in combination with PEBS + * + * AJ67 could be worked around by restricting the OS/USR flags. + * AJ69 could be worked around by setting PMU_FREEZE_ON_PMI. + * + * AJ106 could possibly be worked around by not allowing LBR + * usage from PEBS, including the fixup. + * AJ68 could possibly be worked around by always programming + * a pebs_event_reset[0] value and coping with the lost events. + * + * But taken together it might just make sense to not enable PEBS on + * these chips. + */ + printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); + x86_pmu.pebs = 0; + x86_pmu.pebs_constraints = NULL; +} + static __init int intel_pmu_init(void) { union cpuid10_edx edx; @@ -856,6 +882,7 @@ static __init int intel_pmu_init(void) break; case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ + x86_pmu.quirks = intel_clovertown_quirks; case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ case 29: /* six-core 45 nm xeon "Dunnington" */ -- cgit v0.10.2 From 74846d35b24b6efd61bb88a0a750b6bb257e6e78 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 5 Mar 2010 13:49:35 +0100 Subject: perf, x86: Clear the LBRs on init Some CPUs have errata where the LBR is not cleared on Power-On. So always clear the LBRs before use. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100305154128.966563424@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 224c952..c135ed7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -767,6 +767,20 @@ static __initconst struct x86_pmu core_pmu = { .event_constraints = intel_core_event_constraints, }; +static void intel_pmu_cpu_starting(int cpu) +{ + init_debug_store_on_cpu(cpu); + /* + * Deal with CPUs that don't clear their LBRs on power-up. + */ + intel_pmu_lbr_reset(); +} + +static void intel_pmu_cpu_dying(int cpu) +{ + fini_debug_store_on_cpu(cpu); +} + static __initconst struct x86_pmu intel_pmu = { .name = "Intel", .handle_irq = intel_pmu_handle_irq, @@ -788,8 +802,8 @@ static __initconst struct x86_pmu intel_pmu = { .max_period = (1ULL << 31) - 1, .get_event_constraints = intel_get_event_constraints, - .cpu_starting = init_debug_store_on_cpu, - .cpu_dying = fini_debug_store_on_cpu, + .cpu_starting = intel_pmu_cpu_starting, + .cpu_dying = intel_pmu_cpu_dying, }; static void intel_clovertown_quirks(void) diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 4f3a124..dcec765 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -53,6 +53,9 @@ static void intel_pmu_lbr_reset_64(void) static void intel_pmu_lbr_reset(void) { + if (!x86_pmu.lbr_nr) + return; + if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_32) intel_pmu_lbr_reset_32(); else -- cgit v0.10.2 From a562b1871f7f7d2f3a835c3c1e07fa58d473cfb7 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 5 Mar 2010 16:29:14 +0100 Subject: perf, x86: Robustify PEBS fixup It turns out the LBR is massively unreliable on certain CPUs, so code the fixup a little more defensive to avoid crashing the kernel. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: <20100305154129.042271287@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index a67fff1..e7ac517 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -399,10 +399,23 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) if (!x86_pmu.intel_cap.pebs_trap) return 1; + /* + * No LBR entry, no basic block, no rewinding + */ if (!cpuc->lbr_stack.nr || !from || !to) return 0; - if (ip < to) + /* + * Basic blocks should never cross user/kernel boundaries + */ + if (kernel_ip(ip) != kernel_ip(to)) + return 0; + + /* + * unsigned math, either ip is before the start (impossible) or + * the basic block is larger than 1 page (sanity) + */ + if ((ip - to) > PAGE_SIZE) return 0; /* @@ -420,7 +433,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) old_to = to; if (!kernel_ip(ip)) { - int bytes, size = min_t(int, MAX_INSN_SIZE, ip - to); + int bytes, size = MAX_INSN_SIZE; bytes = copy_from_user_nmi(buf, (void __user *)to, size); if (bytes != size) @@ -440,6 +453,10 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) return 1; } + /* + * Even though we decoded the basic block, the instruction stream + * never matched the given IP, either the TO or the IP got corrupted. + */ return 0; } -- cgit v0.10.2 From cc7f00820b2f3be656569c41158d9323e425bcfe Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 8 Mar 2010 17:51:33 +0100 Subject: perf, x86: Avoid double disable on throttle vs ioctl(PERF_IOC_DISABLE) Calling ioctl(PERF_EVENT_IOC_DISABLE) on a thottled counter would result in a double disable, cure this by using x86_pmu_{start,stop} for throttle/unthrottle and teach x86_pmu_stop() to check ->active_mask. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index e7ac517..a7401e4 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -461,7 +461,6 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) } static int intel_pmu_save_and_restart(struct perf_event *event); -static void intel_pmu_disable_event(struct perf_event *event); static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) { @@ -528,7 +527,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) regs.flags &= ~PERF_EFLAGS_EXACT; if (perf_event_overflow(event, 1, &data, ®s)) - intel_pmu_disable_event(event); + x86_pmu_stop(event); out: intel_pmu_pebs_enable_all(); @@ -603,7 +602,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) regs.flags &= ~PERF_EFLAGS_EXACT; if (perf_event_overflow(event, 1, &data, ®s)) - intel_pmu_disable_event(event); + x86_pmu_stop(event); } out: intel_pmu_pebs_enable_all(); -- cgit v0.10.2 From 8f4aebd2be9892bf8fb79a2d8576d3f3ee7f00f6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 6 Mar 2010 13:26:11 +0100 Subject: perf, x86: Fix pebs drains I overlooked the perf_disable()/perf_enable() calls in intel_pmu_handle_irq(), (pointed out by Markus) so we should not explicitly disable_all/enable_all pebs counters in the drain functions, these are already disabled and enabling them early is confusing. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index a7401e4..66c6962 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -476,18 +476,16 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) if (!event || !ds || !x86_pmu.pebs) return; - intel_pmu_pebs_disable_all(); - at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base; top = (struct pebs_record_core *)(unsigned long)ds->pebs_index; if (top <= at) - goto out; + return; ds->pebs_index = ds->pebs_buffer_base; if (!intel_pmu_save_and_restart(event)) - goto out; + return; perf_sample_data_init(&data, 0); data.period = event->hw.last_period; @@ -528,9 +526,6 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) if (perf_event_overflow(event, 1, &data, ®s)) x86_pmu_stop(event); - -out: - intel_pmu_pebs_enable_all(); } static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) @@ -547,13 +542,11 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) if (!ds || !x86_pmu.pebs) return; - intel_pmu_pebs_disable_all(); - at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; if (top <= at) - goto out; + return; ds->pebs_index = ds->pebs_buffer_base; @@ -604,8 +597,6 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) if (perf_event_overflow(event, 1, &data, ®s)) x86_pmu_stop(event); } -out: - intel_pmu_pebs_enable_all(); } /* -- cgit v0.10.2 From 4807e3d5dc7bb7057dd6ca3abb09f3da2eb8c323 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 6 Mar 2010 13:47:07 +0100 Subject: perf, x86: Fix PEBS enable/disable vs cpuc->enabled We should never call ->enable with the pmu enabled, and we _can_ have ->disable called with the pmu enabled. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 66c6962..9ad0e67 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -338,7 +338,7 @@ static void intel_pmu_pebs_enable(struct perf_event *event) hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; val |= 1ULL << hwc->idx; - wrmsrl(MSR_IA32_PEBS_ENABLE, val); + WARN_ON_ONCE(cpuc->enabled); if (x86_pmu.intel_cap.pebs_trap) intel_pmu_lbr_enable(event); @@ -351,7 +351,8 @@ static void intel_pmu_pebs_disable(struct perf_event *event) u64 val = cpuc->pebs_enabled; val &= ~(1ULL << hwc->idx); - wrmsrl(MSR_IA32_PEBS_ENABLE, val); + if (cpuc->enabled) + wrmsrl(MSR_IA32_PEBS_ENABLE, val); hwc->config |= ARCH_PERFMON_EVENTSEL_INT; -- cgit v0.10.2 From 2df202bf7520eaffcbfb07e45dfa3cfb0aeee2c0 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 6 Mar 2010 13:48:54 +0100 Subject: perf, x86: Fix LBR enable/disable vs cpuc->enabled We should never call ->enable with the pmu enabled, and we _can_ have ->disable called with the pmu enabled. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index dcec765..0145f99 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -69,7 +69,7 @@ static void intel_pmu_lbr_enable(struct perf_event *event) if (!x86_pmu.lbr_nr) return; - WARN_ON(cpuc->enabled); + WARN_ON_ONCE(cpuc->enabled); /* * Reset the LBR stack if this is the first LBR user or @@ -93,9 +93,10 @@ static void intel_pmu_lbr_disable(struct perf_event *event) return; cpuc->lbr_users--; - BUG_ON(cpuc->lbr_users < 0); - WARN_ON(cpuc->enabled); + + if (cpuc->enabled && !cpuc->lbr_users) + __intel_pmu_lbr_disable(); } static void intel_pmu_lbr_enable_all(void) -- cgit v0.10.2 From d329527e47851f84b1e7944ed9601205f35f1b93 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 8 Mar 2010 13:57:14 +0100 Subject: perf, x86: Reorder intel_pmu_enable_all() The documentation says we have to enable PEBS before we enable the PMU proper. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index c135ed7..d3e2424 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -487,6 +487,8 @@ static void intel_pmu_enable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + intel_pmu_pebs_enable_all(); + intel_pmu_lbr_enable_all(); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { @@ -498,9 +500,6 @@ static void intel_pmu_enable_all(void) intel_pmu_enable_bts(event->hw.config); } - - intel_pmu_pebs_enable_all(); - intel_pmu_lbr_enable_all(); } static inline u64 intel_pmu_get_status(void) -- cgit v0.10.2 From 12ab854d744f04bfc5c6c4db723b7e31fc03eb29 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 6 Mar 2010 18:57:38 +0100 Subject: perf, x86: Deal with multiple state bits for pebs-fmt1 Its unclear if the PEBS state record will have only a single bit set, in case it does not and accumulates bits, deal with that by only processing each event once. Also, robustify some of the code. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 9ad0e67..b4680da 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -538,6 +538,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) struct perf_event *event = NULL; struct perf_raw_record raw; struct pt_regs regs; + u64 status = 0; int bit, n; if (!ds || !x86_pmu.pebs) @@ -561,13 +562,22 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) for ( ; at < top; at++) { for_each_bit(bit, (unsigned long *)&at->status, MAX_PEBS_EVENTS) { - if (!cpuc->events[bit]->attr.precise) + event = cpuc->events[bit]; + if (!test_bit(bit, cpuc->active_mask)) continue; - event = cpuc->events[bit]; + WARN_ON_ONCE(!event); + + if (!event->attr.precise) + continue; + + if (__test_and_set_bit(bit, (unsigned long *)&status)) + continue; + + break; } - if (!event) + if (!event || bit >= MAX_PEBS_EVENTS) continue; if (!intel_pmu_save_and_restart(event)) -- cgit v0.10.2 From ad0e6cfe2a2a61d7b5530188e571d508146cb43b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sat, 6 Mar 2010 19:49:06 +0100 Subject: perf, x86: Fix silly bug in intel_pmu_pebs_{enable,disable} We need to use the actual cpuc->pebs_enabled value, not a local copy for the changes to take effect. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index b4680da..2423694 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -333,11 +333,10 @@ static void intel_pmu_pebs_enable(struct perf_event *event) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct hw_perf_event *hwc = &event->hw; - u64 val = cpuc->pebs_enabled; hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; - val |= 1ULL << hwc->idx; + cpuc->pebs_enabled |= 1ULL << hwc->idx; WARN_ON_ONCE(cpuc->enabled); if (x86_pmu.intel_cap.pebs_trap) @@ -348,11 +347,10 @@ static void intel_pmu_pebs_disable(struct perf_event *event) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct hw_perf_event *hwc = &event->hw; - u64 val = cpuc->pebs_enabled; - val &= ~(1ULL << hwc->idx); + cpuc->pebs_enabled &= ~(1ULL << hwc->idx); if (cpuc->enabled) - wrmsrl(MSR_IA32_PEBS_ENABLE, val); + wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); hwc->config |= ARCH_PERFMON_EVENTSEL_INT; -- cgit v0.10.2 From b83a46e7da4a948cc852ba7805dfb1a392dec861 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 8 Mar 2010 13:51:12 +0100 Subject: perf, x86: Don't reset the LBR as frequently If we reset the LBR on each first counter, simple counter rotation which first deschedules all counters and then reschedules the new ones will lead to LBR reset, even though we're still in the same task context. Reduce this by not flushing on the first counter but only flushing on different task contexts. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 0145f99..f278136 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -72,12 +72,11 @@ static void intel_pmu_lbr_enable(struct perf_event *event) WARN_ON_ONCE(cpuc->enabled); /* - * Reset the LBR stack if this is the first LBR user or - * we changed task context so as to avoid data leaks. + * Reset the LBR stack if we changed task context to + * avoid data leaks. */ - if (!cpuc->lbr_users || - (event->ctx->task && cpuc->lbr_context != event->ctx)) { + if (event->ctx->task && cpuc->lbr_context != event->ctx) { intel_pmu_lbr_reset(); cpuc->lbr_context = event->ctx; } @@ -93,7 +92,7 @@ static void intel_pmu_lbr_disable(struct perf_event *event) return; cpuc->lbr_users--; - BUG_ON(cpuc->lbr_users < 0); + WARN_ON_ONCE(cpuc->lbr_users < 0); if (cpuc->enabled && !cpuc->lbr_users) __intel_pmu_lbr_disable(); -- cgit v0.10.2 From 7645a24cbd01cbf4865d1273d5ddaa8d8c2ccb3a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 8 Mar 2010 13:51:31 +0100 Subject: perf, x86: Remove checking_{wr,rd}msr() usage We don't need checking_{wr,rd}msr() calls, since we should know what cpu we're running on and not use blindly poke at msrs. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 335ee1d..e24f637 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -29,6 +29,17 @@ #include #include +#if 0 +#undef wrmsrl +#define wrmsrl(msr, val) \ +do { \ + trace_printk("wrmsrl(%lx, %lx)\n", (unsigned long)(msr),\ + (unsigned long)(val)); \ + native_write_msr((msr), (u32)((u64)(val)), \ + (u32)((u64)(val) >> 32)); \ +} while (0) +#endif + /* * best effort, GUP based copy_from_user() that assumes IRQ or NMI context */ @@ -821,14 +832,15 @@ void hw_perf_enable(void) static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc) { - (void)checking_wrmsrl(hwc->config_base + hwc->idx, + wrmsrl(hwc->config_base + hwc->idx, hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE); } static inline void x86_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - (void)checking_wrmsrl(hwc->config_base + hwc->idx, hwc->config); + + wrmsrl(hwc->config_base + hwc->idx, hwc->config); } static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); @@ -843,7 +855,7 @@ x86_perf_event_set_period(struct perf_event *event) struct hw_perf_event *hwc = &event->hw; s64 left = atomic64_read(&hwc->period_left); s64 period = hwc->sample_period; - int err, ret = 0, idx = hwc->idx; + int ret = 0, idx = hwc->idx; if (idx == X86_PMC_IDX_FIXED_BTS) return 0; @@ -881,8 +893,8 @@ x86_perf_event_set_period(struct perf_event *event) */ atomic64_set(&hwc->prev_count, (u64)-left); - err = checking_wrmsrl(hwc->event_base + idx, - (u64)(-left) & x86_pmu.event_mask); + wrmsrl(hwc->event_base + idx, + (u64)(-left) & x86_pmu.event_mask); perf_event_update_userpage(event); @@ -987,7 +999,7 @@ void perf_event_print_debug(void) pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs); } - pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); + pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); for (idx = 0; idx < x86_pmu.num_events; idx++) { rdmsrl(x86_pmu.eventsel + idx, pmc_ctrl); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index d3e2424..971dc6e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -525,7 +525,7 @@ static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) rdmsrl(hwc->config_base, ctrl_val); ctrl_val &= ~mask; - (void)checking_wrmsrl(hwc->config_base, ctrl_val); + wrmsrl(hwc->config_base, ctrl_val); } static void intel_pmu_disable_event(struct perf_event *event) @@ -553,7 +553,6 @@ static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) { int idx = hwc->idx - X86_PMC_IDX_FIXED; u64 ctrl_val, bits, mask; - int err; /* * Enable IRQ generation (0x8), @@ -578,7 +577,7 @@ static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) rdmsrl(hwc->config_base, ctrl_val); ctrl_val &= ~mask; ctrl_val |= bits; - err = checking_wrmsrl(hwc->config_base, ctrl_val); + wrmsrl(hwc->config_base, ctrl_val); } static void intel_pmu_enable_event(struct perf_event *event) -- cgit v0.10.2 From d80c7502ff63aa0d99d8c0c5803d28bbef67a74e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 9 Mar 2010 11:41:02 +0100 Subject: perf, x86: Fixup the PEBS handler for Core2 cpus Pull the core handler in line with the nhm one, also make sure we always drain the buffer. Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 2423694..1bfd59b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -472,20 +472,39 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) struct pt_regs regs; int n; - if (!event || !ds || !x86_pmu.pebs) + if (!ds || !x86_pmu.pebs) return; at = (struct pebs_record_core *)(unsigned long)ds->pebs_buffer_base; top = (struct pebs_record_core *)(unsigned long)ds->pebs_index; - if (top <= at) + /* + * Whatever else happens, drain the thing + */ + ds->pebs_index = ds->pebs_buffer_base; + + if (!test_bit(0, cpuc->active_mask)) return; - ds->pebs_index = ds->pebs_buffer_base; + WARN_ON_ONCE(!event); + + if (!event->attr.precise) + return; + + n = top - at; + if (n <= 0) + return; if (!intel_pmu_save_and_restart(event)) return; + /* + * Should not happen, we program the threshold at 1 and do not + * set a reset value. + */ + WARN_ON_ONCE(n > 1); + at += n - 1; + perf_sample_data_init(&data, 0); data.period = event->hw.last_period; @@ -495,14 +514,6 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) data.raw = &raw; } - n = top - at; - - /* - * Should not happen, we program the threshold at 1 and do not - * set a reset value. - */ - WARN_ON_ONCE(n > 1); - /* * We use the interrupt regs as a base because the PEBS record * does not contain a full regs set, specifically it seems to @@ -545,12 +556,11 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) at = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base; top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index; - if (top <= at) - return; - ds->pebs_index = ds->pebs_buffer_base; n = top - at; + if (n <= 0) + return; /* * Should not happen, we program the threshold at 1 and do not -- cgit v0.10.2 From 63fb3f9b2312e131be5a0a2dddb63f2fb123db9b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 9 Mar 2010 11:51:02 +0100 Subject: perf, x86: Fix LBR read-out Don't decrement the TOS twice... Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: paulus@samba.org Cc: eranian@google.com Cc: robert.richter@amd.com Cc: fweisbec@gmail.com LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index f278136..df4c98e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -129,7 +129,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) u64 tos = intel_pmu_lbr_tos(); int i; - for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) { + for (i = 0; i < x86_pmu.lbr_nr; i++) { unsigned long lbr_idx = (tos - i) & mask; union { struct { @@ -162,7 +162,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) u64 tos = intel_pmu_lbr_tos(); int i; - for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) { + for (i = 0; i < x86_pmu.lbr_nr; i++) { unsigned long lbr_idx = (tos - i) & mask; u64 from, to, flags = 0; -- cgit v0.10.2 From ba7e4d13fc7e25af1d167d40e6f028298dfc55ad Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 6 Jun 2009 13:58:12 +0200 Subject: perf, x86: Add INSTRUCTION_DECODER config flag The PEBS+LBR decoding magic needs the insn_get_length() infrastructure to be able to decode x86 instruction length. So split it out of KPROBES dependency and make it enabled when either KPROBES or PERF_EVENTS is enabled. Cc: Peter Zijlstra Cc: Masami Hiramatsu Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e984403..e1240f6 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -58,6 +58,9 @@ config X86 select HAVE_ARCH_KMEMCHECK select HAVE_USER_RETURN_NOTIFIER +config INSTRUCTION_DECODER + def_bool (KPROBES || PERF_EVENTS) + config OUTPUT_FORMAT string default "elf32-i386" if X86_32 diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 419386c..cbaf8f2 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -20,7 +20,7 @@ lib-y := delay.o lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o getuser.o putuser.o lib-y += memcpy_$(BITS).o -lib-$(CONFIG_KPROBES) += insn.o inat.o +lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o obj-y += msr.o msr-reg.o msr-reg-export.o -- cgit v0.10.2 From caa0142d84ceb0fc83e28f0475d0a7316cb6df77 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 6 Jun 2009 13:58:12 +0200 Subject: perf, x86: Fix the !CONFIG_CPU_SUP_INTEL build Fix typo. But the modularization here is ugly and should be improved. Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 1bfd59b..c59678a 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -661,7 +661,7 @@ static void intel_ds_init(void) #else /* CONFIG_CPU_SUP_INTEL */ -static int reseve_ds_buffers(void) +static int reserve_ds_buffers(void) { return 0; } -- cgit v0.10.2 From 9b33fa6ba0e2f90fdf407501db801c2511121564 Mon Sep 17 00:00:00 2001 From: "eranian@google.com" Date: Wed, 10 Mar 2010 22:26:05 -0800 Subject: perf_events: Improve task_sched_in() This patch is an optimization in perf_event_task_sched_in() to avoid scheduling the events twice in a row. Without it, the perf_disable()/perf_enable() pair is invoked twice, thereby pinned events counts while scheduling flexible events and we go throuh hw_perf_enable() twice. By encapsulating, the whole sequence into perf_disable()/perf_enable() we ensure, hw_perf_enable() is going to be invoked only once because of the refcount protection. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <1268288765-5326-1-git-send-email-eranian@google.com> Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 52c69a3..3853d49 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -1368,6 +1368,8 @@ void perf_event_task_sched_in(struct task_struct *task) if (cpuctx->task_ctx == ctx) return; + perf_disable(); + /* * We want to keep the following priority order: * cpu pinned (that don't need to move), task pinned, @@ -1380,6 +1382,8 @@ void perf_event_task_sched_in(struct task_struct *task) ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE); cpuctx->task_ctx = ctx; + + perf_enable(); } #define MAX_INTERRUPTS (~0ULL) -- cgit v0.10.2 From a072738e04f0eb26370e39ec679e9a0d65e49aea Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 11 Mar 2010 19:54:39 +0300 Subject: perf, x86: Implement initial P4 PMU driver The netburst PMU is way different from the "architectural perfomance monitoring" specification that current CPUs use. P4 uses a tuple of ESCR+CCCR+COUNTER MSR registers to handle perfomance monitoring events. A few implementational details: 1) We need a separate x86_pmu::hw_config helper in struct x86_pmu since register bit-fields are quite different from P6, Core and later cpu series. 2) For the same reason is a x86_pmu::schedule_events helper introduced. 3) hw_perf_event::config consists of packed ESCR+CCCR values. It's allowed since in reality both registers only use a half of their size. Of course before making a real write into a particular MSR we need to unpack the value and extend it to a proper size. 4) The tuple of packed ESCR+CCCR in hw_perf_event::config doesn't describe the memory address of ESCR MSR register so that we need to keep a mapping between these tuples used and available ESCR (various P4 events may use same ESCRs but not simultaneously), for this sake every active event has a per-cpu map of hw_perf_event::idx <--> ESCR addresses. 5) Since hw_perf_event::idx is an offset to counter/control register we need to lift X86_PMC_MAX_GENERIC up, otherwise kernel strips it down to 8 registers and event armed may never be turned off (ie the bit in active_mask is set but the loop never reaches this index to check), thanks to Peter Zijlstra Restrictions: - No cascaded counters support (do we ever need them?) - No dependent events support (so PERF_COUNT_HW_INSTRUCTIONS doesn't work for now) - There are events with same counters which can't work simultaneously (need to use intersected ones due to broken counter 1) - No PERF_COUNT_HW_CACHE_ events yet Todo: - Implement dependent events - Need proper hashing for event opcodes (no linear search, good for debugging stage but not in real loads) - Some events counted during a clock cycle -- need to set threshold for them and count every clock cycle just to get summary statistics (ie to behave the same way as other PMUs do) - Need to swicth to use event_constraints - To support RAW events we need to encode a global list of P4 events into p4_templates - Cache events need to be added Event support status matrix: Event status ----------------------------- cycles works cache-references works cache-misses works branch-misses works bus-cycles partially (does not work on 64bit cpu with HT enabled) instruction doesnt work (needs dependent event [mop tagging]) branches doesnt work Signed-off-by: Cyrill Gorcunov Signed-off-by: Lin Ming Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Cc: Robert Richter Cc: Frederic Weisbecker LKML-Reference: <20100311165439.GB5129@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index a9038c9..124dddd 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -5,7 +5,7 @@ * Performance event hw details: */ -#define X86_PMC_MAX_GENERIC 8 +#define X86_PMC_MAX_GENERIC 32 #define X86_PMC_MAX_FIXED 3 #define X86_PMC_IDX_GENERIC 0 diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h new file mode 100644 index 0000000..829f471 --- /dev/null +++ b/arch/x86/include/asm/perf_event_p4.h @@ -0,0 +1,707 @@ +/* + * Netburst Perfomance Events (P4, old Xeon) + */ + +#ifndef PERF_EVENT_P4_H +#define PERF_EVENT_P4_H + +#include +#include + +/* + * NetBurst has perfomance MSRs shared between + * threads if HT is turned on, ie for both logical + * processors (mem: in turn in Atom with HT support + * perf-MSRs are not shared and every thread has its + * own perf-MSRs set) + */ +#define ARCH_P4_TOTAL_ESCR (46) +#define ARCH_P4_RESERVED_ESCR (2) /* IQ_ESCR(0,1) not always present */ +#define ARCH_P4_MAX_ESCR (ARCH_P4_TOTAL_ESCR - ARCH_P4_RESERVED_ESCR) +#define ARCH_P4_MAX_CCCR (18) +#define ARCH_P4_MAX_COUNTER (ARCH_P4_MAX_CCCR / 2) + +#define P4_EVNTSEL_EVENT_MASK 0x7e000000U +#define P4_EVNTSEL_EVENT_SHIFT 25 +#define P4_EVNTSEL_EVENTMASK_MASK 0x01fffe00U +#define P4_EVNTSEL_EVENTMASK_SHIFT 9 +#define P4_EVNTSEL_TAG_MASK 0x000001e0U +#define P4_EVNTSEL_TAG_SHIFT 5 +#define P4_EVNTSEL_TAG_ENABLE 0x00000010U +#define P4_EVNTSEL_T0_OS 0x00000008U +#define P4_EVNTSEL_T0_USR 0x00000004U +#define P4_EVNTSEL_T1_OS 0x00000002U +#define P4_EVNTSEL_T1_USR 0x00000001U + +/* Non HT mask */ +#define P4_EVNTSEL_MASK \ + (P4_EVNTSEL_EVENT_MASK | \ + P4_EVNTSEL_EVENTMASK_MASK | \ + P4_EVNTSEL_TAG_MASK | \ + P4_EVNTSEL_TAG_ENABLE | \ + P4_EVNTSEL_T0_OS | \ + P4_EVNTSEL_T0_USR) + +/* HT mask */ +#define P4_EVNTSEL_MASK_HT \ + (P4_EVNTSEL_MASK | \ + P4_EVNTSEL_T1_OS | \ + P4_EVNTSEL_T1_USR) + +#define P4_CCCR_OVF 0x80000000U +#define P4_CCCR_CASCADE 0x40000000U +#define P4_CCCR_OVF_PMI_T0 0x04000000U +#define P4_CCCR_OVF_PMI_T1 0x08000000U +#define P4_CCCR_FORCE_OVF 0x02000000U +#define P4_CCCR_EDGE 0x01000000U +#define P4_CCCR_THRESHOLD_MASK 0x00f00000U +#define P4_CCCR_THRESHOLD_SHIFT 20 +#define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) +#define P4_CCCR_COMPLEMENT 0x00080000U +#define P4_CCCR_COMPARE 0x00040000U +#define P4_CCCR_ESCR_SELECT_MASK 0x0000e000U +#define P4_CCCR_ESCR_SELECT_SHIFT 13 +#define P4_CCCR_ENABLE 0x00001000U +#define P4_CCCR_THREAD_SINGLE 0x00010000U +#define P4_CCCR_THREAD_BOTH 0x00020000U +#define P4_CCCR_THREAD_ANY 0x00030000U + +/* Non HT mask */ +#define P4_CCCR_MASK \ + (P4_CCCR_OVF | \ + P4_CCCR_CASCADE | \ + P4_CCCR_OVF_PMI_T0 | \ + P4_CCCR_FORCE_OVF | \ + P4_CCCR_EDGE | \ + P4_CCCR_THRESHOLD_MASK | \ + P4_CCCR_COMPLEMENT | \ + P4_CCCR_COMPARE | \ + P4_CCCR_ESCR_SELECT_MASK | \ + P4_CCCR_ENABLE) + +/* HT mask */ +#define P4_CCCR_MASK_HT \ + (P4_CCCR_MASK | \ + P4_CCCR_THREAD_ANY) + +/* + * format is 32 bit: ee ss aa aa + * where + * ee - 8 bit event + * ss - 8 bit selector + * aa aa - 16 bits reserved for tags/attributes + */ +#define P4_EVENT_PACK(event, selector) (((event) << 24) | ((selector) << 16)) +#define P4_EVENT_UNPACK_EVENT(packed) (((packed) >> 24) & 0xff) +#define P4_EVENT_UNPACK_SELECTOR(packed) (((packed) >> 16) & 0xff) +#define P4_EVENT_PACK_ATTR(attr) ((attr)) +#define P4_EVENT_UNPACK_ATTR(packed) ((packed) & 0xffff) +#define P4_MAKE_EVENT_ATTR(class, name, bit) class##_##name = (1 << bit) +#define P4_EVENT_ATTR(class, name) class##_##name +#define P4_EVENT_ATTR_STR(class, name) __stringify(class##_##name) + +/* + * config field is 64bit width and consists of + * HT << 63 | ESCR << 32 | CCCR + * where HT is HyperThreading bit (since ESCR + * has it reserved we may use it for own purpose) + * + * note that this is NOT the addresses of respective + * ESCR and CCCR but rather an only packed value should + * be unpacked and written to a proper addresses + * + * the base idea is to pack as much info as + * possible + */ +#define p4_config_pack_escr(v) (((u64)(v)) << 32) +#define p4_config_pack_cccr(v) (((u64)(v)) & 0xffffffffULL) +#define p4_config_unpack_escr(v) (((u64)(v)) >> 32) +#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xffffffffULL) + +#define p4_config_unpack_emask(v) \ + ({ \ + u32 t = p4_config_unpack_escr((v)); \ + t &= P4_EVNTSEL_EVENTMASK_MASK; \ + t >>= P4_EVNTSEL_EVENTMASK_SHIFT; \ + t; \ + }) + +#define P4_CONFIG_HT_SHIFT 63 +#define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT) + +static inline u32 p4_config_unpack_opcode(u64 config) +{ + u32 e, s; + + /* + * we don't care about HT presence here since + * event opcode doesn't depend on it + */ + e = (p4_config_unpack_escr(config) & P4_EVNTSEL_EVENT_MASK) >> P4_EVNTSEL_EVENT_SHIFT; + s = (p4_config_unpack_cccr(config) & P4_CCCR_ESCR_SELECT_MASK) >> P4_CCCR_ESCR_SELECT_SHIFT; + + return P4_EVENT_PACK(e, s); +} + +static inline bool p4_is_event_cascaded(u64 config) +{ + u32 cccr = p4_config_unpack_cccr(config); + return !!(cccr & P4_CCCR_CASCADE); +} + +static inline int p4_ht_config_thread(u64 config) +{ + return !!(config & P4_CONFIG_HT); +} + +static inline u64 p4_set_ht_bit(u64 config) +{ + return config | P4_CONFIG_HT; +} + +static inline u64 p4_clear_ht_bit(u64 config) +{ + return config & ~P4_CONFIG_HT; +} + +static inline int p4_ht_active(void) +{ +#ifdef CONFIG_SMP + return smp_num_siblings > 1; +#endif + return 0; +} + +static inline int p4_ht_thread(int cpu) +{ +#ifdef CONFIG_SMP + if (smp_num_siblings == 2) + return cpu != cpumask_first(__get_cpu_var(cpu_sibling_map)); +#endif + return 0; +} + +static inline int p4_should_swap_ts(u64 config, int cpu) +{ + return p4_ht_config_thread(config) ^ p4_ht_thread(cpu); +} + +static inline u32 p4_default_cccr_conf(int cpu) +{ + /* + * Note that P4_CCCR_THREAD_ANY is "required" on + * non-HT machines (on HT machines we count TS events + * regardless the state of second logical processor + */ + u32 cccr = P4_CCCR_THREAD_ANY; + + if (!p4_ht_thread(cpu)) + cccr |= P4_CCCR_OVF_PMI_T0; + else + cccr |= P4_CCCR_OVF_PMI_T1; + + return cccr; +} + +static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) +{ + u32 escr = 0; + + if (!p4_ht_thread(cpu)) { + if (!exclude_os) + escr |= P4_EVNTSEL_T0_OS; + if (!exclude_usr) + escr |= P4_EVNTSEL_T0_USR; + } else { + if (!exclude_os) + escr |= P4_EVNTSEL_T1_OS; + if (!exclude_usr) + escr |= P4_EVNTSEL_T1_USR; + } + + return escr; +} + +/* + * Comments below the event represent ESCR restriction + * for this event and counter index per ESCR + * + * MSR_P4_IQ_ESCR0 and MSR_P4_IQ_ESCR1 are available only on early + * processor builds (family 0FH, models 01H-02H). These MSRs + * are not available on later versions, so that we don't use + * them completely + * + * Also note that CCCR1 do not have P4_CCCR_ENABLE bit properly + * working so that we should not use this CCCR and respective + * counter as result + */ +#define P4_TC_DELIVER_MODE P4_EVENT_PACK(0x01, 0x01) + /* + * MSR_P4_TC_ESCR0: 4, 5 + * MSR_P4_TC_ESCR1: 6, 7 + */ + +#define P4_BPU_FETCH_REQUEST P4_EVENT_PACK(0x03, 0x00) + /* + * MSR_P4_BPU_ESCR0: 0, 1 + * MSR_P4_BPU_ESCR1: 2, 3 + */ + +#define P4_ITLB_REFERENCE P4_EVENT_PACK(0x18, 0x03) + /* + * MSR_P4_ITLB_ESCR0: 0, 1 + * MSR_P4_ITLB_ESCR1: 2, 3 + */ + +#define P4_MEMORY_CANCEL P4_EVENT_PACK(0x02, 0x05) + /* + * MSR_P4_DAC_ESCR0: 8, 9 + * MSR_P4_DAC_ESCR1: 10, 11 + */ + +#define P4_MEMORY_COMPLETE P4_EVENT_PACK(0x08, 0x02) + /* + * MSR_P4_SAAT_ESCR0: 8, 9 + * MSR_P4_SAAT_ESCR1: 10, 11 + */ + +#define P4_LOAD_PORT_REPLAY P4_EVENT_PACK(0x04, 0x02) + /* + * MSR_P4_SAAT_ESCR0: 8, 9 + * MSR_P4_SAAT_ESCR1: 10, 11 + */ + +#define P4_STORE_PORT_REPLAY P4_EVENT_PACK(0x05, 0x02) + /* + * MSR_P4_SAAT_ESCR0: 8, 9 + * MSR_P4_SAAT_ESCR1: 10, 11 + */ + +#define P4_MOB_LOAD_REPLAY P4_EVENT_PACK(0x03, 0x02) + /* + * MSR_P4_MOB_ESCR0: 0, 1 + * MSR_P4_MOB_ESCR1: 2, 3 + */ + +#define P4_PAGE_WALK_TYPE P4_EVENT_PACK(0x01, 0x04) + /* + * MSR_P4_PMH_ESCR0: 0, 1 + * MSR_P4_PMH_ESCR1: 2, 3 + */ + +#define P4_BSQ_CACHE_REFERENCE P4_EVENT_PACK(0x0c, 0x07) + /* + * MSR_P4_BSU_ESCR0: 0, 1 + * MSR_P4_BSU_ESCR1: 2, 3 + */ + +#define P4_IOQ_ALLOCATION P4_EVENT_PACK(0x03, 0x06) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_IOQ_ACTIVE_ENTRIES P4_EVENT_PACK(0x1a, 0x06) + /* + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_FSB_DATA_ACTIVITY P4_EVENT_PACK(0x17, 0x06) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_BSQ_ALLOCATION P4_EVENT_PACK(0x05, 0x07) + /* + * MSR_P4_BSU_ESCR0: 0, 1 + */ + +#define P4_BSQ_ACTIVE_ENTRIES P4_EVENT_PACK(0x06, 0x07) + /* + * MSR_P4_BSU_ESCR1: 2, 3 + */ + +#define P4_SSE_INPUT_ASSIST P4_EVENT_PACK(0x34, 0x01) + /* + * MSR_P4_FIRM_ESCR: 8, 9 + * MSR_P4_FIRM_ESCR: 10, 11 + */ + +#define P4_PACKED_SP_UOP P4_EVENT_PACK(0x08, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_PACKED_DP_UOP P4_EVENT_PACK(0x0c, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_SCALAR_SP_UOP P4_EVENT_PACK(0x0a, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_SCALAR_DP_UOP P4_EVENT_PACK(0x0e, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_64BIT_MMX_UOP P4_EVENT_PACK(0x02, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_128BIT_MMX_UOP P4_EVENT_PACK(0x1a, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_X87_FP_UOP P4_EVENT_PACK(0x04, 0x01) + /* + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 + */ + +#define P4_TC_MISC P4_EVENT_PACK(0x06, 0x01) + /* + * MSR_P4_TC_ESCR0: 4, 5 + * MSR_P4_TC_ESCR1: 6, 7 + */ + +#define P4_GLOBAL_POWER_EVENTS P4_EVENT_PACK(0x13, 0x06) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_TC_MS_XFER P4_EVENT_PACK(0x05, 0x00) + /* + * MSR_P4_MS_ESCR0: 4, 5 + * MSR_P4_MS_ESCR1: 6, 7 + */ + +#define P4_UOP_QUEUE_WRITES P4_EVENT_PACK(0x09, 0x00) + /* + * MSR_P4_MS_ESCR0: 4, 5 + * MSR_P4_MS_ESCR1: 6, 7 + */ + +#define P4_RETIRED_MISPRED_BRANCH_TYPE P4_EVENT_PACK(0x05, 0x02) + /* + * MSR_P4_TBPU_ESCR0: 4, 5 + * MSR_P4_TBPU_ESCR0: 6, 7 + */ + +#define P4_RETIRED_BRANCH_TYPE P4_EVENT_PACK(0x04, 0x02) + /* + * MSR_P4_TBPU_ESCR0: 4, 5 + * MSR_P4_TBPU_ESCR0: 6, 7 + */ + +#define P4_RESOURCE_STALL P4_EVENT_PACK(0x01, 0x01) + /* + * MSR_P4_ALF_ESCR0: 12, 13, 16 + * MSR_P4_ALF_ESCR1: 14, 15, 17 + */ + +#define P4_WC_BUFFER P4_EVENT_PACK(0x05, 0x05) + /* + * MSR_P4_DAC_ESCR0: 8, 9 + * MSR_P4_DAC_ESCR1: 10, 11 + */ + +#define P4_B2B_CYCLES P4_EVENT_PACK(0x16, 0x03) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_BNR P4_EVENT_PACK(0x08, 0x03) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_SNOOP P4_EVENT_PACK(0x06, 0x03) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_RESPONSE P4_EVENT_PACK(0x04, 0x03) + /* + * MSR_P4_FSB_ESCR0: 0, 1 + * MSR_P4_FSB_ESCR1: 2, 3 + */ + +#define P4_FRONT_END_EVENT P4_EVENT_PACK(0x08, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_EXECUTION_EVENT P4_EVENT_PACK(0x0c, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_REPLAY_EVENT P4_EVENT_PACK(0x09, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_INSTR_RETIRED P4_EVENT_PACK(0x02, 0x04) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_UOPS_RETIRED P4_EVENT_PACK(0x01, 0x04) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_UOP_TYPE P4_EVENT_PACK(0x02, 0x02) + /* + * MSR_P4_RAT_ESCR0: 12, 13, 16 + * MSR_P4_RAT_ESCR1: 14, 15, 17 + */ + +#define P4_BRANCH_RETIRED P4_EVENT_PACK(0x06, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_MISPRED_BRANCH_RETIRED P4_EVENT_PACK(0x03, 0x04) + /* + * MSR_P4_CRU_ESCR0: 12, 13, 16 + * MSR_P4_CRU_ESCR1: 14, 15, 17 + */ + +#define P4_X87_ASSIST P4_EVENT_PACK(0x03, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_MACHINE_CLEAR P4_EVENT_PACK(0x02, 0x05) + /* + * MSR_P4_CRU_ESCR2: 12, 13, 16 + * MSR_P4_CRU_ESCR3: 14, 15, 17 + */ + +#define P4_INSTR_COMPLETED P4_EVENT_PACK(0x07, 0x04) + /* + * MSR_P4_CRU_ESCR0: 12, 13, 16 + * MSR_P4_CRU_ESCR1: 14, 15, 17 + */ + +/* + * a caller should use P4_EVENT_ATTR helper to + * pick the attribute needed, for example + * + * P4_EVENT_ATTR(P4_TC_DELIVER_MODE, DD) + */ +enum P4_EVENTS_ATTR { + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DD, 0), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DB, 1), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DI, 2), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BD, 3), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BB, 4), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BI, 5), + P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, ID, 6), + + P4_MAKE_EVENT_ATTR(P4_BPU_FETCH_REQUEST, TCMISS, 0), + + P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, HIT, 0), + P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, MISS, 1), + P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, HIT_UK, 2), + + P4_MAKE_EVENT_ATTR(P4_MEMORY_CANCEL, ST_RB_FULL, 2), + P4_MAKE_EVENT_ATTR(P4_MEMORY_CANCEL, 64K_CONF, 3), + + P4_MAKE_EVENT_ATTR(P4_MEMORY_COMPLETE, LSC, 0), + P4_MAKE_EVENT_ATTR(P4_MEMORY_COMPLETE, SSC, 1), + + P4_MAKE_EVENT_ATTR(P4_LOAD_PORT_REPLAY, SPLIT_LD, 1), + + P4_MAKE_EVENT_ATTR(P4_STORE_PORT_REPLAY, SPLIT_ST, 1), + + P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, NO_STA, 1), + P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, NO_STD, 3), + P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, PARTIAL_DATA, 4), + P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, UNALGN_ADDR, 5), + + P4_MAKE_EVENT_ATTR(P4_PAGE_WALK_TYPE, DTMISS, 0), + P4_MAKE_EVENT_ATTR(P4_PAGE_WALK_TYPE, ITMISS, 1), + + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITS, 0), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITE, 1), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITM, 2), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITS, 3), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITE, 4), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITM, 5), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_MISS, 8), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_MISS, 9), + P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, WR_2ndL_MISS, 10), + + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, DEFAULT, 0), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, ALL_READ, 5), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, ALL_WRITE, 6), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_UC, 7), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WC, 8), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WT, 9), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WP, 10), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WB, 11), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, OWN, 13), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, OTHER, 14), + P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, PREFETCH, 15), + + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, DEFAULT, 0), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, ALL_READ, 5), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, ALL_WRITE, 6), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_UC, 7), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WC, 8), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WT, 9), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WP, 10), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WB, 11), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, OWN, 13), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, OTHER, 14), + P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, PREFETCH, 15), + + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_DRV, 0), + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OWN, 1), + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OTHER, 2), + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_DRV, 3), + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_OWN, 4), + P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_OTHER, 5), + + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_TYPE0, 0), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_TYPE1, 1), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LEN0, 2), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LEN1, 3), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_IO_TYPE, 5), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LOCK_TYPE, 6), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_CACHE_TYPE, 7), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_SPLIT_TYPE, 8), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_DEM_TYPE, 9), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_ORD_TYPE, 10), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE0, 11), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE1, 12), + P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE2, 13), + + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_TYPE0, 0), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_TYPE1, 1), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LEN0, 2), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LEN1, 3), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_IO_TYPE, 5), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LOCK_TYPE, 6), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_CACHE_TYPE, 7), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_SPLIT_TYPE, 8), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_DEM_TYPE, 9), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_ORD_TYPE, 10), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE0, 11), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE1, 12), + P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE2, 13), + + P4_MAKE_EVENT_ATTR(P4_SSE_INPUT_ASSIST, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_PACKED_SP_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_PACKED_DP_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_SCALAR_SP_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_SCALAR_DP_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_64BIT_MMX_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_128BIT_MMX_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_X87_FP_UOP, ALL, 15), + + P4_MAKE_EVENT_ATTR(P4_TC_MISC, FLUSH, 4), + + P4_MAKE_EVENT_ATTR(P4_GLOBAL_POWER_EVENTS, RUNNING, 0), + + P4_MAKE_EVENT_ATTR(P4_TC_MS_XFER, CISC, 0), + + P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_TC_BUILD, 0), + P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_TC_DELIVER, 1), + P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_ROM, 2), + + P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, CONDITIONAL, 1), + P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, CALL, 2), + P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, RETURN, 3), + P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, INDIRECT, 4), + + P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CONDITIONAL, 1), + P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CALL, 2), + P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, RETURN, 3), + P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, INDIRECT, 4), + + P4_MAKE_EVENT_ATTR(P4_RESOURCE_STALL, SBFULL, 5), + + P4_MAKE_EVENT_ATTR(P4_WC_BUFFER, WCB_EVICTS, 0), + P4_MAKE_EVENT_ATTR(P4_WC_BUFFER, WCB_FULL_EVICTS, 1), + + P4_MAKE_EVENT_ATTR(P4_FRONT_END_EVENT, NBOGUS, 0), + P4_MAKE_EVENT_ATTR(P4_FRONT_END_EVENT, BOGUS, 1), + + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS0, 0), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS1, 1), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS2, 2), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS3, 3), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS0, 4), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS1, 5), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS2, 6), + P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS3, 7), + + P4_MAKE_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS, 0), + P4_MAKE_EVENT_ATTR(P4_REPLAY_EVENT, BOGUS, 1), + + P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG, 0), + P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSTAG, 1), + P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG, 2), + P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSTAG, 3), + + P4_MAKE_EVENT_ATTR(P4_UOPS_RETIRED, NBOGUS, 0), + P4_MAKE_EVENT_ATTR(P4_UOPS_RETIRED, BOGUS, 1), + + P4_MAKE_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS, 1), + P4_MAKE_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES, 2), + + P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMNP, 0), + P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMNM, 1), + P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMTP, 2), + P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMTM, 3), + + P4_MAKE_EVENT_ATTR(P4_MISPRED_BRANCH_RETIRED, NBOGUS, 0), + + P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, FPSU, 0), + P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, FPSO, 1), + P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, POAO, 2), + P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, POAU, 3), + P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, PREA, 4), + + P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, CLEAR, 0), + P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, MOCLEAR, 1), + P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, SMCLEAR, 2), + + P4_MAKE_EVENT_ATTR(P4_INSTR_COMPLETED, NBOGUS, 0), + P4_MAKE_EVENT_ATTR(P4_INSTR_COMPLETED, BOGUS, 1), +}; + +#endif /* PERF_EVENT_P4_H */ diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index e24f637..e6a3f5f 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -190,6 +190,8 @@ struct x86_pmu { void (*enable_all)(void); void (*enable)(struct perf_event *); void (*disable)(struct perf_event *); + int (*hw_config)(struct perf_event_attr *attr, struct hw_perf_event *hwc); + int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); unsigned eventsel; unsigned perfctr; u64 (*event_map)(int); @@ -415,6 +417,25 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) return 0; } +static int x86_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) +{ + /* + * Generate PMC IRQs: + * (keep 'enabled' bit clear for now) + */ + hwc->config = ARCH_PERFMON_EVENTSEL_INT; + + /* + * Count user and OS events unless requested not to + */ + if (!attr->exclude_user) + hwc->config |= ARCH_PERFMON_EVENTSEL_USR; + if (!attr->exclude_kernel) + hwc->config |= ARCH_PERFMON_EVENTSEL_OS; + + return 0; +} + /* * Setup the hardware configuration for a given attr_type */ @@ -446,23 +467,13 @@ static int __hw_perf_event_init(struct perf_event *event) event->destroy = hw_perf_event_destroy; - /* - * Generate PMC IRQs: - * (keep 'enabled' bit clear for now) - */ - hwc->config = ARCH_PERFMON_EVENTSEL_INT; - hwc->idx = -1; hwc->last_cpu = -1; hwc->last_tag = ~0ULL; - /* - * Count user and OS events unless requested not to. - */ - if (!attr->exclude_user) - hwc->config |= ARCH_PERFMON_EVENTSEL_USR; - if (!attr->exclude_kernel) - hwc->config |= ARCH_PERFMON_EVENTSEL_OS; + /* Processor specifics */ + if (x86_pmu.hw_config(attr, hwc)) + return -EOPNOTSUPP; if (!hwc->sample_period) { hwc->sample_period = x86_pmu.max_period; @@ -517,7 +528,7 @@ static int __hw_perf_event_init(struct perf_event *event) return -EOPNOTSUPP; /* BTS is currently only allowed for user-mode. */ - if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) + if (!attr->exclude_kernel) return -EOPNOTSUPP; } @@ -931,7 +942,7 @@ static int x86_pmu_enable(struct perf_event *event) if (n < 0) return n; - ret = x86_schedule_events(cpuc, n, assign); + ret = x86_pmu.schedule_events(cpuc, n, assign); if (ret) return ret; /* @@ -1263,7 +1274,7 @@ int hw_perf_group_sched_in(struct perf_event *leader, if (n0 < 0) return n0; - ret = x86_schedule_events(cpuc, n0, assign); + ret = x86_pmu.schedule_events(cpuc, n0, assign); if (ret) return ret; @@ -1313,6 +1324,7 @@ undo: #include "perf_event_amd.c" #include "perf_event_p6.c" +#include "perf_event_p4.c" #include "perf_event_intel_lbr.c" #include "perf_event_intel_ds.c" #include "perf_event_intel.c" @@ -1515,7 +1527,7 @@ static int validate_group(struct perf_event *event) fake_cpuc->n_events = n; - ret = x86_schedule_events(fake_cpuc, n, NULL); + ret = x86_pmu.schedule_events(fake_cpuc, n, NULL); out_free: kfree(fake_cpuc); diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 573458f..358a8e3 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -363,6 +363,8 @@ static __initconst struct x86_pmu amd_pmu = { .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, + .hw_config = x86_hw_config, + .schedule_events = x86_schedule_events, .eventsel = MSR_K7_EVNTSEL0, .perfctr = MSR_K7_PERFCTR0, .event_map = amd_pmu_event_map, diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 971dc6e..044b843 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -749,6 +749,8 @@ static __initconst struct x86_pmu core_pmu = { .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, + .hw_config = x86_hw_config, + .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, @@ -786,6 +788,8 @@ static __initconst struct x86_pmu intel_pmu = { .enable_all = intel_pmu_enable_all, .enable = intel_pmu_enable_event, .disable = intel_pmu_disable_event, + .hw_config = x86_hw_config, + .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, @@ -839,12 +843,13 @@ static __init int intel_pmu_init(void) int version; if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { - /* check for P6 processor family */ - if (boot_cpu_data.x86 == 6) { - return p6_pmu_init(); - } else { + switch (boot_cpu_data.x86) { + case 0x6: + return p6_pmu_init(); + case 0xf: + return p4_pmu_init(); + } return -ENODEV; - } } /* diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c new file mode 100644 index 0000000..381f593 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -0,0 +1,607 @@ +/* + * Netburst Perfomance Events (P4, old Xeon) + * + * Copyright (C) 2010 Parallels, Inc., Cyrill Gorcunov + * Copyright (C) 2010 Intel Corporation, Lin Ming + * + * For licencing details see kernel-base/COPYING + */ + +#ifdef CONFIG_CPU_SUP_INTEL + +#include + +/* + * array indices: 0,1 - HT threads, used with HT enabled cpu + */ +struct p4_event_template { + u32 opcode; /* ESCR event + CCCR selector */ + u64 config; /* packed predefined bits */ + int dep; /* upstream dependency event index */ + unsigned int emask; /* ESCR EventMask */ + unsigned int escr_msr[2]; /* ESCR MSR for this event */ + unsigned int cntr[2]; /* counter index (offset) */ +}; + +struct p4_pmu_res { + /* maps hw_conf::idx into template for ESCR sake */ + struct p4_event_template *tpl[ARCH_P4_MAX_CCCR]; +}; + +static DEFINE_PER_CPU(struct p4_pmu_res, p4_pmu_config); + +/* + * WARN: CCCR1 doesn't have a working enable bit so try to not + * use it if possible + * + * Also as only we start to support raw events we will need to + * append _all_ P4_EVENT_PACK'ed events here + */ +struct p4_event_template p4_templates[] = { + [0] = { + .opcode = P4_UOP_TYPE, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS) | + P4_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES), + .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, + .cntr = { 16, 17 }, + }, + [1] = { + .opcode = P4_GLOBAL_POWER_EVENTS, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_GLOBAL_POWER_EVENTS, RUNNING), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { 0, 2 }, + }, + [2] = { + .opcode = P4_INSTR_RETIRED, + .config = 0, + .dep = 0, /* needs front-end tagging */ + .emask = + P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG) | + P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSTAG) | + P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG) | + P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSTAG), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { 12, 14 }, + }, + [3] = { + .opcode = P4_BSQ_CACHE_REFERENCE, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITM) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITS) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITE) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITM), + .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, + .cntr = { 0, 2 }, + }, + [4] = { + .opcode = P4_BSQ_CACHE_REFERENCE, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) | + P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, WR_2ndL_MISS), + .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, + .cntr = { 0, 3 }, + }, + [5] = { + .opcode = P4_RETIRED_BRANCH_TYPE, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CONDITIONAL) | + P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CALL) | + P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, RETURN) | + P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, INDIRECT), + .escr_msr = { MSR_P4_TBPU_ESCR0, MSR_P4_TBPU_ESCR1 }, + .cntr = { 4, 6 }, + }, + [6] = { + .opcode = P4_MISPRED_BRANCH_RETIRED, + .config = 0, + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_MISPRED_BRANCH_RETIRED, NBOGUS), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, + .cntr = { 12, 14 }, + }, + [7] = { + .opcode = P4_FSB_DATA_ACTIVITY, + .config = p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE), + .dep = -1, + .emask = + P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_DRV) | + P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OWN), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { 0, 2 }, + }, +}; + +static struct p4_event_template *p4_event_map[PERF_COUNT_HW_MAX] = { + /* non-halted CPU clocks */ + [PERF_COUNT_HW_CPU_CYCLES] = &p4_templates[1], + + /* retired instructions: dep on tagging the FSB */ + [PERF_COUNT_HW_INSTRUCTIONS] = &p4_templates[2], + + /* cache hits */ + [PERF_COUNT_HW_CACHE_REFERENCES] = &p4_templates[3], + + /* cache misses */ + [PERF_COUNT_HW_CACHE_MISSES] = &p4_templates[4], + + /* branch instructions retired */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = &p4_templates[5], + + /* mispredicted branches retired */ + [PERF_COUNT_HW_BRANCH_MISSES] = &p4_templates[6], + + /* bus ready clocks (cpu is driving #DRDY_DRV\#DRDY_OWN): */ + [PERF_COUNT_HW_BUS_CYCLES] = &p4_templates[7], +}; + +static u64 p4_pmu_event_map(int hw_event) +{ + struct p4_event_template *tpl; + u64 config; + + if (hw_event > ARRAY_SIZE(p4_event_map)) { + printk_once(KERN_ERR "PMU: Incorrect event index\n"); + return 0; + } + tpl = p4_event_map[hw_event]; + + /* + * fill config up according to + * a predefined event template + */ + config = tpl->config; + config |= p4_config_pack_escr(P4_EVENT_UNPACK_EVENT(tpl->opcode) << P4_EVNTSEL_EVENT_SHIFT); + config |= p4_config_pack_escr(tpl->emask << P4_EVNTSEL_EVENTMASK_SHIFT); + config |= p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(tpl->opcode) << P4_CCCR_ESCR_SELECT_SHIFT); + + /* on HT machine we need a special bit */ + if (p4_ht_active() && p4_ht_thread(raw_smp_processor_id())) + config = p4_set_ht_bit(config); + + return config; +} + +/* + * Note that we still have 5 events (from global events SDM list) + * intersected in opcode+emask bits so we will need another + * scheme there do distinguish templates. + */ +static inline int p4_pmu_emask_match(unsigned int dst, unsigned int src) +{ + return dst & src; +} + +static struct p4_event_template *p4_pmu_template_lookup(u64 config) +{ + u32 opcode = p4_config_unpack_opcode(config); + unsigned int emask = p4_config_unpack_emask(config); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(p4_templates); i++) { + if (opcode == p4_templates[i].opcode && + p4_pmu_emask_match(emask, p4_templates[i].emask)) + return &p4_templates[i]; + } + + return NULL; +} + +/* + * We don't control raw events so it's up to the caller + * to pass sane values (and we don't count the thread number + * on HT machine but allow HT-compatible specifics to be + * passed on) + */ +static u64 p4_pmu_raw_event(u64 hw_event) +{ + return hw_event & + (p4_config_pack_escr(P4_EVNTSEL_MASK_HT) | + p4_config_pack_cccr(P4_CCCR_MASK_HT)); +} + +static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) +{ + int cpu = raw_smp_processor_id(); + + /* + * the reason we use cpu that early is that: if we get scheduled + * first time on the same cpu -- we will not need swap thread + * specific flags in config (and will save some cpu cycles) + */ + + /* CCCR by default */ + hwc->config = p4_config_pack_cccr(p4_default_cccr_conf(cpu)); + + /* Count user and OS events unless not requested to */ + hwc->config |= p4_config_pack_escr(p4_default_escr_conf(cpu, attr->exclude_kernel, + attr->exclude_user)); + return 0; +} + +static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc) +{ + unsigned long dummy; + + rdmsrl(hwc->config_base + hwc->idx, dummy); + if (dummy & P4_CCCR_OVF) { + (void)checking_wrmsrl(hwc->config_base + hwc->idx, + ((u64)dummy) & ~P4_CCCR_OVF); + } +} + +static inline void p4_pmu_disable_event(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + /* + * If event gets disabled while counter is in overflowed + * state we need to clear P4_CCCR_OVF, otherwise interrupt get + * asserted again and again + */ + (void)checking_wrmsrl(hwc->config_base + hwc->idx, + (u64)(p4_config_unpack_cccr(hwc->config)) & + ~P4_CCCR_ENABLE & ~P4_CCCR_OVF); +} + +static void p4_pmu_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int idx; + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + struct perf_event *event = cpuc->events[idx]; + if (!test_bit(idx, cpuc->active_mask)) + continue; + p4_pmu_disable_event(event); + } +} + +static void p4_pmu_enable_event(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + int thread = p4_ht_config_thread(hwc->config); + u64 escr_conf = p4_config_unpack_escr(p4_clear_ht_bit(hwc->config)); + u64 escr_base; + struct p4_event_template *tpl; + struct p4_pmu_res *c; + + /* + * some preparation work from per-cpu private fields + * since we need to find out which ESCR to use + */ + c = &__get_cpu_var(p4_pmu_config); + tpl = c->tpl[hwc->idx]; + if (!tpl) { + pr_crit("%s: Wrong index: %d\n", __func__, hwc->idx); + return; + } + escr_base = (u64)tpl->escr_msr[thread]; + + /* + * - we dont support cascaded counters yet + * - and counter 1 is broken (erratum) + */ + WARN_ON_ONCE(p4_is_event_cascaded(hwc->config)); + WARN_ON_ONCE(hwc->idx == 1); + + (void)checking_wrmsrl(escr_base, escr_conf); + (void)checking_wrmsrl(hwc->config_base + hwc->idx, + (u64)(p4_config_unpack_cccr(hwc->config)) | P4_CCCR_ENABLE); +} + +static void p4_pmu_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int idx; + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + struct perf_event *event = cpuc->events[idx]; + if (!test_bit(idx, cpuc->active_mask)) + continue; + p4_pmu_enable_event(event); + } +} + +static int p4_pmu_handle_irq(struct pt_regs *regs) +{ + struct perf_sample_data data; + struct cpu_hw_events *cpuc; + struct perf_event *event; + struct hw_perf_event *hwc; + int idx, handled = 0; + u64 val; + + data.addr = 0; + data.raw = NULL; + + cpuc = &__get_cpu_var(cpu_hw_events); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + + if (!test_bit(idx, cpuc->active_mask)) + continue; + + event = cpuc->events[idx]; + hwc = &event->hw; + + WARN_ON_ONCE(hwc->idx != idx); + + /* + * FIXME: Redundant call, actually not needed + * but just to check if we're screwed + */ + p4_pmu_clear_cccr_ovf(hwc); + + val = x86_perf_event_update(event); + if (val & (1ULL << (x86_pmu.event_bits - 1))) + continue; + + /* + * event overflow + */ + handled = 1; + data.period = event->hw.last_period; + + if (!x86_perf_event_set_period(event)) + continue; + if (perf_event_overflow(event, 1, &data, regs)) + p4_pmu_disable_event(event); + } + + if (handled) { + /* p4 quirk: unmask it again */ + apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); + inc_irq_stat(apic_perf_irqs); + } + + return handled; +} + +/* + * swap thread specific fields according to a thread + * we are going to run on + */ +static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) +{ + u32 escr, cccr; + + /* + * we either lucky and continue on same cpu or no HT support + */ + if (!p4_should_swap_ts(hwc->config, cpu)) + return; + + /* + * the event is migrated from an another logical + * cpu, so we need to swap thread specific flags + */ + + escr = p4_config_unpack_escr(hwc->config); + cccr = p4_config_unpack_cccr(hwc->config); + + if (p4_ht_thread(cpu)) { + cccr &= ~P4_CCCR_OVF_PMI_T0; + cccr |= P4_CCCR_OVF_PMI_T1; + if (escr & P4_EVNTSEL_T0_OS) { + escr &= ~P4_EVNTSEL_T0_OS; + escr |= P4_EVNTSEL_T1_OS; + } + if (escr & P4_EVNTSEL_T0_USR) { + escr &= ~P4_EVNTSEL_T0_USR; + escr |= P4_EVNTSEL_T1_USR; + } + hwc->config = p4_config_pack_escr(escr); + hwc->config |= p4_config_pack_cccr(cccr); + hwc->config |= P4_CONFIG_HT; + } else { + cccr &= ~P4_CCCR_OVF_PMI_T1; + cccr |= P4_CCCR_OVF_PMI_T0; + if (escr & P4_EVNTSEL_T1_OS) { + escr &= ~P4_EVNTSEL_T1_OS; + escr |= P4_EVNTSEL_T0_OS; + } + if (escr & P4_EVNTSEL_T1_USR) { + escr &= ~P4_EVNTSEL_T1_USR; + escr |= P4_EVNTSEL_T0_USR; + } + hwc->config = p4_config_pack_escr(escr); + hwc->config |= p4_config_pack_cccr(cccr); + hwc->config &= ~P4_CONFIG_HT; + } +} + +/* ESCRs are not sequential in memory so we need a map */ +static unsigned int p4_escr_map[ARCH_P4_TOTAL_ESCR] = { + MSR_P4_ALF_ESCR0, /* 0 */ + MSR_P4_ALF_ESCR1, /* 1 */ + MSR_P4_BPU_ESCR0, /* 2 */ + MSR_P4_BPU_ESCR1, /* 3 */ + MSR_P4_BSU_ESCR0, /* 4 */ + MSR_P4_BSU_ESCR1, /* 5 */ + MSR_P4_CRU_ESCR0, /* 6 */ + MSR_P4_CRU_ESCR1, /* 7 */ + MSR_P4_CRU_ESCR2, /* 8 */ + MSR_P4_CRU_ESCR3, /* 9 */ + MSR_P4_CRU_ESCR4, /* 10 */ + MSR_P4_CRU_ESCR5, /* 11 */ + MSR_P4_DAC_ESCR0, /* 12 */ + MSR_P4_DAC_ESCR1, /* 13 */ + MSR_P4_FIRM_ESCR0, /* 14 */ + MSR_P4_FIRM_ESCR1, /* 15 */ + MSR_P4_FLAME_ESCR0, /* 16 */ + MSR_P4_FLAME_ESCR1, /* 17 */ + MSR_P4_FSB_ESCR0, /* 18 */ + MSR_P4_FSB_ESCR1, /* 19 */ + MSR_P4_IQ_ESCR0, /* 20 */ + MSR_P4_IQ_ESCR1, /* 21 */ + MSR_P4_IS_ESCR0, /* 22 */ + MSR_P4_IS_ESCR1, /* 23 */ + MSR_P4_ITLB_ESCR0, /* 24 */ + MSR_P4_ITLB_ESCR1, /* 25 */ + MSR_P4_IX_ESCR0, /* 26 */ + MSR_P4_IX_ESCR1, /* 27 */ + MSR_P4_MOB_ESCR0, /* 28 */ + MSR_P4_MOB_ESCR1, /* 29 */ + MSR_P4_MS_ESCR0, /* 30 */ + MSR_P4_MS_ESCR1, /* 31 */ + MSR_P4_PMH_ESCR0, /* 32 */ + MSR_P4_PMH_ESCR1, /* 33 */ + MSR_P4_RAT_ESCR0, /* 34 */ + MSR_P4_RAT_ESCR1, /* 35 */ + MSR_P4_SAAT_ESCR0, /* 36 */ + MSR_P4_SAAT_ESCR1, /* 37 */ + MSR_P4_SSU_ESCR0, /* 38 */ + MSR_P4_SSU_ESCR1, /* 39 */ + MSR_P4_TBPU_ESCR0, /* 40 */ + MSR_P4_TBPU_ESCR1, /* 41 */ + MSR_P4_TC_ESCR0, /* 42 */ + MSR_P4_TC_ESCR1, /* 43 */ + MSR_P4_U2L_ESCR0, /* 44 */ + MSR_P4_U2L_ESCR1, /* 45 */ +}; + +static int p4_get_escr_idx(unsigned int addr) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(p4_escr_map); i++) { + if (addr == p4_escr_map[i]) + return i; + } + + return -1; +} + +static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) +{ + unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; + unsigned long escr_mask[BITS_TO_LONGS(ARCH_P4_TOTAL_ESCR)]; + + struct hw_perf_event *hwc; + struct p4_event_template *tpl; + struct p4_pmu_res *c; + int cpu = raw_smp_processor_id(); + int escr_idx, thread, i, num; + + bitmap_zero(used_mask, X86_PMC_IDX_MAX); + bitmap_zero(escr_mask, ARCH_P4_TOTAL_ESCR); + + c = &__get_cpu_var(p4_pmu_config); + /* + * Firstly find out which resource events are going + * to use, if ESCR+CCCR tuple is already borrowed + * then get out of here + */ + for (i = 0, num = n; i < n; i++, num--) { + hwc = &cpuc->event_list[i]->hw; + tpl = p4_pmu_template_lookup(hwc->config); + if (!tpl) + goto done; + thread = p4_ht_thread(cpu); + escr_idx = p4_get_escr_idx(tpl->escr_msr[thread]); + if (escr_idx == -1) + goto done; + + /* already allocated and remains on the same cpu */ + if (hwc->idx != -1 && !p4_should_swap_ts(hwc->config, cpu)) { + if (assign) + assign[i] = hwc->idx; + /* upstream dependent event */ + if (unlikely(tpl->dep != -1)) + printk_once(KERN_WARNING "PMU: Dep events are " + "not implemented yet\n"); + goto reserve; + } + + /* it may be already borrowed */ + if (test_bit(tpl->cntr[thread], used_mask) || + test_bit(escr_idx, escr_mask)) + goto done; + + /* + * ESCR+CCCR+COUNTERs are available to use lets swap + * thread specific bits, push assigned bits + * back and save template into per-cpu + * area (which will allow us to find out the ESCR + * to be used at moment of "enable event via real MSR") + */ + p4_pmu_swap_config_ts(hwc, cpu); + if (assign) { + assign[i] = tpl->cntr[thread]; + c->tpl[assign[i]] = tpl; + } +reserve: + set_bit(tpl->cntr[thread], used_mask); + set_bit(escr_idx, escr_mask); + } + +done: + return num ? -ENOSPC : 0; +} + +static __initconst struct x86_pmu p4_pmu = { + .name = "Netburst P4/Xeon", + .handle_irq = p4_pmu_handle_irq, + .disable_all = p4_pmu_disable_all, + .enable_all = p4_pmu_enable_all, + .enable = p4_pmu_enable_event, + .disable = p4_pmu_disable_event, + .eventsel = MSR_P4_BPU_CCCR0, + .perfctr = MSR_P4_BPU_PERFCTR0, + .event_map = p4_pmu_event_map, + .raw_event = p4_pmu_raw_event, + .max_events = ARRAY_SIZE(p4_event_map), + .get_event_constraints = x86_get_event_constraints, + /* + * IF HT disabled we may need to use all + * ARCH_P4_MAX_CCCR counters simulaneously + * though leave it restricted at moment assuming + * HT is on + */ + .num_events = ARCH_P4_MAX_CCCR, + .apic = 1, + .event_bits = 40, + .event_mask = (1ULL << 40) - 1, + .max_period = (1ULL << 39) - 1, + .hw_config = p4_hw_config, + .schedule_events = p4_pmu_schedule_events, +}; + +static __init int p4_pmu_init(void) +{ + unsigned int low, high; + + /* If we get stripped -- indexig fails */ + BUILD_BUG_ON(ARCH_P4_MAX_CCCR > X86_PMC_MAX_GENERIC); + + rdmsr(MSR_IA32_MISC_ENABLE, low, high); + if (!(low & (1 << 7))) { + pr_cont("unsupported Netburst CPU model %d ", + boot_cpu_data.x86_model); + return -ENODEV; + } + + pr_cont("Netburst events, "); + + x86_pmu = p4_pmu; + + return 0; +} + +#endif /* CONFIG_CPU_SUP_INTEL */ diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index a330485..6ff4d01d 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -109,6 +109,8 @@ static __initconst struct x86_pmu p6_pmu = { .enable_all = p6_pmu_enable_all, .enable = p6_pmu_enable_event, .disable = p6_pmu_disable_event, + .hw_config = x86_hw_config, + .schedule_events = x86_schedule_events, .eventsel = MSR_P6_EVNTSEL0, .perfctr = MSR_P6_PERFCTR0, .event_map = p6_pmu_event_map, -- cgit v0.10.2 From 6230f2c7ef01a69e2ba9370326572c287209d32a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 15:53:12 -0300 Subject: perf record: Mention paranoid sysctl when failing to create counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [acme@mica linux-2.6-tip]$ perf record -a -f Fatal: Permission error - are you root? Consider tweaking /proc/sys/kernel/perf_event_paranoid. [acme@mica linux-2.6-tip]$ Suggested-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268333592-30872-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 771533c..6e4a393 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -280,7 +280,8 @@ try_again: int err = errno; if (err == EPERM || err == EACCES) - die("Permission error - are you root?\n"); + die("Permission error - are you root?\n" + "\t Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n"); else if (err == ENODEV && profile_cpu != -1) die("No such device - did you specify an out-of-range profile CPU?\n"); -- cgit v0.10.2 From 0b861225a5890f22445f08ca9cc7a87cff276ff7 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 12 Mar 2010 00:50:16 +0300 Subject: x86, perf: Fix NULL deref on not assigned x86_pmu In case of not assigned x86_pmu and software events NULL dereference may being hit via x86_pmu::schedule_events method. Fix it by checking if x86_pmu is initialized at all. Signed-off-by: Cyrill Gorcunov Cc: Lin Ming Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Cc: Robert Richter Cc: Frederic Weisbecker Cc: Peter Zijlstra LKML-Reference: <20100311215016.GG25162@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index e6a3f5f..5586a02 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1269,6 +1269,9 @@ int hw_perf_group_sched_in(struct perf_event *leader, int assign[X86_PMC_IDX_MAX]; int n0, n1, ret; + if (!x86_pmu_initialized()) + return 0; + /* n0 = total number of events */ n0 = collect_events(cpuc, leader, true); if (n0 < 0) -- cgit v0.10.2 From fe2197b8bb2f318c1e0b0f589f24f781dd27d1f2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:40 -0300 Subject: perf symbols: Bump plt synthesizing warning debug level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 323c0ae..75cd468 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -862,8 +862,8 @@ out_close: if (err == 0) return nr; out: - pr_warning("%s: problems reading %s PLT info.\n", - __func__, self->long_name); + pr_debug("%s: problems reading %s PLT info.\n", + __func__, self->long_name); return 0; } -- cgit v0.10.2 From 895f0edc3cd04a2a3de2c608ba20821b832a8abb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:41 -0300 Subject: perf top: Export get_window_dimensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used by the newt code too. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ec48223..57e232f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -133,7 +133,7 @@ static inline struct symbol *sym_entry__symbol(struct sym_entry *self) return ((void *)self) + symbol_conf.priv_size; } -static void get_term_dimensions(struct winsize *ws) +void get_term_dimensions(struct winsize *ws) { char *s = getenv("LINES"); diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 6fb379b..aa78615 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -1,6 +1,10 @@ #ifndef _PERF_PERF_H #define _PERF_PERF_H +struct winsize; + +void get_term_dimensions(struct winsize *ws); + #if defined(__i386__) #include "../../arch/x86/include/asm/unistd.h" #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") -- cgit v0.10.2 From b4f5296f0eec2aa7061dfd8bb8c0744f095f9bd1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:42 -0300 Subject: perf tools: Use eprintf for pr_{err,warning,info} too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like we do for pr_debug, so that we can have a single point where to redirect to the currently used output system, be it stdio or newt. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index c6c24c5..58720a1 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -7,8 +7,6 @@ extern int verbose; extern int dump_trace; -int eprintf(int level, - const char *fmt, ...) __attribute__((format(printf, 2, 3))); int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index f261165..388ab1b 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -85,16 +85,19 @@ simple_strtoul(const char *nptr, char **endptr, int base) return strtoul(nptr, endptr, base); } +int eprintf(int level, + const char *fmt, ...) __attribute__((format(printf, 2, 3))); + #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #define pr_err(fmt, ...) \ - do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ - do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ - do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug(fmt, ...) \ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debugN(n, fmt, ...) \ -- cgit v0.10.2 From dd2ee78dd8e4c6d6f1a333fd60c3dd27d1b07042 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:43 -0300 Subject: perf tools: Add missing bytes printed in hist_entry__fprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need those to properly size the browser widht in the newt TUI. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index bdcfd61..d43be34 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -455,11 +455,11 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -static size_t hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total) +size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) { struct sort_entry *se; u64 count, total; @@ -485,9 +485,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, if (symbol_conf.show_nr_samples) { if (sep) - fprintf(fp, "%c%lld", *sep, count); + ret += fprintf(fp, "%c%lld", *sep, count); else - fprintf(fp, "%11lld", count); + ret += fprintf(fp, "%11lld", count); } if (pair_session) { @@ -518,9 +518,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - fprintf(fp, "%c%s", *sep, bf); + ret += fprintf(fp, "%c%s", *sep, bf); else - fprintf(fp, "%6.6s", bf); + ret += fprintf(fp, "%6.6s", bf); } } @@ -528,7 +528,7 @@ static size_t hist_entry__fprintf(struct hist_entry *self, if (se->elide) continue; - fprintf(fp, "%s", sep ?: " "); + ret += fprintf(fp, "%s", sep ?: " "); ret += se->print(fp, self, se->width ? *se->width : 0); } @@ -544,8 +544,8 @@ static size_t hist_entry__fprintf(struct hist_entry *self, left_margin -= thread__comm_len(self->thread); } - hist_entry_callchain__fprintf(fp, self, session_total, - left_margin); + ret += hist_entry_callchain__fprintf(fp, self, session_total, + left_margin); } return ret; -- cgit v0.10.2 From f9224c5c944b60cf709db4adf1f5195264b8d194 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 11 Mar 2010 20:12:44 -0300 Subject: perf report: Implement initial UI using newt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newt has widespread availability and provides a rather simple API as can be seen by the size of this patch. The work needed to support it will benefit other frontends too. In this initial patch it just checks if the output is a tty, if not it falls back to the previous behaviour, also if newt-devel/libnewt-dev is not installed the previous behaviour is maintaned. Pressing enter on a symbol will annotate it, ESC in the annotation window will return to the report symbol list. More work will be done to remove the special casing in color_fprintf, stop using fmemopen/FILE in the printing of hist_entries, etc. Also the annotation doesn't need to be done via spawning "perf annotate" and then browsing its output, we can do better by calling directly the builtin-annotate.c functions, that would then be moved to tools/perf/util/annotate.c and shared with perf top, etc But lets go by baby steps, this patch already improves perf usability by allowing to quickly do annotations on symbols from the report screen and provides a first experimentation with libnewt/TUI integration of tools. Tested on RHEL5 and Fedora12 X86_64 and on Debian PARISC64 to browse a perf.data file collected on a Fedora12 x86_64 box. Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268349164-5822-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 8a8f52d..0abd25e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -513,6 +513,14 @@ else LIB_OBJS += util/probe-finder.o endif +ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) + msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); + BASIC_CFLAGS += -DNO_NEWT_SUPPORT +else + EXTLIBS += -lnewt + LIB_OBJS += util/newt.o +endif + ifndef NO_LIBPERL PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f815de2..1f9f869 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -267,6 +267,7 @@ static int __cmd_report(void) int ret = -EINVAL; struct perf_session *session; struct rb_node *next; + const char *help = "For a higher level overview, try: perf report --sort comm,dso"; session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) @@ -301,30 +302,38 @@ static int __cmd_report(void) stats = rb_entry(next, struct event_stat_id, rb_node); perf_session__collapse_resort(&stats->hists); perf_session__output_resort(&stats->hists, stats->stats.total); - if (rb_first(&session->stats_by_id) == - rb_last(&session->stats_by_id)) - fprintf(stdout, "# Samples: %Ld\n#\n", - stats->stats.total); - else - fprintf(stdout, "# Samples: %Ld %s\n#\n", - stats->stats.total, - __event_name(stats->type, stats->config)); - perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, + if (use_browser) + perf_session__browse_hists(&stats->hists, + stats->stats.total, help); + else { + if (rb_first(&session->stats_by_id) == + rb_last(&session->stats_by_id)) + fprintf(stdout, "# Samples: %Ld\n#\n", + stats->stats.total); + else + fprintf(stdout, "# Samples: %Ld %s\n#\n", + stats->stats.total, + __event_name(stats->type, stats->config)); + + perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, stats->stats.total); - fprintf(stdout, "\n\n"); + fprintf(stdout, "\n\n"); + } + next = rb_next(&stats->rb_node); } - if (sort_order == default_sort_order && - parent_pattern == default_parent_pattern) - fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); + if (!use_browser && sort_order == default_sort_order && + parent_pattern == default_parent_pattern) { + fprintf(stdout, "#\n# (%s)\n#\n", help); - if (show_threads) { - bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); - perf_read_values_display(stdout, &show_threads_values, - raw_printing_style); - perf_read_values_destroy(&show_threads_values); + if (show_threads) { + bool style = !strcmp(pretty_printing_style, "raw"); + perf_read_values_display(stdout, &show_threads_values, + style); + perf_read_values_destroy(&show_threads_values); + } } out_delete: perf_session__delete(session); @@ -447,7 +456,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, report_usage, 0); - setup_pager(); + setup_browser(); if (symbol__init() < 0) return -1; diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 57cb107..9ff186b 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -265,6 +265,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) if (status) return status & 0xff; + exit_browser(); + /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 918eb37..47b12a3 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -1,6 +1,7 @@ #ifndef __PERF_CACHE_H #define __PERF_CACHE_H +#include #include "util.h" #include "strbuf.h" #include "../perf.h" @@ -69,6 +70,19 @@ extern const char *pager_program; extern int pager_in_use(void); extern int pager_use_color; +extern bool use_browser; + +#ifdef NO_NEWT_SUPPORT +static inline void setup_browser(void) +{ + setup_pager(); +} +static inline void exit_browser(void) {} +#else +void setup_browser(void); +void exit_browser(void); +#endif + extern const char *editor_program; extern const char *excludes_file; diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e88bca5..9da0191 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -203,7 +203,10 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int r; va_start(args, fmt); - r = color_vfprintf(fp, color, fmt, args); + if (use_browser) + r = vfprintf(fp, fmt, args); + else + r = color_vfprintf(fp, color, fmt, args); va_end(args); return r; } diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 0905600..033d66d 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -6,6 +6,7 @@ #include #include +#include "cache.h" #include "color.h" #include "event.h" #include "debug.h" @@ -21,7 +22,10 @@ int eprintf(int level, const char *fmt, ...) if (verbose >= level) { va_start(args, fmt); - ret = vfprintf(stderr, fmt, args); + if (use_browser) + ret = browser__show_help(fmt, args); + else + ret = vfprintf(stderr, fmt, args); va_end(args); } diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 58720a1..03accb8 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -9,5 +9,6 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); +int browser__show_help(const char *format, va_list ap); #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 16f360c..fe366ce 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -18,6 +18,11 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); +size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total); void hist_entry__free(struct hist_entry *); void perf_session__output_resort(struct rb_root *hists, u64 total_samples); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c new file mode 100644 index 0000000..3d3a936 --- /dev/null +++ b/tools/perf/util/newt.c @@ -0,0 +1,194 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE + +#include +#include + +#include "cache.h" +#include "hist.h" +#include "session.h" +#include "sort.h" +#include "symbol.h" + +static size_t hist_entry__append_browser(struct hist_entry *self, + newtComponent listbox, u64 total) +{ + char bf[1024]; + size_t len; + FILE *fp; + + if (symbol_conf.exclude_other && !self->parent) + return 0; + + fp = fmemopen(bf, sizeof(bf), "w"); + if (fp == NULL) + return 0; + + len = hist_entry__fprintf(self, NULL, false, 0, fp, total); + + fclose(fp); + newtListboxAppendEntry(listbox, bf, self); + return len; +} + +static void hist_entry__annotate_browser(struct hist_entry *self) +{ + FILE *fp; + struct winsize ws; + newtComponent form, listbox; + struct newtExitStruct es; + char *str; + size_t line_len, max_line_len = 0; + size_t max_usable_width; + char *line = NULL; + + if (self->sym == NULL) + return; + + if (asprintf(&str, "perf annotate %s | expand", self->sym->name) < 0) + return; + + fp = popen(str, "r"); + if (fp == NULL) + goto out_free_str; + + newtPushHelpLine("Press ESC to exit"); + get_term_dimensions(&ws); + listbox = newtListbox(0, 0, ws.ws_row - 5, NEWT_FLAG_SCROLL); + + while (!feof(fp)) { + if (getline(&line, &line_len, fp) < 0 || !line_len) + break; + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + if (line_len > max_line_len) + max_line_len = line_len; + newtListboxAppendEntry(listbox, line, NULL); + } + fclose(fp); + free(line); + + max_usable_width = ws.ws_col - 22; + if (max_line_len > max_usable_width) + max_line_len = max_usable_width; + + newtListboxSetWidth(listbox, max_line_len); + + newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); + form = newtForm(NULL, NULL, 0); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddComponents(form, listbox, NULL); + + newtFormRun(form, &es); + newtFormDestroy(form); + newtPopWindow(); + newtPopHelpLine(); +out_free_str: + free(str); +} + +void perf_session__browse_hists(struct rb_root *hists, u64 session_total, + const char *helpline) +{ + struct sort_entry *se; + struct rb_node *nd; + unsigned int width; + char *col_width = symbol_conf.col_width_list_str; + struct winsize ws; + size_t max_len = 0; + char str[1024]; + newtComponent form, listbox; + struct newtExitStruct es; + + snprintf(str, sizeof(str), "Samples: %Ld", session_total); + newtDrawRootText(0, 0, str); + newtPushHelpLine(helpline); + + get_term_dimensions(&ws); + + form = newtForm(NULL, NULL, 0); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + + listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | + NEWT_FLAG_BORDER | + NEWT_FLAG_RETURNEXIT)); + + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (se->elide) + continue; + width = strlen(se->header); + if (se->width) { + if (symbol_conf.col_width_list_str) { + if (col_width) { + *se->width = atoi(col_width); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; + } + } + *se->width = max(*se->width, width); + } + } + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + size_t len = hist_entry__append_browser(h, listbox, session_total); + if (len > max_len) + max_len = len; + } + + newtListboxSetWidth(listbox, max_len); + newtFormAddComponents(form, listbox, NULL); + + while (1) { + struct hist_entry *selection; + + newtFormRun(form, &es); + if (es.reason == NEWT_EXIT_HOTKEY) + break; + selection = newtListboxGetCurrent(listbox); + hist_entry__annotate_browser(selection); + } + + newtFormDestroy(form); +} + +int browser__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + static char msg[1024]; + + ret = vsnprintf(msg + backlog, sizeof(msg) - backlog, format, ap); + backlog += ret; + + if (msg[backlog - 1] == '\n') { + newtPopHelpLine(); + newtPushHelpLine(msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} + +bool use_browser; + +void setup_browser(void) +{ + if (!isatty(1)) + return; + + use_browser = true; + newtInit(); + newtCls(); + newtPushHelpLine(" "); +} + +void exit_browser(void) +{ + if (use_browser) + newtFinished(); +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5c33417..34d7339 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -86,4 +86,13 @@ static inline struct map * { return map_groups__new_module(&self->kmaps, start, filename); } + +#ifdef NO_NEWT_SUPPORT +static inline void perf_session__browse_hists(struct rb_root *hists __used, + u64 session_total __used, + const char *helpline __used) {} +#else +void perf_session__browse_hists(struct rb_root *hists, u64 session_total, + const char *helpline); +#endif #endif /* __PERF_SESSION_H */ -- cgit v0.10.2 From 7081e087b90d4eb4348f7970bd6b266d837321ef Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 10:48:12 -0300 Subject: perf newt: Add 'Q', 'q' and Ctrl+C as ways to exit from forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are keys people expect when pressed to exit the current widget, so have associate all of them to this semantic. Suggested-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268401692-9361-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3d3a936..2a4308a 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -4,6 +4,7 @@ #include #include +#include #include "cache.h" #include "hist.h" @@ -11,6 +12,22 @@ #include "sort.h" #include "symbol.h" +static void newt_form__set_exit_keys(newtComponent self) +{ + newtFormAddHotKey(self, NEWT_KEY_ESCAPE); + newtFormAddHotKey(self, 'Q'); + newtFormAddHotKey(self, 'q'); + newtFormAddHotKey(self, CTRL('c')); +} + +static newtComponent newt_form__new(void) +{ + newtComponent self = newtForm(NULL, NULL, 0); + if (self) + newt_form__set_exit_keys(self); + return self; +} + static size_t hist_entry__append_browser(struct hist_entry *self, newtComponent listbox, u64 total) { @@ -77,8 +94,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) newtListboxSetWidth(listbox, max_line_len); newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); - form = newtForm(NULL, NULL, 0); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + form = newt_form__new(); newtFormAddComponents(form, listbox, NULL); newtFormRun(form, &es); @@ -108,8 +124,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, get_term_dimensions(&ws); - form = newtForm(NULL, NULL, 0); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + form = newt_form__new(); listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | NEWT_FLAG_BORDER | -- cgit v0.10.2 From cb7afb7092bc502b890f0a897ffd67c2b078d347 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 12:46:47 -0300 Subject: perf newt: Use newtGetScreenSize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency, use the newt API more fully. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268408808-13595-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2a4308a..f6ec6f5 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -52,7 +52,7 @@ static size_t hist_entry__append_browser(struct hist_entry *self, static void hist_entry__annotate_browser(struct hist_entry *self) { FILE *fp; - struct winsize ws; + int cols, rows; newtComponent form, listbox; struct newtExitStruct es; char *str; @@ -71,8 +71,8 @@ static void hist_entry__annotate_browser(struct hist_entry *self) goto out_free_str; newtPushHelpLine("Press ESC to exit"); - get_term_dimensions(&ws); - listbox = newtListbox(0, 0, ws.ws_row - 5, NEWT_FLAG_SCROLL); + newtGetScreenSize(&cols, &rows); + listbox = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); while (!feof(fp)) { if (getline(&line, &line_len, fp) < 0 || !line_len) @@ -87,13 +87,13 @@ static void hist_entry__annotate_browser(struct hist_entry *self) fclose(fp); free(line); - max_usable_width = ws.ws_col - 22; + max_usable_width = cols - 22; if (max_line_len > max_usable_width) max_line_len = max_usable_width; newtListboxSetWidth(listbox, max_line_len); - newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); newtFormAddComponents(form, listbox, NULL); @@ -112,7 +112,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, struct rb_node *nd; unsigned int width; char *col_width = symbol_conf.col_width_list_str; - struct winsize ws; + int rows; size_t max_len = 0; char str[1024]; newtComponent form, listbox; @@ -122,13 +122,13 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); - get_term_dimensions(&ws); + newtGetScreenSize(NULL, &rows); form = newt_form__new(); - listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL | - NEWT_FLAG_BORDER | - NEWT_FLAG_RETURNEXIT)); + listbox = newtListbox(1, 1, rows - 2, (NEWT_FLAG_SCROLL | + NEWT_FLAG_BORDER | + NEWT_FLAG_RETURNEXIT)); list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) -- cgit v0.10.2 From 3997d3776a6e89586e76a0ef355bfbbd8a76966c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 12:46:48 -0300 Subject: perf hist: Don't fprintf the callgraph unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [root@doppio ~]# perf report -i newt.data | head -10 # Samples: 11999679868 # # Overhead Command Shared Object Symbol # ........ ....... ............................. ...... # 63.61% perf libslang.so.2.1.4 [.] SLsmg_write_chars 6.30% perf perf [.] symbols__find 2.19% perf libnewt.so.0.52.10 [.] newtListboxAppendEntry 2.08% perf libslang.so.2.1.4 [.] SLsmg_write_chars@plt 1.99% perf libc-2.10.2.so [.] _IO_vfprintf_internal [root@doppio ~]# Not good, the newt form for report works, but slang has to eat the cost of the additional callgraph lines everytime it prints a line, and the callgraph doesn't appear on the screen, so move the callgraph printing to a separate function and don't use it in newt.c. Newt tree widgets are being investigated to properly support callgraphs, but till that gets merged, lets remove this huge overhead and show at least the symbol overheads for a callgraph rich perf.data with good performance. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268408808-13595-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d43be34..1a4e837 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -532,23 +532,23 @@ size_t hist_entry__fprintf(struct hist_entry *self, ret += se->print(fp, self, se->width ? *se->width : 0); } - ret += fprintf(fp, "\n"); - - if (symbol_conf.use_callchain) { - int left_margin = 0; + return ret + fprintf(fp, "\n"); +} - if (sort__first_dimension == SORT_COMM) { - se = list_first_entry(&hist_entry__sort_list, typeof(*se), - list); - left_margin = se->width ? *se->width : 0; - left_margin -= thread__comm_len(self->thread); - } +static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, + u64 session_total) +{ + int left_margin = 0; - ret += hist_entry_callchain__fprintf(fp, self, session_total, - left_margin); + if (sort__first_dimension == SORT_COMM) { + struct sort_entry *se = list_first_entry(&hist_entry__sort_list, + typeof(*se), list); + left_margin = se->width ? *se->width : 0; + left_margin -= thread__comm_len(self->thread); } - return ret; + return hist_entry_callchain__fprintf(fp, self, session_total, + left_margin); } size_t perf_session__fprintf_hists(struct rb_root *hists, @@ -655,6 +655,10 @@ print_entries: } ret += hist_entry__fprintf(h, pair, show_displacement, displacement, fp, session_total); + + if (symbol_conf.use_callchain) + ret += hist_entry__fprintf_callchain(h, fp, session_total); + if (h->map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, fp); -- cgit v0.10.2 From 567e54790e5c07152a93b6de4d0210af8b77da87 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 12 Mar 2010 21:05:10 -0300 Subject: perf tools: Fix non-newt build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use_browser needs to be in a file that is always built and also we need a browser__show_help stub in that case. Reported-by: Anton Blanchard Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1268438710-32697-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 9ff186b..0d4b9ed 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -16,6 +16,8 @@ #include "util/string.h" #include "util/debugfs.h" +bool use_browser; + const char perf_usage_string[] = "perf [--version] [--help] COMMAND [ARGS]"; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 03accb8..0172edf 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -9,6 +9,14 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); + +#ifdef NO_NEWT_SUPPORT +static inline int browser__show_help(const char *format __used, va_list ap __used) +{ + return 0; +} +#else int browser__show_help(const char *format, va_list ap); +#endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f6ec6f5..2836394 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -189,8 +189,6 @@ int browser__show_help(const char *format, va_list ap) return ret; } -bool use_browser; - void setup_browser(void) { if (!isatty(1)) -- cgit v0.10.2 From 8576e1971663ffdb6139041de97cdd2e1d4791cc Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 13 Mar 2010 11:11:16 +0300 Subject: x86, perf: Unmask LVTPC only if we have APIC supported Ingo reported: | | There's a build failure on -tip with the P4 driver, on UP 32-bit, if | PERF_EVENTS is enabled but UP_APIC is disabled: | | arch/x86/built-in.o: In function `p4_pmu_handle_irq': | perf_event.c:(.text+0xa756): undefined reference to `apic' | perf_event.c:(.text+0xa76e): undefined reference to `apic' | So we have to unmask LVTPC only if we're configured to have one. Reported-by: Ingo Molnar Signed-off-by: Cyrill Gorcunov CC: Lin Ming CC: Peter Zijlstra LKML-Reference: <20100313081116.GA5179@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 381f593..ef861da1 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -365,8 +365,10 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) } if (handled) { +#ifdef CONFIG_X86_LOCAL_APIC /* p4 quirk: unmask it again */ apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); +#endif inc_irq_stat(apic_perf_irqs); } -- cgit v0.10.2 From e4495262826d1eabca3529fa6ac22394eb348132 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Mon, 15 Mar 2010 12:58:22 +0800 Subject: perf, x86: Enable not tagged retired instruction counting on P4s This should turn on instruction counting on P4s, which was missing in the first version of the new PMU driver. It's inaccurate for now, we still need dependant event to tag mops before we can count them precisely. The result is that the number of instruction may be lifted up. Signed-off-by: Cyrill Gorcunov Signed-off-by: Lin Ming Cc: Peter Zijlstra LKML-Reference: <1268629102.3355.11.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 829f471..b47b9e9 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -324,8 +324,8 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) #define P4_SSE_INPUT_ASSIST P4_EVENT_PACK(0x34, 0x01) /* - * MSR_P4_FIRM_ESCR: 8, 9 - * MSR_P4_FIRM_ESCR: 10, 11 + * MSR_P4_FIRM_ESCR0: 8, 9 + * MSR_P4_FIRM_ESCR1: 10, 11 */ #define P4_PACKED_SP_UOP P4_EVENT_PACK(0x08, 0x01) @@ -462,8 +462,8 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) #define P4_INSTR_RETIRED P4_EVENT_PACK(0x02, 0x04) /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 + * MSR_P4_CRU_ESCR0: 12, 13, 16 + * MSR_P4_CRU_ESCR1: 14, 15, 17 */ #define P4_UOPS_RETIRED P4_EVENT_PACK(0x01, 0x04) diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index ef861da1..a11ce73 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -60,13 +60,11 @@ struct p4_event_template p4_templates[] = { [2] = { .opcode = P4_INSTR_RETIRED, .config = 0, - .dep = 0, /* needs front-end tagging */ + .dep = -1, /* needs front-end tagging */ .emask = P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG) | - P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSTAG) | - P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG) | - P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSTAG), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, .cntr = { 12, 14 }, }, [3] = { -- cgit v0.10.2 From bedbfdea31daf3880745001d56450c683959ee7e Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Mon, 15 Mar 2010 11:46:57 -0300 Subject: perf record: Enable the enable_on_exec flag if record forks the target When forking its target, perf record can capture data from before the target application is started. Perf stat uses the enable_on_exec flag in the event attributes to keep from displaying events from before the target program starts, this patch adds the same functionality to perf record when it is will fork the target process. Signed-off-by: Eric B Munson Signed-off-by: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <1268664418-28328-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index bed175d..962cdbf 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -225,7 +225,7 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n return h_attr; } -static void create_counter(int counter, int cpu, pid_t pid) +static void create_counter(int counter, int cpu, pid_t pid, bool forks) { char *filter = filters[counter]; struct perf_event_attr *attr = attrs + counter; @@ -277,6 +277,9 @@ static void create_counter(int counter, int cpu, pid_t pid) attr->inherit = inherit; attr->disabled = 1; + if (forks) + attr->enable_on_exec = 1; + try_again: fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); @@ -381,13 +384,13 @@ try_again: ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); } -static void open_counters(int cpu, pid_t pid) +static void open_counters(int cpu, pid_t pid, bool forks) { int counter; group_fd = -1; for (counter = 0; counter < nr_counters; counter++) - create_counter(counter, cpu, pid); + create_counter(counter, cpu, pid, forks); nr_cpu++; } @@ -547,11 +550,11 @@ static int __cmd_record(int argc, const char **argv) if ((!system_wide && !inherit) || profile_cpu != -1) { - open_counters(profile_cpu, target_pid); + open_counters(profile_cpu, target_pid, forks); } else { nr_cpus = read_cpu_map(); for (i = 0; i < nr_cpus; i++) - open_counters(cpumap[i], target_pid); + open_counters(cpumap[i], target_pid, forks); } if (file_new) { -- cgit v0.10.2 From b0a9ab62ab96e258a0ddd81d7fe2719c3db36006 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 15 Mar 2010 11:46:58 -0300 Subject: perf top: Properly notify the user that vmlinux is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch this message would very briefly appear on the screen and then the screen would get updates only on the top, for number of interrupts received, etc, but no annotation would be performed: [root@doppio linux-2.6-tip]# perf top -s n_tty_write > /tmp/bla objdump: '[kernel.kallsyms]': No such file Now this is what the user gets: [root@doppio linux-2.6-tip]# perf top -s n_tty_write Can't annotate n_tty_write: No vmlinux file was found in the path: [0] vmlinux [1] /boot/vmlinux [2] /boot/vmlinux-2.6.33-rc5 [3] /lib/modules/2.6.33-rc5/build/vmlinux [4] /usr/lib/debug/lib/modules/2.6.33-rc5/vmlinux [root@doppio linux-2.6-tip]# This bug was introduced when we added automatic search for vmlinux, before that time the user had to specify a vmlinux file. Reported-by: David S. Miller Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: LKML-Reference: <1268664418-28328-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 57e232f..c968bd3 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -169,7 +169,7 @@ static void sig_winch_handler(int sig __used) update_print_entries(&winsize); } -static void parse_source(struct sym_entry *syme) +static int parse_source(struct sym_entry *syme) { struct symbol *sym; struct sym_entry_source *source; @@ -180,12 +180,21 @@ static void parse_source(struct sym_entry *syme) u64 len; if (!syme) - return; + return -1; + + sym = sym_entry__symbol(syme); + map = syme->map; + + /* + * We can't annotate with just /proc/kallsyms + */ + if (map->dso->origin == DSO__ORIG_KERNEL) + return -1; if (syme->src == NULL) { syme->src = zalloc(sizeof(*source)); if (syme->src == NULL) - return; + return -1; pthread_mutex_init(&syme->src->lock, NULL); } @@ -195,9 +204,6 @@ static void parse_source(struct sym_entry *syme) pthread_mutex_lock(&source->lock); goto out_assign; } - - sym = sym_entry__symbol(syme); - map = syme->map; path = map->dso->long_name; len = sym->end - sym->start; @@ -209,7 +215,7 @@ static void parse_source(struct sym_entry *syme) file = popen(command, "r"); if (!file) - return; + return -1; pthread_mutex_lock(&source->lock); source->lines_tail = &source->lines; @@ -245,6 +251,7 @@ static void parse_source(struct sym_entry *syme) out_assign: sym_filter_entry = syme; pthread_mutex_unlock(&source->lock); + return 0; } static void __zero_source_counters(struct sym_entry *syme) @@ -991,7 +998,17 @@ static void event__process_sample(const event_t *self, if (sym_filter_entry_sched) { sym_filter_entry = sym_filter_entry_sched; sym_filter_entry_sched = NULL; - parse_source(sym_filter_entry); + if (parse_source(sym_filter_entry) < 0) { + struct symbol *sym = sym_entry__symbol(sym_filter_entry); + + pr_err("Can't annotate %s", sym->name); + if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { + pr_err(": No vmlinux file was found in the path:\n"); + vmlinux_path__fprintf(stderr); + } else + pr_err(".\n"); + exit(1); + } } syme = symbol__priv(al.sym); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 75cd468..292f941 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -18,18 +18,6 @@ #define NT_GNU_BUILD_ID 3 #endif -enum dso_origin { - DSO__ORIG_KERNEL = 0, - DSO__ORIG_JAVA_JIT, - DSO__ORIG_BUILD_ID_CACHE, - DSO__ORIG_FEDORA, - DSO__ORIG_UBUNTU, - DSO__ORIG_BUILDID, - DSO__ORIG_DSO, - DSO__ORIG_KMODULE, - DSO__ORIG_NOT_FOUND, -}; - static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, @@ -1017,7 +1005,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, } curr_map->map_ip = identity__map_ip; curr_map->unmap_ip = identity__map_ip; - curr_dso->origin = DSO__ORIG_KERNEL; + curr_dso->origin = self->origin; map_groups__insert(kmap->kmaps, curr_map); dsos__add(&dsos__kernel, curr_dso); dso__set_loaded(curr_dso, map->type); @@ -1887,6 +1875,17 @@ out_fail: return -1; } +size_t vmlinux_path__fprintf(FILE *fp) +{ + int i; + size_t printed = 0; + + for (i = 0; i < vmlinux_path__nr_entries; ++i) + printed += fprintf(fp, "[%d] %s\n", i, vmlinux_path[i]); + + return printed; +} + static int setup_list(struct strlist **list, const char *list_str, const char *list_name) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 280dadd..d983bba 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -149,6 +149,19 @@ size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); + +enum dso_origin { + DSO__ORIG_KERNEL = 0, + DSO__ORIG_JAVA_JIT, + DSO__ORIG_BUILD_ID_CACHE, + DSO__ORIG_FEDORA, + DSO__ORIG_UBUNTU, + DSO__ORIG_BUILDID, + DSO__ORIG_DSO, + DSO__ORIG_KMODULE, + DSO__ORIG_NOT_FOUND, +}; + char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); @@ -168,4 +181,6 @@ int kallsyms__parse(const char *filename, void *arg, int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); +size_t vmlinux_path__fprintf(FILE *fp); + #endif /* __PERF_SYMBOL */ -- cgit v0.10.2 From d06d92b7c9b99ea52bdaeb13f544675529891b8a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 15 Mar 2010 13:04:33 -0300 Subject: perf annotate: Properly notify the user that vmlinux is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch we would not find a vmlinux, then try to pass objdump "[kernel.kallsyms]" as the filename, it would get confused and produce no output: [root@doppio ~]# perf annotate n_tty_write ------------------------------------------------ Percent | Source code & Disassembly of [kernel.kallsyms] ------------------------------------------------ Now we check that and emit meaningful warning: [root@doppio ~]# perf annotate n_tty_write Can't annotate n_tty_write: No vmlinux file was found in the path: [0] vmlinux [1] /boot/vmlinux [2] /boot/vmlinux-2.6.34-rc1-tip+ [3] /lib/modules/2.6.34-rc1-tip+/build/vmlinux [4] /usr/lib/debug/lib/modules/2.6.34-rc1-tip+/vmlinux [root@doppio ~]# This bug was introduced when we added automatic search for vmlinux, before that time the user had to specify a vmlinux file. v2: Print the warning just for the first symbol found when no symbol name is specified, otherwise it will spam the screen repeating the warning for each symbol. Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: LKML-Reference: <1268669073-6856-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6ad7148..45d1466 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -452,6 +452,16 @@ static void annotate_sym(struct hist_entry *he) if (!filename) return; + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path:\n", sym->name); + vmlinux_path__fprintf(stderr); + return; + } + pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2836394..2d19e7a 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -63,7 +63,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate %s | expand", self->sym->name) < 0) + if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->sym->name) < 0) return; fp = popen(str, "r"); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d983bba..5bd91d1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -106,6 +106,7 @@ struct dso { u8 has_build_id:1; u8 kernel:1; u8 hit:1; + u8 annotate_warned:1; unsigned char origin; u8 sorted_by_name; u8 loaded; -- cgit v0.10.2 From 1d199b1ad606ae8b88acebd295b101c4e1cf2a57 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 16 Mar 2010 01:05:02 +0100 Subject: perf: Fix unexported generic perf_arch_fetch_caller_regs perf_arch_fetch_caller_regs() is exported for the overriden x86 version, but not for the generic weak version. As a general rule, weak functions should not have their symbol exported in the same file they are defined. So let's export it on trace_event_perf.c as it is used by trace events only. This fixes: ERROR: ".perf_arch_fetch_caller_regs" [fs/xfs/xfs.ko] undefined! ERROR: ".perf_arch_fetch_caller_regs" [arch/powerpc/platforms/cell/spufs/spufs.ko] undefined! -v2: And also only build it if trace events are enabled. -v3: Fix changelog mistake Reported-by: Stephen Rothwell Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Xiao Guangrong Cc: Paul Mackerras LKML-Reference: <1268697902-9518-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 978d297..0d3466c 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1695,6 +1695,7 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) return entry; } +#ifdef CONFIG_EVENT_TRACING void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) { regs->ip = ip; @@ -1706,4 +1707,4 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski regs->cs = __KERNEL_CS; local_save_flags(regs->flags); } -EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs); +#endif diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 8bf6127..455393e 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2790,10 +2790,12 @@ __weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) return NULL; } +#ifdef CONFIG_EVENT_TRACING __weak void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) { } +#endif /* * Output diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 0709e4f..7d79a10 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -12,6 +12,8 @@ DEFINE_PER_CPU(struct pt_regs, perf_trace_regs); EXPORT_PER_CPU_SYMBOL_GPL(perf_trace_regs); +EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs); + static char *perf_trace_buf; static char *perf_trace_buf_nmi; -- cgit v0.10.2 From 8ea7f544100844307072cae2f5fc108afdef999a Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 16 Mar 2010 10:12:36 +0800 Subject: x86, perf: Fix comments in Pentium-4 PMU definitions Reported-by: Cyrill Gorcunov Signed-off-by: Lin Ming Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker LKML-Reference: <1268705556.3379.8.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index b47b9e9..b842b32 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -319,6 +319,7 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) #define P4_BSQ_ACTIVE_ENTRIES P4_EVENT_PACK(0x06, 0x07) /* + * NOTE: no ESCR name in docs, it's guessed * MSR_P4_BSU_ESCR1: 2, 3 */ @@ -468,8 +469,8 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) #define P4_UOPS_RETIRED P4_EVENT_PACK(0x01, 0x04) /* - * MSR_P4_CRU_ESCR2: 12, 13, 16 - * MSR_P4_CRU_ESCR3: 14, 15, 17 + * MSR_P4_CRU_ESCR0: 12, 13, 16 + * MSR_P4_CRU_ESCR1: 14, 15, 17 */ #define P4_UOP_TYPE P4_EVENT_PACK(0x02, 0x02) -- cgit v0.10.2 From 5cc718b9dad682329a60e73547c6e708faa5bbe4 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 15 Mar 2010 13:00:54 -0400 Subject: kprobes: Hide CONFIG_OPTPROBES and set if arch supports optimized kprobes Hide CONFIG_OPTPROBES and set if the arch supports optimized kprobes (IOW, HAVE_OPTPROBES=y), since this option doesn't change the major behavior of kprobes, and workarounds for minor changes are documented. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Dieter Ries Cc: Ananth N Mavinakayanahalli Cc: OGAWA Hirofumi Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker LKML-Reference: <20100315170054.31593.3153.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 2f9115c..61c291c 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -165,8 +165,8 @@ the user entry_handler invocation is also skipped. 1.4 How Does Jump Optimization Work? -If you configured your kernel with CONFIG_OPTPROBES=y (currently -this option is supported on x86/x86-64, non-preemptive kernel) and +If your kernel is built with CONFIG_OPTPROBES=y (currently this flag +is automatically set 'y' on x86/x86-64, non-preemptive kernel) and the "debug.kprobes_optimization" kernel parameter is set to 1 (see sysctl(8)), Kprobes tries to reduce probe-hit overhead by using a jump instruction instead of a breakpoint instruction at each probepoint. @@ -271,8 +271,6 @@ tweak the kernel's execution path, you need to suppress optimization, using one of the following techniques: - Specify an empty function for the kprobe's post_handler or break_handler. or -- Config CONFIG_OPTPROBES=n. - or - Execute 'sysctl -w debug.kprobes_optimization=n' 2. Architectures Supported @@ -307,10 +305,6 @@ it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO), so you can use "objdump -d -l vmlinux" to see the source-to-object code mapping. -If you want to reduce probing overhead, set "Kprobes jump optimization -support" (CONFIG_OPTPROBES) to "y". You can find this option under the -"Kprobes" line. - 4. API Reference The Kprobes API includes a "register" function and an "unregister" diff --git a/arch/Kconfig b/arch/Kconfig index e5eb133..f06010f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -42,15 +42,10 @@ config KPROBES If in doubt, say "N". config OPTPROBES - bool "Kprobes jump optimization support (EXPERIMENTAL)" - default y - depends on KPROBES + def_bool y + depends on KPROBES && HAVE_OPTPROBES depends on !PREEMPT - depends on HAVE_OPTPROBES select KALLSYMS_ALL - help - This option will allow kprobes to optimize breakpoint to - a jump for reducing its overhead. config HAVE_EFFICIENT_UNALIGNED_ACCESS bool -- cgit v0.10.2 From 984763cb90d4b5444baa0c3e43feff7926bf1834 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 16 Mar 2010 17:07:33 +0100 Subject: perf, x86: Report error code that returned from x86_pmu.hw_config() If x86_pmu.hw_config() fails a fixed error code (-EOPNOTSUPP) is returned even if a different error was reported. This patch fixes this. Signed-off-by: Robert Richter Acked-by: Cyrill Gorcunov Acked-by: Lin Ming Cc: acme@redhat.com Cc: eranian@google.com Cc: gorcunov@openvz.org Cc: peterz@infradead.org Cc: fweisbec@gmail.com LKML-Reference: <20100316160733.GR1585@erda.amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 0d3466c..5dacf63 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -472,8 +472,9 @@ static int __hw_perf_event_init(struct perf_event *event) hwc->last_tag = ~0ULL; /* Processor specifics */ - if (x86_pmu.hw_config(attr, hwc)) - return -EOPNOTSUPP; + err = x86_pmu.hw_config(attr, hwc); + if (err) + return err; if (!hwc->sample_period) { hwc->sample_period = x86_pmu.max_period; -- cgit v0.10.2 From a6b84574eed7e4fd8cb8dac2d0926fe2cf34b941 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 16 Mar 2010 01:05:02 +0100 Subject: perf: Fix unexported generic perf_arch_fetch_caller_regs perf_arch_fetch_caller_regs() is exported for the overriden x86 version, but not for the generic weak version. As a general rule, weak functions should not have their symbol exported in the same file they are defined. So let's export it on trace_event_perf.c as it is used by trace events only. This fixes: ERROR: ".perf_arch_fetch_caller_regs" [fs/xfs/xfs.ko] undefined! ERROR: ".perf_arch_fetch_caller_regs" [arch/powerpc/platforms/cell/spufs/spufs.ko] undefined! -v2: And also only build it if trace events are enabled. -v3: Fix changelog mistake Reported-by: Stephen Rothwell Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Xiao Guangrong Cc: Paul Mackerras LKML-Reference: <1268697902-9518-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 7645fae..60398a0 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1702,6 +1702,7 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) return entry; } +#ifdef CONFIG_EVENT_TRACING void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) { regs->ip = ip; @@ -1713,4 +1714,4 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski regs->cs = __KERNEL_CS; local_save_flags(regs->flags); } -EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs); +#endif diff --git a/kernel/perf_event.c b/kernel/perf_event.c index fb3031c..574ee58 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2786,10 +2786,12 @@ __weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) return NULL; } +#ifdef CONFIG_EVENT_TRACING __weak void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int skip) { } +#endif /* * Output diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 0709e4f..7d79a10 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -12,6 +12,8 @@ DEFINE_PER_CPU(struct pt_regs, perf_trace_regs); EXPORT_PER_CPU_SYMBOL_GPL(perf_trace_regs); +EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs); + static char *perf_trace_buf; static char *perf_trace_buf_nmi; -- cgit v0.10.2 From a1d37d5285bcda07f9c0b80a2634ca20ab545297 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:21 -0400 Subject: perf tools: Introduce xzalloc() for detecting out of memory conditions Introducing xzalloc() which wrapping zalloc() for detecting out of memory conditions. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220521.32050.85155.stgit@localhost6.localdomain6> [ -v2: small cleanups in surrounding code ] Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f5b2a6..5270108 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -295,6 +295,13 @@ extern void *xmemdupz(const void *data, size_t len); extern char *xstrndup(const char *str, size_t len); extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); +static inline void *xzalloc(size_t size) +{ + void *buf = xmalloc(size); + + return memset(buf, 0, size); +} + static inline void *zalloc(size_t size) { return calloc(1, size); @@ -309,6 +316,7 @@ static inline int has_extension(const char *filename, const char *ext) { size_t len = strlen(filename); size_t extlen = strlen(ext); + return len > extlen && !memcmp(filename + len - extlen, ext, extlen); } @@ -322,6 +330,7 @@ static inline int has_extension(const char *filename, const char *ext) #undef isalnum #undef tolower #undef toupper + extern unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 -- cgit v0.10.2 From 31facc5f1ac674fbcc29f212377e589396bb934c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:30 -0400 Subject: perf probe: Use wrapper functions Use wrapped functions as much as possible, to check out of memory conditions in perf probe. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220530.32050.53951.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 152d6c9..b6afe7b 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -87,9 +87,7 @@ static void parse_probe_event_argv(int argc, const char **argv) 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."); + buf = xzalloc(len + 1); len = 0; for (i = 0; i < argc; i++) len += sprintf(&buf[len], "%s ", argv[i]); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7c004b6..88a3b6d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -33,6 +33,7 @@ #include #undef _GNU_SOURCE +#include "util.h" #include "event.h" #include "string.h" #include "strlist.h" @@ -90,9 +91,9 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) if (*tmp != '\0') semantic_error("Tailing with invalid character '%d'.", *tmp); - tmp = strndup(arg, (ptr - arg)); + tmp = xstrndup(arg, (ptr - arg)); } else - tmp = strdup(arg); + tmp = xstrdup(arg); if (strchr(tmp, '.')) lr->file = tmp; @@ -135,7 +136,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.", arg); - pp->event = strdup(arg); + pp->event = xstrdup(arg); arg = tmp; } @@ -147,17 +148,16 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) /* Check arg is function or file and copy it */ if (strchr(arg, '.')) /* File */ - pp->file = strdup(arg); + pp->file = xstrdup(arg); else /* Function */ - pp->function = strdup(arg); - DIE_IF(pp->file == NULL && pp->function == NULL); + pp->function = xstrdup(arg); /* Parse other options */ while (ptr) { arg = ptr; c = nc; if (c == ';') { /* Lazy pattern must be the last part */ - pp->lazy_line = strdup(arg); + pp->lazy_line = xstrdup(arg); break; } ptr = strpbrk(arg, ";:+@%"); @@ -181,8 +181,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) case '@': /* File name */ if (pp->file) semantic_error("SRC@SRC is not allowed."); - pp->file = strdup(arg); - DIE_IF(pp->file == NULL); + pp->file = xstrdup(arg); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { @@ -247,11 +246,9 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp, /* Copy arguments and ensure return probe has no C argument */ pp->nr_args = argc - 1; - pp->args = zalloc(sizeof(char *) * pp->nr_args); + pp->args = xzalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = strdup(argv[i + 1]); - if (!pp->args[i]) - die("Failed to copy argument."); + pp->args[i] = xstrdup(argv[i + 1]); if (is_c_varname(pp->args[i])) { if (pp->retprobe) semantic_error("You can't specify local" @@ -299,14 +296,12 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) pp->file = NULL; pp->nr_args = argc - 2; - pp->args = zalloc(sizeof(char *) * pp->nr_args); + pp->args = xzalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ *p = '\0'; - pp->args[i] = strdup(argv[i + 2]); - if (!pp->args[i]) - die("Failed to copy argument."); + pp->args[i] = xstrdup(argv[i + 2]); } argv_free(argv); @@ -319,10 +314,8 @@ int synthesize_perf_probe_point(struct probe_point *pp) char offs[64] = "", line[64] = ""; int ret; - pp->probes[0] = buf = zalloc(MAX_CMDLEN); + pp->probes[0] = buf = xzalloc(MAX_CMDLEN); pp->found = 1; - if (!buf) - die("Failed to allocate memory by zalloc."); if (pp->offset) { ret = e_snprintf(offs, 64, "+%d", pp->offset); if (ret <= 0) @@ -380,9 +373,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) char *buf; int i, len, ret; - pp->probes[0] = buf = zalloc(MAX_CMDLEN); - if (!buf) - die("Failed to allocate memory by zalloc."); + pp->probes[0] = buf = xzalloc(MAX_CMDLEN); ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); if (ret <= 0) goto error; @@ -612,10 +603,9 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, for (j = 0; j < nr_probes; j++) { pp = probes + j; if (!pp->event) - pp->event = strdup(pp->function); + pp->event = xstrdup(pp->function); if (!pp->group) - pp->group = strdup(PERFPROBE_GROUP); - DIE_IF(!pp->event || !pp->group); + pp->group = xstrdup(PERFPROBE_GROUP); /* If force_add is true, suffix search is allowed */ allow_suffix = force_add; for (i = 0; i < pp->found; i++) { @@ -709,9 +699,7 @@ void del_trace_kprobe_events(struct strlist *dellist) namelist = get_perf_event_names(fd, true); strlist__for_each(ent, dellist) { - str = strdup(ent->s); - if (!str) - die("Failed to copy event."); + str = xstrdup(ent->s); pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c171a24..e887bb6 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -125,8 +125,7 @@ static void line_list__add_line(struct list_head *head, unsigned int line) p = head; found: pr_debug("line list: add a line %u\n", line); - ln = zalloc(sizeof(struct line_node)); - DIE_IF(ln == NULL); + ln = xzalloc(sizeof(struct line_node)); ln->line = line; INIT_LIST_HEAD(&ln->list); list_add(&ln->list, p); @@ -416,7 +415,7 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) (unsigned long)(pf->addr - eaddr)); /* Copy the function name if possible */ if (!pp->function) { - pp->function = strdup(name); + pp->function = xstrdup(name); pp->offset = (size_t)(pf->addr - eaddr); } } else { @@ -425,7 +424,7 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) (uintmax_t)pf->addr); if (!pp->function) { /* TODO: Use _stext */ - pp->function = strdup(""); + pp->function = xstrdup(""); pp->offset = (size_t)pf->addr; } } @@ -456,7 +455,7 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) if (pp->found == MAX_PROBES) die("Too many( > %d) probe point found.\n", MAX_PROBES); - pp->probes[pp->found] = strdup(tmp); + pp->probes[pp->found] = xstrdup(tmp); pp->found++; } @@ -506,8 +505,7 @@ static int find_lazy_match_lines(struct list_head *head, if (fd < 0) die("failed to open %s", fname); DIE_IF(fstat(fd, &st) < 0); - fbuf = malloc(st.st_size + 2); - DIE_IF(fbuf == NULL); + fbuf = xmalloc(st.st_size + 2); DIE_IF(read(fd, fbuf, st.st_size) < 0); close(fd); fbuf[st.st_size] = '\n'; /* Dummy line */ @@ -727,7 +725,7 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) /* Copy real path */ if (!lf->lr->path) - lf->lr->path = strdup(src); + lf->lr->path = xstrdup(src); line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); } /* Update status */ -- cgit v0.10.2 From e0faa8d35845bb1893cf9e608a5a5d92e9390bf0 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:37 -0400 Subject: perf probe: Move add-probe routine to util/ Move add-probe routine to util/probe_event.c. This simplifies main routine for reducing maintenance cost. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220537.32050.72214.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index b6afe7b..2087034 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -36,11 +36,9 @@ #include "builtin.h" #include "util/util.h" #include "util/strlist.h" -#include "util/event.h" +#include "util/symbol.h" #include "util/debug.h" #include "util/debugfs.h" -#include "util/symbol.h" -#include "util/thread.h" #include "util/parse-options.h" #include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" @@ -57,8 +55,6 @@ static struct { int nr_probe; struct probe_point probes[MAX_PROBES]; struct strlist *dellist; - struct map_groups kmap_groups; - struct map *kmaps[MAP__NR_TYPES]; struct line_range line_range; } session; @@ -114,29 +110,7 @@ static int opt_del_probe_event(const struct option *opt __used, return 0; } -/* Currently just checking function name from symbol map */ -static void evaluate_probe_point(struct probe_point *pp) -{ - struct symbol *sym; - sym = map__find_symbol_by_name(session.kmaps[MAP__FUNCTION], - pp->function, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - pp->function); -} - #ifndef NO_DWARF_SUPPORT -static int open_vmlinux(void) -{ - if (map__load(session.kmaps[MAP__FUNCTION], NULL) < 0) { - pr_debug("Failed to load kernel map.\n"); - return -EINVAL; - } - pr_debug("Try to open %s\n", - session.kmaps[MAP__FUNCTION]->dso->long_name); - return open(session.kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); -} - static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { @@ -204,31 +178,8 @@ static const struct option options[] = { OPT_END() }; -/* Initialize symbol maps for vmlinux */ -static void init_vmlinux(void) -{ - symbol_conf.sort_by_name = true; - if (symbol_conf.vmlinux_name == NULL) - symbol_conf.try_vmlinux_path = true; - else - pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); - if (symbol__init() < 0) - die("Failed to init symbol map."); - - map_groups__init(&session.kmap_groups); - if (map_groups__create_kernel_maps(&session.kmap_groups, - session.kmaps) < 0) - die("Failed to create kernel maps."); -} - int cmd_probe(int argc, const char **argv, const char *prefix __used) { - int i, ret; -#ifndef NO_DWARF_SUPPORT - int fd; -#endif - struct probe_point *pp; - argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (argc > 0) { @@ -267,14 +218,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) " --add/--del.\n"); usage_with_options(probe_usage, options); } - init_vmlinux(); - fd = open_vmlinux(); - if (fd < 0) - die("Could not open debuginfo file."); - ret = find_line_range(fd, &session.line_range); - if (ret <= 0) - die("Source line is not found.\n"); - close(fd); + show_line_range(&session.line_range); return 0; } @@ -287,72 +231,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) return 0; } - /* Add probes */ - init_vmlinux(); - - if (session.need_dwarf) -#ifdef NO_DWARF_SUPPORT - die("Debuginfo-analysis is not supported"); -#else /* !NO_DWARF_SUPPORT */ - pr_debug("Some probes require debuginfo.\n"); - - fd = open_vmlinux(); - if (fd < 0) { - if (session.need_dwarf) - die("Could not open debuginfo file."); - - pr_debug("Could not open vmlinux/module file." - " Try to use symbols.\n"); - goto end_dwarf; - } - - /* Searching probe points */ - for (i = 0; i < session.nr_probe; i++) { - pp = &session.probes[i]; - if (pp->found) - continue; - - lseek(fd, SEEK_SET, 0); - ret = find_probe_point(fd, pp); - if (ret > 0) - continue; - if (ret == 0) { /* No error but failed to find probe point. */ - synthesize_perf_probe_point(pp); - die("Probe point '%s' not found. - probe not added.", - pp->probes[0]); - } - /* Error path */ - if (session.need_dwarf) { - if (ret == -ENOENT) - pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); - } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - break; - } - close(fd); - -end_dwarf: -#endif /* !NO_DWARF_SUPPORT */ - - /* Synthesize probes without dwarf */ - for (i = 0; i < session.nr_probe; i++) { - pp = &session.probes[i]; - if (pp->found) /* This probe is already found. */ - continue; - - evaluate_probe_point(pp); - ret = synthesize_trace_kprobe_event(pp); - if (ret == -E2BIG) - die("probe point definition becomes too long."); - else if (ret < 0) - die("Failed to synthesize a probe point."); - } - - /* Settng up probe points */ add_trace_kprobe_events(session.probes, session.nr_probe, - session.force_add); + session.force_add, session.need_dwarf); return 0; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 88a3b6d..1e60a65 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,6 +40,8 @@ #include "debug.h" #include "cache.h" #include "color.h" +#include "symbol.h" +#include "thread.h" #include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" @@ -65,6 +67,38 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } + +static struct map_groups kmap_groups; +static struct map *kmaps[MAP__NR_TYPES]; + +/* Initialize symbol maps for vmlinux */ +static void init_vmlinux(void) +{ + symbol_conf.sort_by_name = true; + if (symbol_conf.vmlinux_name == NULL) + symbol_conf.try_vmlinux_path = true; + else + pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + if (symbol__init() < 0) + die("Failed to init symbol map."); + + map_groups__init(&kmap_groups); + if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0) + die("Failed to create kernel maps."); +} + +#ifndef NO_DWARF_SUPPORT +static int open_vmlinux(void) +{ + if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { + pr_debug("Failed to load kernel map.\n"); + return -EINVAL; + } + pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); + return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); +} +#endif + void parse_line_range_desc(const char *arg, struct line_range *lr) { const char *ptr; @@ -586,8 +620,8 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add) +static void __add_trace_kprobe_events(struct probe_point *probes, + int nr_probes, bool force_add) { int i, j, fd; struct probe_point *pp; @@ -640,6 +674,92 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, close(fd); } +/* Currently just checking function name from symbol map */ +static void evaluate_probe_point(struct probe_point *pp) +{ + struct symbol *sym; + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + pp->function, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + pp->function); +} + +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add, bool need_dwarf) +{ + int i, ret; + struct probe_point *pp; +#ifndef NO_DWARF_SUPPORT + int fd; +#endif + /* Add probes */ + init_vmlinux(); + + if (need_dwarf) +#ifdef NO_DWARF_SUPPORT + die("Debuginfo-analysis is not supported"); +#else /* !NO_DWARF_SUPPORT */ + pr_debug("Some probes require debuginfo.\n"); + + fd = open_vmlinux(); + if (fd < 0) { + if (need_dwarf) + die("Could not open debuginfo file."); + + pr_debug("Could not open vmlinux/module file." + " Try to use symbols.\n"); + goto end_dwarf; + } + + /* Searching probe points */ + for (i = 0; i < nr_probes; i++) { + pp = &probes[i]; + if (pp->found) + continue; + + lseek(fd, SEEK_SET, 0); + ret = find_probe_point(fd, pp); + if (ret > 0) + continue; + if (ret == 0) { /* No error but failed to find probe point. */ + synthesize_perf_probe_point(pp); + die("Probe point '%s' not found. - probe not added.", + pp->probes[0]); + } + /* Error path */ + if (need_dwarf) { + if (ret == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); + } + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); + break; + } + close(fd); + +end_dwarf: +#endif /* !NO_DWARF_SUPPORT */ + + /* Synthesize probes without dwarf */ + for (i = 0; i < nr_probes; i++) { + pp = &probes[i]; + if (pp->found) /* This probe is already found. */ + continue; + + evaluate_probe_point(pp); + ret = synthesize_trace_kprobe_event(pp); + if (ret == -E2BIG) + die("probe point definition becomes too long."); + else if (ret < 0) + die("Failed to synthesize a probe point."); + } + + /* Settng up probe points */ + __add_trace_kprobe_events(probes, nr_probes, force_add); +} + static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; @@ -759,6 +879,17 @@ void show_line_range(struct line_range *lr) unsigned int l = 1; struct line_node *ln; FILE *fp; + int fd, ret; + + /* Search a line range */ + init_vmlinux(); + fd = open_vmlinux(); + if (fd < 0) + die("Could not open debuginfo file."); + ret = find_line_range(fd, lr); + if (ret <= 0) + die("Source line is not found.\n"); + close(fd); setup_pager(); @@ -788,3 +919,5 @@ void show_line_range(struct line_range *lr) fclose(fp); } + + diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 711287d..3865e16 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -13,7 +13,7 @@ extern int synthesize_perf_probe_event(struct probe_point *pp); extern void parse_trace_kprobe_event(const char *str, 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, - bool force_add); + bool force_add, bool need_dwarf); extern void del_trace_kprobe_events(struct strlist *dellist); extern void show_perf_probe_events(void); extern void show_line_range(struct line_range *lr); -- cgit v0.10.2 From 12a1fadb41b5a6733c36b488b881fb19a28c92d3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:44 -0400 Subject: perf probe: Rename session to param Since this name 'session' conflicts with 'perf_session', and this structure just holds parameters anymore. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220544.32050.8788.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 2087034..f577e14 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -56,20 +56,20 @@ static struct { struct probe_point probes[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; -} session; +} params; /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { - struct probe_point *pp = &session.probes[session.nr_probe]; + struct probe_point *pp = ¶ms.probes[params.nr_probe]; - pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); - if (++session.nr_probe == MAX_PROBES) + pr_debug("probe-definition(%d): %s\n", params.nr_probe, str); + if (++params.nr_probe == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); /* Parse perf-probe event into probe_point */ - parse_perf_probe_event(str, pp, &session.need_dwarf); + parse_perf_probe_event(str, pp, ¶ms.need_dwarf); pr_debug("%d arguments\n", pp->nr_args); } @@ -103,9 +103,9 @@ 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); + if (!params.dellist) + params.dellist = strlist__new(true, NULL); + strlist__add(params.dellist, str); } return 0; } @@ -115,9 +115,9 @@ static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { if (str) - parse_line_range_desc(str, &session.line_range); - INIT_LIST_HEAD(&session.line_range.line_list); - session.show_lines = true; + parse_line_range_desc(str, ¶ms.line_range); + INIT_LIST_HEAD(¶ms.line_range.line_list); + params.show_lines = true; return 0; } #endif @@ -140,7 +140,7 @@ static const struct option options[] = { OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), #endif - OPT_BOOLEAN('l', "list", &session.list_events, + OPT_BOOLEAN('l', "list", ¶ms.list_events, "list up current probe events"), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), @@ -168,7 +168,7 @@ static const struct option options[] = { #endif "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), - OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" + OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" " with existing name"), #ifndef NO_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, @@ -190,20 +190,20 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) parse_probe_event_argv(argc, argv); } - if ((!session.nr_probe && !session.dellist && !session.list_events && - !session.show_lines)) + if ((!params.nr_probe && !params.dellist && !params.list_events && + !params.show_lines)) usage_with_options(probe_usage, options); if (debugfs_valid_mountpoint(debugfs_path) < 0) die("Failed to find debugfs path."); - if (session.list_events) { - if (session.nr_probe != 0 || session.dellist) { + if (params.list_events) { + if (params.nr_probe != 0 || params.dellist) { pr_warning(" Error: Don't use --list with" " --add/--del.\n"); usage_with_options(probe_usage, options); } - if (session.show_lines) { + if (params.show_lines) { pr_warning(" Error: Don't use --list with --line.\n"); usage_with_options(probe_usage, options); } @@ -212,27 +212,27 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } #ifndef NO_DWARF_SUPPORT - if (session.show_lines) { - if (session.nr_probe != 0 || session.dellist) { + if (params.show_lines) { + if (params.nr_probe != 0 || params.dellist) { pr_warning(" Error: Don't use --line with" " --add/--del.\n"); usage_with_options(probe_usage, options); } - show_line_range(&session.line_range); + show_line_range(¶ms.line_range); return 0; } #endif - if (session.dellist) { - del_trace_kprobe_events(session.dellist); - strlist__delete(session.dellist); - if (session.nr_probe == 0) + if (params.dellist) { + del_trace_kprobe_events(params.dellist); + strlist__delete(params.dellist); + if (params.nr_probe == 0) return 0; } - add_trace_kprobe_events(session.probes, session.nr_probe, - session.force_add, session.need_dwarf); + add_trace_kprobe_events(params.probes, params.nr_probe, + params.force_add, params.need_dwarf); return 0; } -- cgit v0.10.2 From 95a3e4c4e21de1920a2ddb54bfc57c0af7e2561e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:51 -0400 Subject: perf probe: Rename some die_get_* functions Rename die_get_real_subprogram and die_get_inlinefunc to die_find_real_subprogram and die_find_inlinefunc respectively, because these functions search its children. After that, 'die_get_' means getting a property of that die, and 'die_find_' means searching DIE-tree to get an appropriate child die. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220551.32050.36181.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e887bb6..c91a960 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -204,8 +204,8 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) } /* Search a real subprogram including this line, */ -static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { struct __addr_die_search_param ad; ad.addr = addr; @@ -218,8 +218,8 @@ static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, } /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ -static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { Dwarf_Die child_die; int ret; @@ -233,7 +233,7 @@ static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, dwarf_haspc(die_mem, addr)) return die_mem; - if (die_get_inlinefunc(die_mem, addr, &child_die)) { + if (die_find_inlinefunc(die_mem, addr, &child_die)) { memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); return die_mem; } @@ -401,7 +401,7 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { - sp_die = die_get_real_subprogram(&pf->cu_die, + sp_die = die_find_real_subprogram(&pf->cu_die, pf->addr, &die_mem); if (!sp_die) die("Probe point is not found in subprograms."); @@ -564,7 +564,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) if (!dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ - if (die_get_inlinefunc(sp_die, addr, &die_mem)) + if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } @@ -714,7 +714,7 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) continue; /* Address filtering 2: No child include addr? */ - if (die_get_inlinefunc(sp_die, addr, &die_mem)) + if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } -- cgit v0.10.2 From 016f262e4fb10c6ecff709317098912f94a21efa Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:58 -0400 Subject: perf probe: Introduce die_find_child() function Introduce die_find_child() function to integrate DIE-tree searching functions. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220558.32050.7905.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c91a960..3942e14 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -186,6 +186,62 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) return src; } +/* Compare diename and tname */ +static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) +{ + const char *name; + name = dwarf_diename(dw_die); + DIE_IF(name == NULL); + return strcmp(tname, name); +} + +/* Get entry pc(or low pc, 1st entry of ranges) of the die */ +static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) +{ + Dwarf_Addr epc; + int ret; + + ret = dwarf_entrypc(dw_die, &epc); + DIE_IF(ret == -1); + return epc; +} + +/* Return values for die_find callbacks */ +enum { + DIE_FIND_CB_FOUND = 0, /* End of Search */ + DIE_FIND_CB_CHILD = 1, /* Search only children */ + DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ + DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ +}; + +/* Search a child die */ +static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, + int (*callback)(Dwarf_Die *, void *), + void *data, Dwarf_Die *die_mem) +{ + Dwarf_Die child_die; + int ret; + + ret = dwarf_child(rt_die, die_mem); + if (ret != 0) + return NULL; + + do { + ret = callback(die_mem, data); + if (ret == DIE_FIND_CB_FOUND) + return die_mem; + + if ((ret & DIE_FIND_CB_CHILD) && + die_find_child(die_mem, callback, data, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while ((ret & DIE_FIND_CB_SIBLING) && + dwarf_siblingof(die_mem, die_mem) == 0); + + return NULL; +} + struct __addr_die_search_param { Dwarf_Addr addr; Dwarf_Die *die_mem; @@ -217,77 +273,45 @@ static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, return die_mem; } -/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ -static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +/* die_find callback for inline function search */ +static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) { - Dwarf_Die child_die; - int ret; + Dwarf_Addr *addr = data; - ret = dwarf_child(sp_die, die_mem); - if (ret != 0) - return NULL; - - do { - if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && - dwarf_haspc(die_mem, addr)) - return die_mem; + if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && + dwarf_haspc(die_mem, *addr)) + return DIE_FIND_CB_FOUND; - if (die_find_inlinefunc(die_mem, addr, &child_die)) { - memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); - return die_mem; - } - } while (dwarf_siblingof(die_mem, die_mem) == 0); - - return NULL; + return DIE_FIND_CB_CONTINUE; } -/* Compare diename and tname */ -static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) +/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ +static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { - const char *name; - name = dwarf_diename(dw_die); - DIE_IF(name == NULL); - return strcmp(tname, name); + return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); } -/* Get entry pc(or low pc, 1st entry of ranges) of the die */ -static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) +static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) { - Dwarf_Addr epc; - int ret; + const char *name = data; + int tag; - ret = dwarf_entrypc(dw_die, &epc); - DIE_IF(ret == -1); - return epc; + tag = dwarf_tag(die_mem); + if ((tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) && + (die_compare_name(die_mem, name) == 0)) + return DIE_FIND_CB_FOUND; + + return DIE_FIND_CB_CONTINUE; } -/* Get a variable die */ +/* Find a variable called 'name' */ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, Dwarf_Die *die_mem) { - Dwarf_Die child_die; - int tag; - int ret; - - ret = dwarf_child(sp_die, die_mem); - if (ret != 0) - return NULL; - - do { - tag = dwarf_tag(die_mem); - if ((tag == DW_TAG_formal_parameter || - tag == DW_TAG_variable) && - (die_compare_name(die_mem, name) == 0)) - return die_mem; - - if (die_find_variable(die_mem, name, &child_die)) { - memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); - return die_mem; - } - } while (dwarf_siblingof(die_mem, die_mem) == 0); - - return NULL; + return die_find_child(sp_die, __die_find_variable_cb, (void *)name, + die_mem); } /* -- cgit v0.10.2 From f4d7da499e4fc1fdff8f26fdeb1a058d475a7a6c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:05 -0400 Subject: perf probe: Add --dry-run option Add --dry-run option for debugging and testing. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220605.32050.6571.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 34202b1..0f944b3 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -57,6 +57,11 @@ OPTIONS --force:: Forcibly add events with existing name. +-n:: +--dry-run:: + Dry run. With this option, --add and --del doesn't execute actual + adding and removal operations. + PROBE SYNTAX ------------ Probe points are defined by following syntax. diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index f577e14..a1a2891 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -175,6 +175,7 @@ static const struct option options[] = { "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", "Show source code lines.", opt_show_lines), #endif + OPT__DRY_RUN(&probe_event_dry_run), OPT_END() }; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1e60a65..ac41578 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -49,6 +49,8 @@ #define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" +bool probe_event_dry_run; /* Dry run flag */ + #define semantic_error(msg ...) die("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ @@ -430,7 +432,7 @@ error: return ret; } -static int open_kprobe_events(int flags, int mode) +static int open_kprobe_events(bool readwrite) { char buf[PATH_MAX]; int ret; @@ -439,7 +441,11 @@ static int open_kprobe_events(int flags, int mode) if (ret < 0) die("Failed to make kprobe_events path."); - ret = open(buf, flags, mode); + if (readwrite && !probe_event_dry_run) + ret = open(buf, O_RDWR, O_APPEND); + else + ret = open(buf, O_RDONLY, 0); + if (ret < 0) { if (errno == ENOENT) die("kprobe_events file does not exist -" @@ -535,7 +541,7 @@ void show_perf_probe_events(void) setup_pager(); memset(&pp, 0, sizeof(pp)); - fd = open_kprobe_events(O_RDONLY, 0); + fd = open_kprobe_events(false); rawlist = get_trace_kprobe_event_rawlist(fd); close(fd); @@ -585,9 +591,11 @@ static void write_trace_kprobe_event(int fd, const char *buf) int ret; pr_debug("Writing event: %s\n", buf); - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + if (!probe_event_dry_run) { + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); + } } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -630,7 +638,7 @@ static void __add_trace_kprobe_events(struct probe_point *probes, struct strlist *namelist; bool allow_suffix; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, false); @@ -814,7 +822,7 @@ void del_trace_kprobe_events(struct strlist *dellist) struct str_node *ent; struct strlist *namelist; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, true); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 3865e16..703b887 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -5,6 +5,8 @@ #include "probe-finder.h" #include "strlist.h" +extern bool probe_event_dry_run; + extern void parse_line_range_desc(const char *arg, struct line_range *lr); extern void parse_perf_probe_event(const char *str, struct probe_point *pp, bool *need_dwarf); -- cgit v0.10.2 From 4235b0454ebeefc2295ad8417e18a8761425b19e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:12 -0400 Subject: perf probe: Introduce kprobe_trace_event and perf_probe_event Introduce kprobe_trace_event and perf_probe_event and replace old probe_point structure with it. probe_point structure is not enough flexible nor extensible. New data structures will help implementing further features. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index a1a2891..e0dafd9 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -48,12 +48,11 @@ /* Session management structure */ static struct { - bool need_dwarf; bool list_events; bool force_add; bool show_lines; - int nr_probe; - struct probe_point probes[MAX_PROBES]; + int nevents; + struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; } params; @@ -62,16 +61,16 @@ static struct { /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { - struct probe_point *pp = ¶ms.probes[params.nr_probe]; + struct perf_probe_event *pev = ¶ms.events[params.nevents]; - pr_debug("probe-definition(%d): %s\n", params.nr_probe, str); - if (++params.nr_probe == MAX_PROBES) + pr_debug("probe-definition(%d): %s\n", params.nevents, str); + if (++params.nevents == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); - /* Parse perf-probe event into probe_point */ - parse_perf_probe_event(str, pp, ¶ms.need_dwarf); + /* Parse a perf-probe command into event */ + parse_perf_probe_command(str, pev); - pr_debug("%d arguments\n", pp->nr_args); + pr_debug("%d arguments\n", pev->nargs); } static void parse_probe_event_argv(int argc, const char **argv) @@ -191,7 +190,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) parse_probe_event_argv(argc, argv); } - if ((!params.nr_probe && !params.dellist && !params.list_events && + if ((!params.nevents && !params.dellist && !params.list_events && !params.show_lines)) usage_with_options(probe_usage, options); @@ -199,7 +198,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) die("Failed to find debugfs path."); if (params.list_events) { - if (params.nr_probe != 0 || params.dellist) { + if (params.nevents != 0 || params.dellist) { pr_warning(" Error: Don't use --list with" " --add/--del.\n"); usage_with_options(probe_usage, options); @@ -214,7 +213,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) #ifndef NO_DWARF_SUPPORT if (params.show_lines) { - if (params.nr_probe != 0 || params.dellist) { + if (params.nevents != 0 || params.dellist) { pr_warning(" Error: Don't use --line with" " --add/--del.\n"); usage_with_options(probe_usage, options); @@ -226,14 +225,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) #endif if (params.dellist) { - del_trace_kprobe_events(params.dellist); + del_perf_probe_events(params.dellist); strlist__delete(params.dellist); - if (params.nr_probe == 0) + if (params.nevents == 0) return 0; } - add_trace_kprobe_events(params.probes, params.nr_probe, - params.force_add, params.need_dwarf); + add_perf_probe_events(params.events, params.nevents, params.force_add); return 0; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ac41578..b44ddfb 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -44,6 +44,7 @@ #include "thread.h" #include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" +#include "probe-finder.h" #define MAX_CMDLEN 256 #define MAX_PROBE_ARGS 128 @@ -150,8 +151,9 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) +static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { + struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; char c, nc = 0; /* @@ -172,7 +174,8 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.", arg); - pp->event = xstrdup(arg); + pev->event = xstrdup(arg); + pev->group = NULL; arg = tmp; } @@ -255,57 +258,65 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); - pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", + pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); } -/* Parse perf-probe event definition */ -void parse_perf_probe_event(const char *str, struct probe_point *pp, - bool *need_dwarf) +/* Parse perf-probe event command */ +void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { char **argv; int argc, i; - *need_dwarf = false; - - argv = argv_split(str, &argc); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc > MAX_PROBE_ARGS + 1) semantic_error("Too many arguments"); /* Parse probe point */ - parse_perf_probe_probepoint(argv[0], pp); - if (pp->file || pp->line || pp->lazy_line) - *need_dwarf = true; + parse_perf_probe_point(argv[0], pev); /* Copy arguments and ensure return probe has no C argument */ - pp->nr_args = argc - 1; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = xstrdup(argv[i + 1]); - if (is_c_varname(pp->args[i])) { - if (pp->retprobe) - semantic_error("You can't specify local" - " variable for kretprobe"); - *need_dwarf = true; - } + pev->nargs = argc - 1; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < pev->nargs; i++) { + pev->args[i].name = xstrdup(argv[i + 1]); + if (is_c_varname(pev->args[i].name) && pev->point.retprobe) + semantic_error("You can't specify local variable for" + " kretprobe"); } argv_free(argv); } +/* Return true if this perf_probe_event requires debuginfo */ +bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) +{ + int i; + + if (pev->point.file || pev->point.line || pev->point.lazy_line) + return true; + + for (i = 0; i < pev->nargs; i++) + if (is_c_varname(pev->args[i].name)) + return true; + + return false; +} + /* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, struct probe_point *pp) +void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) { + struct kprobe_trace_point *tp = &tev->point; char pr; char *p; int ret, i, argc; char **argv; - pr_debug("Parsing kprobe_events: %s\n", str); - argv = argv_split(str, &argc); + pr_debug("Parsing kprobe_events: %s\n", cmd); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc < 2) @@ -313,47 +324,46 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)&pp->group, - (float *)(void *)&pp->event); + &pr, (float *)(void *)&tev->group, + (float *)(void *)&tev->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); + pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); - pp->retprobe = (pr == 'r'); + tp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, - &pp->offset); + ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, + &tp->offset); if (ret == 1) - pp->offset = 0; - - /* kprobe_events doesn't have this information */ - pp->line = 0; - pp->file = NULL; + tp->offset = 0; - pp->nr_args = argc - 2; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { + tev->nargs = argc - 2; + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + for (i = 0; i < tev->nargs; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ - *p = '\0'; - pp->args[i] = xstrdup(argv[i + 2]); + *p++ = '\0'; + else + p = argv[i + 2]; + tev->args[i].name = xstrdup(argv[i + 2]); + /* TODO: parse regs and offset */ + tev->args[i].value = xstrdup(p); } argv_free(argv); } -/* Synthesize only probe point (not argument) */ -int synthesize_perf_probe_point(struct probe_point *pp) +/* Compose only probe point (not argument) */ +static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { char *buf; char offs[64] = "", line[64] = ""; int ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - pp->found = 1; + buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%d", pp->offset); + ret = e_snprintf(offs, 64, "+%lu", pp->offset); if (ret <= 0) goto error; } @@ -368,68 +378,209 @@ int synthesize_perf_probe_point(struct probe_point *pp) offs, pp->retprobe ? "%return" : "", line); else ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); - if (ret <= 0) { + if (ret <= 0) + goto error; + + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; - pp->found = 0; - } - return ret; + die("Failed to synthesize perf probe point: %s", strerror(-ret)); } -int synthesize_perf_probe_event(struct probe_point *pp) +#if 0 +char *synthesize_perf_probe_command(struct perf_probe_event *pev) { char *buf; int i, len, ret; - len = synthesize_perf_probe_point(pp); - if (len < 0) - return 0; + buf = synthesize_perf_probe_point(&pev->point); + if (!buf) + return NULL; - buf = pp->probes[0]; - for (i = 0; i < pp->nr_args; i++) { + len = strlen(buf); + for (i = 0; i < pev->nargs; i++) { ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); - if (ret <= 0) - goto error; + pev->args[i].name); + if (ret <= 0) { + free(buf); + return NULL; + } len += ret; } - pp->found = 1; - return pp->found; -error: - free(pp->probes[0]); - pp->probes[0] = NULL; + return buf; +} +#endif + +static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, + char **buf, size_t *buflen, + int depth) +{ + int ret; + if (ref->next) { + depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, + buflen, depth + 1); + if (depth < 0) + goto out; + } + + ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); + if (ret < 0) + depth = ret; + else { + *buf += ret; + *buflen -= ret; + } +out: + return depth; - return ret; } -int synthesize_trace_kprobe_event(struct probe_point *pp) +static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, + char *buf, size_t buflen) { + int ret, depth = 0; + char *tmp = buf; + + /* Argument name or separator */ + if (arg->name) + ret = e_snprintf(buf, buflen, " %s=", arg->name); + else + ret = e_snprintf(buf, buflen, " "); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Dereferencing arguments */ + if (arg->ref) { + depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, + &buflen, 1); + if (depth < 0) + return depth; + } + + /* Print argument value */ + ret = e_snprintf(buf, buflen, "%s", arg->value); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Closing */ + while (depth--) { + ret = e_snprintf(buf, buflen, ")"); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + } + + return buf - tmp; +} + +char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_point *tp = &tev->point; char *buf; int i, len, ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); - if (ret <= 0) + buf = xzalloc(MAX_CMDLEN); + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", + tp->retprobe ? 'r' : 'p', + tev->group, tev->event, + tp->symbol, tp->offset); + if (len <= 0) goto error; - len = ret; - for (i = 0; i < pp->nr_args; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); + for (i = 0; i < tev->nargs; i++) { + ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, + MAX_CMDLEN - len); if (ret <= 0) goto error; len += ret; } - pp->found = 1; - return pp->found; + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; + free(buf); + return NULL; +} - return ret; +void convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev) +{ + char buf[64]; + int i; + + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + + /* Convert trace_point to probe_point */ + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + pev->point.retprobe = tev->point.retprobe; + + /* Convert trace_arg to probe_arg */ + pev->nargs = tev->nargs; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < tev->nargs; i++) + if (tev->args[i].name) + pev->args[i].name = xstrdup(tev->args[i].name); + else { + synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); + pev->args[i].name = xstrdup(buf); + } +} + +void clear_perf_probe_event(struct perf_probe_event *pev) +{ + struct perf_probe_point *pp = &pev->point; + int i; + + if (pev->event) + free(pev->event); + if (pev->group) + free(pev->group); + if (pp->file) + free(pp->file); + if (pp->function) + free(pp->function); + if (pp->lazy_line) + free(pp->lazy_line); + for (i = 0; i < pev->nargs; i++) + if (pev->args[i].name) + free(pev->args[i].name); + if (pev->args) + free(pev->args); + memset(pev, 0, sizeof(*pev)); +} + +void clear_kprobe_trace_event(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_arg_ref *ref, *next; + int i; + + if (tev->event) + free(tev->event); + if (tev->group) + free(tev->group); + if (tev->point.symbol) + free(tev->point.symbol); + for (i = 0; i < tev->nargs; i++) { + if (tev->args[i].name) + free(tev->args[i].name); + if (tev->args[i].value) + free(tev->args[i].value); + ref = tev->args[i].ref; + while (ref) { + next = ref->next; + free(ref); + ref = next; + } + } + if (tev->args) + free(tev->args); + memset(tev, 0, sizeof(*tev)); } static int open_kprobe_events(bool readwrite) @@ -458,7 +609,7 @@ static int open_kprobe_events(bool readwrite) } /* Get raw string list of current kprobe_events */ -static struct strlist *get_trace_kprobe_event_rawlist(int fd) +static struct strlist *get_kprobe_trace_command_rawlist(int fd) { int ret, idx; FILE *fp; @@ -486,99 +637,82 @@ static struct strlist *get_trace_kprobe_event_rawlist(int fd) return sl; } -/* Free and zero clear probe_point */ -static void clear_probe_point(struct probe_point *pp) -{ - int i; - - if (pp->event) - free(pp->event); - if (pp->group) - free(pp->group); - if (pp->function) - free(pp->function); - if (pp->file) - free(pp->file); - if (pp->lazy_line) - free(pp->lazy_line); - for (i = 0; i < pp->nr_args; i++) - free(pp->args[i]); - if (pp->args) - free(pp->args); - for (i = 0; i < pp->found; i++) - free(pp->probes[i]); - memset(pp, 0, sizeof(*pp)); -} - /* Show an event */ -static void show_perf_probe_event(const char *event, const char *place, - struct probe_point *pp) +static void show_perf_probe_event(struct perf_probe_event *pev) { int i, ret; char buf[128]; + char *place; - ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); + /* Synthesize only event probe point */ + place = synthesize_perf_probe_point(&pev->point); + + ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); printf(" %-40s (on %s", buf, place); - if (pp->nr_args > 0) { + if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pp->nr_args; i++) - printf(" %s", pp->args[i]); + for (i = 0; i < pev->nargs; i++) + printf(" %s", pev->args[i].name); } printf(")\n"); + free(place); } /* List up current perf-probe events */ void show_perf_probe_events(void) { int fd; - struct probe_point pp; + struct kprobe_trace_event tev; + struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; setup_pager(); - memset(&pp, 0, sizeof(pp)); + + memset(&tev, 0, sizeof(tev)); + memset(&pev, 0, sizeof(pev)); fd = open_kprobe_events(false); - rawlist = get_trace_kprobe_event_rawlist(fd); + rawlist = get_kprobe_trace_command_rawlist(fd); close(fd); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); - /* Synthesize only event probe point */ - synthesize_perf_probe_point(&pp); + parse_kprobe_trace_command(ent->s, &tev); + convert_to_perf_probe_event(&tev, &pev); /* Show an event */ - show_perf_probe_event(pp.event, pp.probes[0], &pp); - clear_probe_point(&pp); + show_perf_probe_event(&pev); + clear_perf_probe_event(&pev); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); } /* Get current perf-probe event names */ -static struct strlist *get_perf_event_names(int fd, bool include_group) +static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) { char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; - struct probe_point pp; + struct kprobe_trace_event tev; - memset(&pp, 0, sizeof(pp)); - rawlist = get_trace_kprobe_event_rawlist(fd); + memset(&tev, 0, sizeof(tev)); + rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); + parse_kprobe_trace_command(ent->s, &tev); if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", pp.group, - pp.event) < 0) + if (e_snprintf(buf, 128, "%s:%s", tev.group, + tev.event) < 0) die("Failed to copy group:event name."); strlist__add(sl, buf); } else - strlist__add(sl, pp.event); - clear_probe_point(&pp); + strlist__add(sl, tev.event); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); @@ -586,9 +720,10 @@ static struct strlist *get_perf_event_names(int fd, bool include_group) return sl; } -static void write_trace_kprobe_event(int fd, const char *buf) +static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { int ret; + char *buf = synthesize_kprobe_trace_command(tev); pr_debug("Writing event: %s\n", buf); if (!probe_event_dry_run) { @@ -596,6 +731,7 @@ static void write_trace_kprobe_event(int fd, const char *buf) if (ret <= 0) die("Failed to write event: %s", strerror(errno)); } + free(buf); } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -628,81 +764,83 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -static void __add_trace_kprobe_events(struct probe_point *probes, - int nr_probes, bool force_add) +static void __add_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event *tevs, + int ntevs, bool allow_suffix) { - int i, j, fd; - struct probe_point *pp; - char buf[MAX_CMDLEN]; - char event[64]; + int i, fd; + struct kprobe_trace_event *tev; + char buf[64]; + const char *event, *group; struct strlist *namelist; - bool allow_suffix; fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, false); - - for (j = 0; j < nr_probes; j++) { - pp = probes + j; - if (!pp->event) - pp->event = xstrdup(pp->function); - if (!pp->group) - pp->group = xstrdup(PERFPROBE_GROUP); - /* If force_add is true, suffix search is allowed */ - allow_suffix = force_add; - for (i = 0; i < pp->found; i++) { - /* Get an unused new event name */ - get_new_event_name(event, 64, pp->event, namelist, - allow_suffix); - snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", - pp->retprobe ? 'r' : 'p', - pp->group, event, - pp->probes[i]); - 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(event, buf, pp); - /* Add added event name to namelist */ - strlist__add(namelist, event); - /* - * Probes after the first probe which comes from same - * user input are always allowed to add suffix, because - * there might be several addresses corresponding to - * one code line. - */ - allow_suffix = true; - } + namelist = get_kprobe_trace_event_names(fd, false); + + printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); + for (i = 0; i < ntevs; i++) { + tev = &tevs[i]; + if (pev->event) + event = pev->event; + else + if (pev->point.function) + event = pev->point.function; + else + event = tev->point.symbol; + if (pev->group) + group = pev->group; + else + group = PERFPROBE_GROUP; + + /* Get an unused new event name */ + get_new_event_name(buf, 64, event, namelist, allow_suffix); + event = buf; + + tev->event = xstrdup(event); + tev->group = xstrdup(group); + write_kprobe_trace_event(fd, tev); + /* Add added event name to namelist */ + strlist__add(namelist, event); + + /* Trick here - save current event/group */ + event = pev->event; + group = pev->group; + pev->event = tev->event; + pev->group = tev->group; + show_perf_probe_event(pev); + /* Trick here - restore current event/group */ + pev->event = (char *)event; + pev->group = (char *)group; + + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; } /* 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); + printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event); strlist__delete(namelist); close(fd); } -/* Currently just checking function name from symbol map */ -static void evaluate_probe_point(struct probe_point *pp) +static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) { struct symbol *sym; - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], - pp->function, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - pp->function); -} - -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add, bool need_dwarf) -{ - int i, ret; - struct probe_point *pp; + bool need_dwarf; #ifndef NO_DWARF_SUPPORT int fd; #endif - /* Add probes */ - init_vmlinux(); + int ntevs = 0, i; + struct kprobe_trace_event *tev; + + need_dwarf = perf_probe_event_need_dwarf(pev); if (need_dwarf) #ifdef NO_DWARF_SUPPORT @@ -721,57 +859,90 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, } /* Searching probe points */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) - continue; - - lseek(fd, SEEK_SET, 0); - ret = find_probe_point(fd, pp); - if (ret > 0) - continue; - if (ret == 0) { /* No error but failed to find probe point. */ - synthesize_perf_probe_point(pp); - die("Probe point '%s' not found. - probe not added.", - pp->probes[0]); - } - /* Error path */ - if (need_dwarf) { - if (ret == -ENOENT) - pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); - } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - break; + ntevs = find_kprobe_trace_events(fd, pev, tevs); + + if (ntevs > 0) /* Found */ + goto found; + + if (ntevs == 0) /* No error but failed to find probe point. */ + die("Probe point '%s' not found. - probe not added.", + synthesize_perf_probe_point(&pev->point)); + + /* Error path */ + if (need_dwarf) { + if (ntevs == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); } - close(fd); + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); end_dwarf: #endif /* !NO_DWARF_SUPPORT */ - /* Synthesize probes without dwarf */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) /* This probe is already found. */ - continue; + /* Allocate trace event buffer */ + ntevs = 1; + tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); + + /* Copy parameters */ + tev->point.symbol = xstrdup(pev->point.function); + tev->point.offset = pev->point.offset; + tev->nargs = pev->nargs; + if (tev->nargs) { + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + for (i = 0; i < tev->nargs; i++) + tev->args[i].value = xstrdup(pev->args[i].name); + } + + /* Currently just checking function name from symbol map */ + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + tev->point.symbol); +found: + close(fd); + return ntevs; +} + +struct __event_package { + struct perf_probe_event *pev; + struct kprobe_trace_event *tevs; + int ntevs; +}; - evaluate_probe_point(pp); - ret = synthesize_trace_kprobe_event(pp); - if (ret == -E2BIG) - die("probe point definition becomes too long."); - else if (ret < 0) - die("Failed to synthesize a probe point."); +void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add) +{ + int i; + struct __event_package *pkgs; + + pkgs = xzalloc(sizeof(struct __event_package) * npevs); + + /* Init vmlinux path */ + init_vmlinux(); + + /* Loop 1: convert all events */ + for (i = 0; i < npevs; i++) { + pkgs[i].pev = &pevs[i]; + /* Convert with or without debuginfo */ + pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, + &pkgs[i].tevs); } - /* Settng up probe points */ - __add_trace_kprobe_events(probes, nr_probes, force_add); + /* Loop 2: add all events */ + for (i = 0; i < npevs; i++) + __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + pkgs[i].ntevs, force_add); + /* TODO: cleanup all trace events? */ } static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; + int ret; /* Convert from perf-probe event to trace-kprobe event */ if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) @@ -781,7 +952,10 @@ static void __del_trace_kprobe_event(int fd, struct str_node *ent) die("Internal error: %s should have ':' but not.", ent->s); *p = '/'; - write_trace_kprobe_event(fd, buf); + pr_debug("Writing event: %s\n", buf); + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); printf("Remove event: %s\n", ent->s); } @@ -814,7 +988,7 @@ static void del_trace_kprobe_event(int fd, const char *group, pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); } -void del_trace_kprobe_events(struct strlist *dellist) +void del_perf_probe_events(struct strlist *dellist) { int fd; const char *group, *event; @@ -824,7 +998,7 @@ void del_trace_kprobe_events(struct strlist *dellist) fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, true); + namelist = get_kprobe_trace_event_names(fd, true); strlist__for_each(ent, dellist) { str = xstrdup(ent->s); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 703b887..2a2f0a2 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -2,24 +2,113 @@ #define _PROBE_EVENT_H #include -#include "probe-finder.h" #include "strlist.h" extern bool probe_event_dry_run; -extern void parse_line_range_desc(const char *arg, struct line_range *lr); -extern void parse_perf_probe_event(const char *str, struct probe_point *pp, - bool *need_dwarf); -extern int synthesize_perf_probe_point(struct probe_point *pp); -extern int synthesize_perf_probe_event(struct probe_point *pp); -extern void parse_trace_kprobe_event(const char *str, 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, - bool force_add, bool need_dwarf); -extern void del_trace_kprobe_events(struct strlist *dellist); +/* kprobe-tracer tracing point */ +struct kprobe_trace_point { + char *symbol; /* Base symbol */ + unsigned long offset; /* Offset from symbol */ + bool retprobe; /* Return probe flag */ +}; + +/* kprobe-tracer tracing argument referencing offset */ +struct kprobe_trace_arg_ref { + struct kprobe_trace_arg_ref *next; /* Next reference */ + long offset; /* Offset value */ +}; + +/* kprobe-tracer tracing argument */ +struct kprobe_trace_arg { + char *name; /* Argument name */ + char *value; /* Base value */ + struct kprobe_trace_arg_ref *ref; /* Referencing offset */ +}; + +/* kprobe-tracer tracing event (point + arg) */ +struct kprobe_trace_event { + char *event; /* Event name */ + char *group; /* Group name */ + struct kprobe_trace_point point; /* Trace point */ + int nargs; /* Number of args */ + struct kprobe_trace_arg *args; /* Arguments */ +}; + +/* Perf probe probing point */ +struct perf_probe_point { + char *file; /* File path */ + char *function; /* Function name */ + int line; /* Line number */ + char *lazy_line; /* Lazy matching pattern */ + unsigned long offset; /* Offset from function entry */ + bool retprobe; /* Return probe flag */ +}; + +/* Perf probe probing argument */ +struct perf_probe_arg { + char *name; /* Argument name */ +}; + +/* Perf probe probing event (point + arg) */ +struct perf_probe_event { + char *event; /* Event name */ + char *group; /* Group name */ + struct perf_probe_point point; /* Probe point */ + int nargs; /* Number of arguments */ + struct perf_probe_arg *args; /* Arguments */ +}; + + +/* Line number container */ +struct line_node { + struct list_head list; + unsigned int line; +}; + +/* Line range */ +struct line_range { + char *file; /* File name */ + char *function; /* Function name */ + unsigned int start; /* Start line number */ + unsigned int end; /* End line number */ + int offset; /* Start line offset */ + char *path; /* Real path name */ + struct list_head line_list; /* Visible lines */ +}; + +/* Command string to events */ +extern void parse_perf_probe_command(const char *cmd, + struct perf_probe_event *pev); +extern void parse_kprobe_trace_command(const char *cmd, + struct kprobe_trace_event *tev); + +/* Events to command string */ +extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); +extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); + +/* Check the perf_probe_event needs debuginfo */ +extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); + +/* Convert from kprobe_trace_event to perf_probe_event */ +extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev); + +/* Release event contents */ +extern void clear_perf_probe_event(struct perf_probe_event *pev); +extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); + +/* Command string to line-range */ +extern void parse_line_range_desc(const char *cmd, struct line_range *lr); + + +extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, + bool force_add); +extern void del_perf_probe_events(struct strlist *dellist); extern void show_perf_probe_events(void); extern void show_line_range(struct line_range *lr); + /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3942e14..251b4c4 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -319,19 +319,20 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, */ /* Show a location */ -static void show_location(Dwarf_Op *op, struct probe_finder *pf) +static void convert_location(Dwarf_Op *op, struct probe_finder *pf) { unsigned int regn; Dwarf_Word offs = 0; - int deref = 0, ret; + bool ref = false; const char *regs; + struct kprobe_trace_arg *tvar = pf->tvar; /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { if (pf->fb_ops == NULL) die("The attribute of frame base is not supported.\n"); - deref = 1; + ref = true; offs = op->number; op = &pf->fb_ops[0]; } @@ -339,13 +340,13 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { regn = op->atom - DW_OP_breg0; offs += op->number; - deref = 1; + ref = true; } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { regn = op->atom - DW_OP_reg0; } else if (op->atom == DW_OP_bregx) { regn = op->number; offs += op->number2; - deref = 1; + ref = true; } else if (op->atom == DW_OP_regx) { regn = op->number; } else @@ -355,17 +356,15 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) if (!regs) die("%u exceeds max register number.", regn); - if (deref) - ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", - pf->var, (intmax_t)offs, regs); - else - ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); - DIE_IF(ret < 0); - DIE_IF(ret >= pf->len); + tvar->value = xstrdup(regs); + if (ref) { + tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + tvar->ref->offset = (long)offs; + } } /* Show a variables in kprobe event format */ -static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) +static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; Dwarf_Op *expr; @@ -379,50 +378,51 @@ static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) if (ret <= 0 || nexpr == 0) goto error; - show_location(expr, pf); + convert_location(expr, pf); /* *expr will be cached in libdw. Don't free it. */ return ; error: /* TODO: Support const_value */ die("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.", pf->var); + " Perhaps, it has been optimized out.", pf->pvar->name); } /* Find a variable in a subprogram die */ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { - int ret; Dwarf_Die vr_die; /* TODO: Support struct members and arrays */ - if (!is_c_varname(pf->var)) { - /* Output raw parameters */ - ret = snprintf(pf->buf, pf->len, " %s", pf->var); - DIE_IF(ret < 0); - DIE_IF(ret >= pf->len); - return ; + if (!is_c_varname(pf->pvar->name)) { + /* Copy raw parameters */ + pf->tvar->value = xstrdup(pf->pvar->name); + } else { + pf->tvar->name = xstrdup(pf->pvar->name); + pr_debug("Searching '%s' variable in context.\n", + pf->pvar->name); + /* Search child die for local variables and parameters. */ + if (!die_find_variable(sp_die, pf->pvar->name, &vr_die)) + die("Failed to find '%s' in this function.", + pf->pvar->name); + convert_variable(&vr_die, pf); } - - pr_debug("Searching '%s' variable in context.\n", pf->var); - /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->var, &vr_die)) - die("Failed to find '%s' in this function.", pf->var); - - show_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { - struct probe_point *pp = pf->pp; + struct kprobe_trace_event *tev; Dwarf_Addr eaddr; Dwarf_Die die_mem; const char *name; - char tmp[MAX_PROBE_BUFFER]; - int ret, i, len; + int ret, i; Dwarf_Attribute fb_attr; size_t nops; + if (pf->ntevs == MAX_PROBES) + die("Too many( > %d) probe point found.\n", MAX_PROBES); + tev = &pf->tevs[pf->ntevs++]; + /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { sp_die = die_find_real_subprogram(&pf->cu_die, @@ -431,31 +431,18 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) die("Probe point is not found in subprograms."); } - /* Output name of probe point */ + /* Copy the name of probe point */ name = dwarf_diename(sp_die); if (name) { dwarf_entrypc(sp_die, &eaddr); - ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, - (unsigned long)(pf->addr - eaddr)); - /* Copy the function name if possible */ - if (!pp->function) { - pp->function = xstrdup(name); - pp->offset = (size_t)(pf->addr - eaddr); - } - } else { + tev->point.symbol = xstrdup(name); + tev->point.offset = (unsigned long)(pf->addr - eaddr); + } else /* This function has no name. */ - ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", - (uintmax_t)pf->addr); - if (!pp->function) { - /* TODO: Use _stext */ - pp->function = xstrdup(""); - pp->offset = (size_t)pf->addr; - } - } - DIE_IF(ret < 0); - DIE_IF(ret >= MAX_PROBE_BUFFER); - len = ret; - pr_debug("Probe point found: %s\n", tmp); + tev->point.offset = (unsigned long)pf->addr; + + pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, + tev->point.offset); /* Get the frame base attribute/ops */ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); @@ -465,22 +452,16 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Find each argument */ /* TODO: use dwarf_cfi_addrframe */ - for (i = 0; i < pp->nr_args; i++) { - pf->var = pp->args[i]; - pf->buf = &tmp[len]; - pf->len = MAX_PROBE_BUFFER - len; + tev->nargs = pf->pev->nargs; + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + for (i = 0; i < pf->pev->nargs; i++) { + pf->pvar = &pf->pev->args[i]; + pf->tvar = &tev->args[i]; find_variable(sp_die, pf); - len += strlen(pf->buf); } /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; - - if (pp->found == MAX_PROBES) - die("Too many( > %d) probe point found.\n", MAX_PROBES); - - pp->probes[pp->found] = xstrdup(tmp); - pp->found++; } /* Find probe point from its line number */ @@ -512,7 +493,7 @@ static void find_probe_point_by_line(struct probe_finder *pf) (int)i, lineno, (uintmax_t)addr); pf->addr = addr; - show_probe_point(NULL, pf); + convert_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } } @@ -563,7 +544,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, - pf->pp->lazy_line); + pf->pev->point.lazy_line); if (ret <= 0) die("No matched lines found in %s.", pf->fname); } @@ -596,7 +577,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - show_probe_point(sp_die, pf); + convert_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ @@ -605,7 +586,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { struct probe_finder *pf = (struct probe_finder *)data; - struct probe_point *pp = pf->pp; + struct perf_probe_point *pp = &pf->pev->point; if (pp->lazy_line) find_probe_point_lazy(in_die, pf); @@ -616,7 +597,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - show_probe_point(in_die, pf); + convert_probe_point(in_die, pf); } return DWARF_CB_OK; @@ -626,7 +607,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct probe_finder *pf = (struct probe_finder *)data; - struct probe_point *pp = pf->pp; + struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || @@ -646,7 +627,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) pf->addr = die_get_entrypc(sp_die); pf->addr += pp->offset; /* TODO: Check the address in this function */ - show_probe_point(sp_die, pf); + convert_probe_point(sp_die, pf); } } else /* Inlined function: search instances */ @@ -660,20 +641,25 @@ static void find_probe_point_by_func(struct probe_finder *pf) dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); } -/* Find a probe point */ -int find_probe_point(int fd, struct probe_point *pp) +/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ +int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) { - struct probe_finder pf = {.pp = pp}; + struct probe_finder pf = {.pev = pev}; + struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; + pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + *tevs = pf.tevs; + pf.ntevs = 0; + dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) return -ENOENT; - pp->found = 0; off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ @@ -704,7 +690,7 @@ int find_probe_point(int fd, struct probe_point *pp) line_list__free(&pf.lcache); dwarf_end(dbg); - return pp->found; + return pf.ntevs; } /* Find line range from its line number */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 21f7354..4949526 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -3,6 +3,7 @@ #include #include "util.h" +#include "probe-event.h" #define MAX_PATH_LEN 256 #define MAX_PROBE_BUFFER 1024 @@ -14,67 +15,32 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -struct probe_point { - char *event; /* Event name */ - char *group; /* Event group */ - - /* Inputs */ - char *file; /* File name */ - int line; /* Line number */ - char *lazy_line; /* Lazy line pattern */ - - char *function; /* Function name */ - int offset; /* Offset bytes */ - - int nr_args; /* Number of arguments */ - char **args; /* Arguments */ - - int retprobe; /* Return probe */ - - /* Output */ - int found; /* Number of found probe points */ - char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ -}; - -/* Line number container */ -struct line_node { - struct list_head list; - unsigned int line; -}; - -/* Line range */ -struct line_range { - char *file; /* File name */ - char *function; /* Function name */ - unsigned int start; /* Start line number */ - unsigned int end; /* End line number */ - int offset; /* Start line offset */ - char *path; /* Real path name */ - struct list_head line_list; /* Visible lines */ -}; - #ifndef NO_DWARF_SUPPORT -extern int find_probe_point(int fd, struct probe_point *pp); +/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ +extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, + struct kprobe_trace_event **tevs); + extern int find_line_range(int fd, struct line_range *lr); #include #include struct probe_finder { - struct probe_point *pp; /* Target probe point */ + struct perf_probe_event *pev; /* Target probe event */ + int ntevs; /* number of trace events */ + struct kprobe_trace_event *tevs; /* Result trace events */ /* For function searching */ Dwarf_Addr addr; /* Address */ - const char *fname; /* File name */ + const char *fname; /* Real file name */ int lno; /* Line number */ Dwarf_Die cu_die; /* Current CU */ + struct list_head lcache; /* Line cache for lazy match */ /* For variable searching */ Dwarf_Op *fb_ops; /* Frame base attribute */ - const char *var; /* Current variable name */ - char *buf; /* Current output buffer */ - int len; /* Length of output buffer */ - struct list_head lcache; /* Line cache for lazy match */ + struct perf_probe_arg *pvar; /* Current target variable */ + struct kprobe_trace_arg *tvar; /* Current result variable */ }; struct line_finder { -- cgit v0.10.2 From fb1587d869a399554220e166d4b90b581a8ade01 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:19 -0400 Subject: perf probe: List probes with line number and file name Improve --list to show current exist probes with line number and file name. This enables user easily to check which line is already probed. for example: ./perf probe --list probe:vfs_read (on vfs_read:8@linux-2.6-tip/fs/read_write.c) Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220619.32050.48702.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b44ddfb..4e3c1ae 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -70,7 +70,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } - static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; @@ -357,27 +356,39 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { - char *buf; - char offs[64] = "", line[64] = ""; - int ret; + char *buf, *tmp; + char offs[32] = "", line[32] = "", file[32] = ""; + int ret, len; buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%lu", pp->offset); + ret = e_snprintf(offs, 32, "+%lu", pp->offset); if (ret <= 0) goto error; } if (pp->line) { - ret = e_snprintf(line, 64, ":%d", pp->line); + ret = e_snprintf(line, 32, ":%d", pp->line); + if (ret <= 0) + goto error; + } + if (pp->file) { + len = strlen(pp->file) - 32; + if (len < 0) + len = 0; + tmp = strchr(pp->file + len, '/'); + if (!tmp) + tmp = pp->file + len - 1; + ret = e_snprintf(file, 32, "@%s", tmp + 1); if (ret <= 0) goto error; } if (pp->function) - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, - offs, pp->retprobe ? "%return" : "", line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, + offs, pp->retprobe ? "%return" : "", line, + file); else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); if (ret <= 0) goto error; @@ -511,15 +522,32 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, { char buf[64]; int i; +#ifndef NO_DWARF_SUPPORT + struct symbol *sym; + int fd, ret = 0; - pev->event = xstrdup(tev->event); - pev->group = xstrdup(tev->group); - + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (sym) { + fd = open_vmlinux(); + ret = find_perf_probe_point(fd, sym->start + tev->point.offset, + &pev->point); + close(fd); + } + if (ret <= 0) { + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + } +#else /* Convert trace_point to probe_point */ pev->point.function = xstrdup(tev->point.symbol); pev->point.offset = tev->point.offset; +#endif pev->point.retprobe = tev->point.retprobe; + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -650,7 +678,7 @@ static void show_perf_probe_event(struct perf_probe_event *pev) ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); - printf(" %-40s (on %s", buf, place); + printf(" %-20s (on %s", buf, place); if (pev->nargs > 0) { printf(" with"); @@ -671,6 +699,7 @@ void show_perf_probe_events(void) struct str_node *ent; setup_pager(); + init_vmlinux(); memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 251b4c4..e02b607 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -693,6 +693,76 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, return pf.ntevs; } +/* Reverse search */ +int find_perf_probe_point(int fd, unsigned long addr, + struct perf_probe_point *ppt) +{ + Dwarf_Die cudie, spdie, indie; + Dwarf *dbg; + Dwarf_Line *line; + Dwarf_Addr laddr, eaddr; + const char *tmp; + int lineno, ret = 0; + + dbg = dwarf_begin(fd, DWARF_C_READ); + if (!dbg) + return -ENOENT; + + /* Find cu die */ + if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) + return -EINVAL; + + /* Find a corresponding line */ + line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); + if (line) { + dwarf_lineaddr(line, &laddr); + if ((Dwarf_Addr)addr == laddr) { + dwarf_lineno(line, &lineno); + ppt->line = lineno; + + tmp = dwarf_linesrc(line, NULL, NULL); + DIE_IF(!tmp); + ppt->file = xstrdup(tmp); + ret = 1; + } + } + + /* Find a corresponding function */ + if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { + tmp = dwarf_diename(&spdie); + if (!tmp) + goto end; + + dwarf_entrypc(&spdie, &eaddr); + if (!lineno) { + /* We don't have a line number, let's use offset */ + ppt->function = xstrdup(tmp); + ppt->offset = addr - (unsigned long)eaddr; + ret = 1; + goto end; + } + if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { + /* addr in an inline function */ + tmp = dwarf_diename(&indie); + if (!tmp) + goto end; + dwarf_decl_line(&indie, &lineno); + } else { + if (eaddr == addr) /* No offset: function entry */ + lineno = ppt->line; + else + dwarf_decl_line(&spdie, &lineno); + } + ppt->function = xstrdup(tmp); + ppt->line -= lineno; /* Make a relative line number */ + } + +end: + dwarf_end(dbg); + return ret; +} + + /* Find line range from its line number */ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 4949526..2f2307d 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -20,6 +20,10 @@ static inline int is_c_varname(const char *name) extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, struct kprobe_trace_event **tevs); +/* Find a perf_probe_point from debuginfo */ +extern int find_perf_probe_point(int fd, unsigned long addr, + struct perf_probe_point *ppt); + extern int find_line_range(int fd, struct line_range *lr); #include -- cgit v0.10.2 From 7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:26 -0400 Subject: perf probe: Add data structure member access support Support accessing members in the data structures. With this, perf-probe accepts data-structure members(IOW, it now accepts dot '.' and arrow '->' operators) as probe arguemnts. e.g. ./perf probe --add 'schedule:44 rq->curr' ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' Note that '>' can be interpreted as redirection in command-line. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220626.32050.57552.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4e3c1ae..64dea6c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -262,6 +262,49 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) pp->lazy_line); } +/* Parse perf-probe event argument */ +static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) +{ + const char *tmp; + struct perf_probe_arg_field **fieldp; + + pr_debug("parsing arg: %s into ", str); + + tmp = strpbrk(str, "-."); + if (!is_c_varname(str) || !tmp) { + /* A variable, register, symbol or special value */ + arg->name = xstrdup(str); + pr_debug("%s\n", arg->name); + return; + } + + /* Structure fields */ + arg->name = xstrndup(str, tmp - str); + pr_debug("%s, ", arg->name); + fieldp = &arg->field; + + do { + *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); + if (*tmp == '.') { + str = tmp + 1; + (*fieldp)->ref = false; + } else if (tmp[1] == '>') { + str = tmp + 2; + (*fieldp)->ref = true; + } else + semantic_error("Argument parse error: %s", str); + + tmp = strpbrk(str, "-."); + if (tmp) { + (*fieldp)->name = xstrndup(str, tmp - str); + pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); + fieldp = &(*fieldp)->next; + } + } while (tmp); + (*fieldp)->name = xstrdup(str); + pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); +} + /* Parse perf-probe event command */ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { @@ -281,7 +324,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) pev->nargs = argc - 1; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); for (i = 0; i < pev->nargs; i++) { - pev->args[i].name = xstrdup(argv[i + 1]); + parse_perf_probe_arg(argv[i + 1], &pev->args[i]); if (is_c_varname(pev->args[i].name) && pev->point.retprobe) semantic_error("You can't specify local variable for" " kretprobe"); @@ -353,6 +396,33 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) argv_free(argv); } +/* Compose only probe arg */ +int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) +{ + struct perf_probe_arg_field *field = pa->field; + int ret; + char *tmp = buf; + + ret = e_snprintf(tmp, len, "%s", pa->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + + while (field) { + ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", + field->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + field = field->next; + } + return tmp - buf; +error: + die("Failed to synthesize perf probe argument: %s", strerror(-ret)); +} + /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { @@ -563,6 +633,7 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, void clear_perf_probe_event(struct perf_probe_event *pev) { struct perf_probe_point *pp = &pev->point; + struct perf_probe_arg_field *field, *next; int i; if (pev->event) @@ -575,9 +646,18 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pp->function); if (pp->lazy_line) free(pp->lazy_line); - for (i = 0; i < pev->nargs; i++) + for (i = 0; i < pev->nargs; i++) { if (pev->args[i].name) free(pev->args[i].name); + field = pev->args[i].field; + while (field) { + next = field->next; + if (field->name) + free(field->name); + free(field); + field = next; + } + } if (pev->args) free(pev->args); memset(pev, 0, sizeof(*pev)); @@ -682,8 +762,10 @@ static void show_perf_probe_event(struct perf_probe_event *pev) if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pev->nargs; i++) - printf(" %s", pev->args[i].name); + for (i = 0; i < pev->nargs; i++) { + synthesize_perf_probe_arg(&pev->args[i], buf, 128); + printf(" %s", buf); + } } printf(")\n"); free(place); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 2a2f0a2..cd308b0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -45,9 +45,17 @@ struct perf_probe_point { bool retprobe; /* Return probe flag */ }; +/* Perf probe probing argument field chain */ +struct perf_probe_arg_field { + struct perf_probe_arg_field *next; /* Next field */ + char *name; /* Name of the field */ + bool ref; /* Referencing flag */ +}; + /* Perf probe probing argument */ struct perf_probe_arg { - char *name; /* Argument name */ + char *name; /* Argument name */ + struct perf_probe_arg_field *field; /* Structure fields */ }; /* Perf probe probing event (point + arg) */ @@ -86,6 +94,8 @@ extern void parse_kprobe_trace_command(const char *cmd, /* Events to command string */ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); +extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, + size_t len); /* Check the perf_probe_event needs debuginfo */ extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e02b607..db52ec2 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -206,6 +206,28 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) return epc; } +/* Get type die, but skip qualifiers and typedef */ +static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +{ + Dwarf_Attribute attr; + int tag; + + do { + if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || + dwarf_formref_die(&attr, die_mem) == NULL) + return NULL; + + tag = dwarf_tag(die_mem); + vr_die = die_mem; + } while (tag == DW_TAG_const_type || + tag == DW_TAG_restrict_type || + tag == DW_TAG_volatile_type || + tag == DW_TAG_shared_type || + tag == DW_TAG_typedef); + + return die_mem; +} + /* Return values for die_find callbacks */ enum { DIE_FIND_CB_FOUND = 0, /* End of Search */ @@ -314,6 +336,25 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, die_mem); } +static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) +{ + const char *name = data; + + if ((dwarf_tag(die_mem) == DW_TAG_member) && + (die_compare_name(die_mem, name) == 0)) + return DIE_FIND_CB_FOUND; + + return DIE_FIND_CB_SIBLING; +} + +/* Find a member called 'name' */ +static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, + Dwarf_Die *die_mem) +{ + return die_find_child(st_die, __die_find_member_cb, (void *)name, + die_mem); +} + /* * Probe finder related functions */ @@ -363,6 +404,62 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) } } +static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, + struct perf_probe_arg_field *field, + struct kprobe_trace_arg_ref **ref_ptr) +{ + struct kprobe_trace_arg_ref *ref = *ref_ptr; + Dwarf_Attribute attr; + Dwarf_Die member; + Dwarf_Die type; + Dwarf_Word offs; + + pr_debug("converting %s in %s\n", field->name, varname); + if (die_get_real_type(vr_die, &type) == NULL) + die("Failed to get a type information of %s.", varname); + + /* Check the pointer and dereference */ + if (dwarf_tag(&type) == DW_TAG_pointer_type) { + if (!field->ref) + die("Semantic error: %s must be referred by '->'", + field->name); + /* Get the type pointed by this pointer */ + if (die_get_real_type(&type, &type) == NULL) + die("Failed to get a type information of %s.", varname); + + ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + if (*ref_ptr) + (*ref_ptr)->next = ref; + else + *ref_ptr = ref; + } else { + if (field->ref) + die("Semantic error: %s must be referred by '.'", + field->name); + if (!ref) + die("Structure on a register is not supported yet."); + } + + /* Verify it is a data structure */ + if (dwarf_tag(&type) != DW_TAG_structure_type) + die("%s is not a data structure.", varname); + + if (die_find_member(&type, field->name, &member) == NULL) + die("%s(tyep:%s) has no member %s.", varname, + dwarf_diename(&type), field->name); + + /* Get the offset of the field */ + if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || + dwarf_formudata(&attr, &offs) != 0) + die("Failed to get the offset of %s.", field->name); + ref->offset += (long)offs; + + /* Converting next field */ + if (field->next) + convert_variable_fields(&member, field->name, field->next, + &ref); +} + /* Show a variables in kprobe event format */ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { @@ -379,6 +476,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) goto error; convert_location(expr, pf); + + if (pf->pvar->field) + convert_variable_fields(vr_die, pf->pvar->name, + pf->pvar->field, &pf->tvar->ref); /* *expr will be cached in libdw. Don't free it. */ return ; error: @@ -391,13 +492,15 @@ error: static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; + char buf[128]; /* TODO: Support struct members and arrays */ if (!is_c_varname(pf->pvar->name)) { /* Copy raw parameters */ pf->tvar->value = xstrdup(pf->pvar->name); } else { - pf->tvar->name = xstrdup(pf->pvar->name); + synthesize_perf_probe_arg(pf->pvar, buf, 128); + pf->tvar->name = xstrdup(buf); pr_debug("Searching '%s' variable in context.\n", pf->pvar->name); /* Search child die for local variables and parameters. */ -- cgit v0.10.2 From 3b0d516463f8deb897a55cb81e9dbbe58a2490ed Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 17 Mar 2010 12:13:28 +0100 Subject: perf probe: Fix !dwarf build Fix the !drawf build. This uses the existing NO_DWARF_SUPPORT mechanism we use for that, but it's really fragile and needs a cleanup. (in a separate patch) 1) Such uses: #ifndef NO_DWARF_SUPPORT are double inverted logic a'la 'not not'. Instead the flag should be called DWARF_SUPPORT. 2) Furthermore, assymetric #ifdef polluted code flow like: if (need_dwarf) #ifdef NO_DWARF_SUPPORT die("Debuginfo-analysis is not supported"); #else /* !NO_DWARF_SUPPORT */ pr_debug("Some probes require debuginfo.\n"); fd = open_vmlinux(); is very fragile and not acceptable. Instead of that helper functions should be created and the dwarf/no-dwarf logic should be separated more cleanly. 3) Local variable #ifdefs like this: #ifndef NO_DWARF_SUPPORT int fd; #endif Are fragile as well and should be eliminated. Helper functions achieve that too. Cc: Masami Hiramatsu Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 64dea6c..f333269 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1012,8 +1012,10 @@ end_dwarf: if (!sym) die("Kernel symbol \'%s\' not found - probe not added.", tev->point.symbol); +#ifndef NO_DWARF_SUPPORT found: close(fd); +#endif return ntevs; } @@ -1172,10 +1174,13 @@ void show_line_range(struct line_range *lr) unsigned int l = 1; struct line_node *ln; FILE *fp; +#ifndef NO_DWARF_SUPPORT int fd, ret; +#endif /* Search a line range */ init_vmlinux(); +#ifndef NO_DWARF_SUPPORT fd = open_vmlinux(); if (fd < 0) die("Could not open debuginfo file."); @@ -1183,6 +1188,7 @@ void show_line_range(struct line_range *lr) if (ret <= 0) die("Source line is not found.\n"); close(fd); +#endif setup_pager(); -- cgit v0.10.2 From b27ea29c6267889be255f2217fa7a6106e6a8b04 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 17 Mar 2010 12:49:10 +0100 Subject: perf/core, x86: Reduce number of CONFIG_X86_LOCAL_APIC macros The function reserve_pmc_hardware() and release_pmc_hardware() were hard to read. This patch improves readability of the code by removing most of the CONFIG_X86_LOCAL_APIC macros. Signed-off-by: Robert Richter Cc: Stephane Eranian Cc: Peter Zijlstra LKML-Reference: <1268826553-19518-2-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 5dacf63..793e63f 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -313,9 +313,10 @@ again: static atomic_t active_events; static DEFINE_MUTEX(pmc_reserve_mutex); +#ifdef CONFIG_X86_LOCAL_APIC + static bool reserve_pmc_hardware(void) { -#ifdef CONFIG_X86_LOCAL_APIC int i; if (nmi_watchdog == NMI_LOCAL_APIC) @@ -330,11 +331,9 @@ static bool reserve_pmc_hardware(void) if (!reserve_evntsel_nmi(x86_pmu.eventsel + i)) goto eventsel_fail; } -#endif return true; -#ifdef CONFIG_X86_LOCAL_APIC eventsel_fail: for (i--; i >= 0; i--) release_evntsel_nmi(x86_pmu.eventsel + i); @@ -349,12 +348,10 @@ perfctr_fail: enable_lapic_nmi_watchdog(); return false; -#endif } static void release_pmc_hardware(void) { -#ifdef CONFIG_X86_LOCAL_APIC int i; for (i = 0; i < x86_pmu.num_events; i++) { @@ -364,9 +361,15 @@ static void release_pmc_hardware(void) if (nmi_watchdog == NMI_LOCAL_APIC) enable_lapic_nmi_watchdog(); -#endif } +#else + +static bool reserve_pmc_hardware(void) { return true; } +static void release_pmc_hardware(void) {} + +#endif + static int reserve_ds_buffers(void); static void release_ds_buffers(void); -- cgit v0.10.2 From 141c4296cb630a7ed4c3730913bc3c0617ef9753 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 17 Mar 2010 12:49:11 +0100 Subject: perf/core: Correct files in MAINTAINERS entry This corrects the file entries for perf_events. The following files are caught now: $ xargs | eval ls $(cat) | sort -u kernel/perf_event*.c include/linux/perf_event.h arch/*/kernel/perf_event*.c arch/*/kernel/*/perf_event*.c arch/*/kernel/*/*/perf_event*.c arch/*/include/asm/perf_event.h arch/*/lib/perf_event*.c arch/*/kernel/perf_callchain.c arch/alpha/include/asm/perf_event.h arch/arm/include/asm/perf_event.h arch/arm/kernel/perf_event.c arch/frv/include/asm/perf_event.h arch/frv/lib/perf_event.c arch/parisc/include/asm/perf_event.h arch/powerpc/include/asm/perf_event.h arch/powerpc/kernel/perf_callchain.c arch/powerpc/kernel/perf_event.c arch/s390/include/asm/perf_event.h arch/sh/include/asm/perf_event.h arch/sh/kernel/cpu/sh4a/perf_event.c arch/sh/kernel/cpu/sh4/perf_event.c arch/sh/kernel/perf_callchain.c arch/sh/kernel/perf_event.c arch/sparc/include/asm/perf_event.h arch/sparc/kernel/perf_event.c arch/x86/include/asm/perf_event.h arch/x86/kernel/cpu/perf_event_amd.c arch/x86/kernel/cpu/perf_event.c arch/x86/kernel/cpu/perf_event_intel.c arch/x86/kernel/cpu/perf_event_p6.c include/linux/perf_event.h kernel/perf_event.c Signed-off-by: Robert Richter Cc: Stephane Eranian Cc: Peter Zijlstra LKML-Reference: <1268826553-19518-3-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/MAINTAINERS b/MAINTAINERS index 14d5c4c..e3aa850 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4295,13 +4295,13 @@ M: Paul Mackerras M: Ingo Molnar M: Arnaldo Carvalho de Melo S: Supported -F: kernel/perf_event.c +F: kernel/perf_event*.c F: include/linux/perf_event.h -F: arch/*/kernel/perf_event.c -F: arch/*/kernel/*/perf_event.c -F: arch/*/kernel/*/*/perf_event.c +F: arch/*/kernel/perf_event*.c +F: arch/*/kernel/*/perf_event*.c +F: arch/*/kernel/*/*/perf_event*.c F: arch/*/include/asm/perf_event.h -F: arch/*/lib/perf_event.c +F: arch/*/lib/perf_event*.c F: arch/*/kernel/perf_callchain.c F: tools/perf/ -- cgit v0.10.2 From 10f1014d86fd4fe5087080d609b51183396c5e4c Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 17 Mar 2010 12:49:12 +0100 Subject: perf/core, x86: Remove cpu_hw_events.interrupts This member in the struct is not used anymore and can be removed. Signed-off-by: Robert Richter Cc: Stephane Eranian Cc: Peter Zijlstra LKML-Reference: <1268826553-19518-4-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 793e63f..104292a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -102,7 +102,6 @@ struct cpu_hw_events { */ struct perf_event *events[X86_PMC_IDX_MAX]; /* in counter order */ unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; - unsigned long interrupts; int enabled; int n_events; -- cgit v0.10.2 From d6dc0b4ead6e8720096ecfa3d9e899b47ddbc8ed Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 17 Mar 2010 12:49:13 +0100 Subject: perf/core, x86: Remove duplicate perf_event_mask variable The same information is stored also in x86_pmu.intel_ctrl. This patch removes perf_event_mask and instead uses x86_pmu.intel_ctrl directly. Signed-off-by: Robert Richter Cc: Stephane Eranian Cc: Peter Zijlstra LKML-Reference: <1268826553-19518-5-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 104292a..c97d5b5 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -75,8 +75,6 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) return len; } -static u64 perf_event_mask __read_mostly; - struct event_constraint { union { unsigned long idxmsk[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; @@ -1406,7 +1404,7 @@ void __init init_hw_perf_events(void) x86_pmu.num_events, X86_PMC_MAX_GENERIC); x86_pmu.num_events = X86_PMC_MAX_GENERIC; } - perf_event_mask = (1 << x86_pmu.num_events) - 1; + x86_pmu.intel_ctrl = (1 << x86_pmu.num_events) - 1; perf_max_events = x86_pmu.num_events; if (x86_pmu.num_events_fixed > X86_PMC_MAX_FIXED) { @@ -1415,9 +1413,8 @@ void __init init_hw_perf_events(void) x86_pmu.num_events_fixed = X86_PMC_MAX_FIXED; } - perf_event_mask |= + x86_pmu.intel_ctrl |= ((1LL << x86_pmu.num_events_fixed)-1) << X86_PMC_IDX_FIXED; - x86_pmu.intel_ctrl = perf_event_mask; perf_events_lapic_init(); register_die_notifier(&perf_event_nmi_notifier); @@ -1442,7 +1439,7 @@ void __init init_hw_perf_events(void) pr_info("... value mask: %016Lx\n", x86_pmu.event_mask); pr_info("... max period: %016Lx\n", x86_pmu.max_period); pr_info("... fixed-purpose events: %d\n", x86_pmu.num_events_fixed); - pr_info("... event mask: %016Lx\n", perf_event_mask); + pr_info("... event mask: %016Lx\n", x86_pmu.intel_ctrl); perf_cpu_notifier(x86_pmu_notifier); } -- cgit v0.10.2 From 6be2850effd6a8bae11d623c8c52e88d2fbc0e96 Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Thu, 18 Mar 2010 11:36:03 -0300 Subject: perf stat: Enable counters when collecting process-wide or system-wide data Command 'perf stat' doesn't enable counters when collecting an existing (by -p) process or system-wide statistics. Fix the issue. Change the condition of fork/exec subcommand. If there is a subcommand parameter, perf always forks/execs it. The usage example is: # perf stat -a sleep 10 So this command could collect statistics for 10 seconds precisely. User still could stop it by CTRL+C. Without the new capability, user could only use CTRL+C to stop it without precise time clock. Another issue is 'perf stat -a' consumes 100% time of a full single logical cpu. It has a bad impact on running workload. Fix it by adding a sleep(1) in the while(!done) loop in function run_perf_stat. Signed-off-by: Zhang Yanmin Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Peter Zijlstra Cc: Sheng Yang Cc: Marcelo Tosatti Cc: Joerg Roedel Cc: Jes Sorensen Cc: Gleb Natapov Cc: Zachary Amsden Cc: LKML-Reference: <1268922965-14774-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 95db31c..5f41244 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -159,8 +159,10 @@ static void create_perf_stat_counter(int counter, int pid) } } else { attr->inherit = inherit; - attr->disabled = 1; - attr->enable_on_exec = 1; + if (target_pid == -1) { + attr->disabled = 1; + attr->enable_on_exec = 1; + } fd[0][counter] = sys_perf_event_open(attr, pid, -1, -1, 0); if (fd[0][counter] < 0 && verbose) @@ -251,9 +253,9 @@ static int run_perf_stat(int argc __used, const char **argv) unsigned long long t0, t1; int status = 0; int counter; - int pid = target_pid; + int pid; int child_ready_pipe[2], go_pipe[2]; - const bool forks = (target_pid == -1 && argc > 0); + const bool forks = (argc > 0); char buf; if (!system_wide) @@ -265,10 +267,10 @@ static int run_perf_stat(int argc __used, const char **argv) } if (forks) { - if ((pid = fork()) < 0) + if ((child_pid = fork()) < 0) perror("failed to fork"); - if (!pid) { + if (!child_pid) { close(child_ready_pipe[0]); close(go_pipe[1]); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); @@ -297,8 +299,6 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } - child_pid = pid; - /* * Wait for the child to be ready to exec. */ @@ -309,6 +309,10 @@ static int run_perf_stat(int argc __used, const char **argv) close(child_ready_pipe[0]); } + if (target_pid == -1) + pid = child_pid; + else + pid = target_pid; for (counter = 0; counter < nr_counters; counter++) create_perf_stat_counter(counter, pid); @@ -321,7 +325,7 @@ static int run_perf_stat(int argc __used, const char **argv) close(go_pipe[1]); wait(&status); } else { - while(!done); + while(!done) sleep(1); } t1 = rdclock(); @@ -459,7 +463,7 @@ static volatile int signr = -1; static void skip_signal(int signo) { - if(target_pid != -1) + if(child_pid == -1) done = 1; signr = signo; -- cgit v0.10.2 From 46be604b5ba738d53e5f5314813a4e7092864baf Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Thu, 18 Mar 2010 11:36:04 -0300 Subject: perf record: Enable counters only when kernel is execing subcommand 'perf record' starts counters before subcommand is execed, so the statistics is not precise because it includes data of some preparation steps. I fix it with the patch. In addition, change the condition to fork/exec subcommand. If there is a subcommand parameter, perf always fork/exec it. The usage example is: # perf record -f -a sleep 10 So this command could collect statistics for 10 seconds precisely. User still could stop it by CTRL+C. Without the new capability, user could only input CTRL+C to stop it without precise time clock. Signed-off-by: Zhang Yanmin Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Peter Zijlstra Cc: Sheng Yang Cc: oerg Roedel Cc: Jes Sorensen Cc: Marcelo Tosatti Cc: Gleb Natapov Cc: Cc: Zachary Amsden LKML-Reference: <1268922965-14774-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 962cdbf..e2b35ad 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -225,7 +225,7 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n return h_attr; } -static void create_counter(int counter, int cpu, pid_t pid, bool forks) +static void create_counter(int counter, int cpu, pid_t pid) { char *filter = filters[counter]; struct perf_event_attr *attr = attrs + counter; @@ -275,10 +275,10 @@ static void create_counter(int counter, int cpu, pid_t pid, bool forks) attr->mmap = track; attr->comm = track; attr->inherit = inherit; - attr->disabled = 1; - - if (forks) + if (target_pid == -1 && !system_wide) { + attr->disabled = 1; attr->enable_on_exec = 1; + } try_again: fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); @@ -380,17 +380,15 @@ try_again: exit(-1); } } - - ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); } -static void open_counters(int cpu, pid_t pid, bool forks) +static void open_counters(int cpu, pid_t pid) { int counter; group_fd = -1; for (counter = 0; counter < nr_counters; counter++) - create_counter(counter, cpu, pid, forks); + create_counter(counter, cpu, pid); nr_cpu++; } @@ -425,7 +423,7 @@ static int __cmd_record(int argc, const char **argv) int err; unsigned long waking = 0; int child_ready_pipe[2], go_pipe[2]; - const bool forks = target_pid == -1 && argc > 0; + const bool forks = argc > 0; char buf; page_size = sysconf(_SC_PAGE_SIZE); @@ -496,13 +494,13 @@ static int __cmd_record(int argc, const char **argv) atexit(atexit_header); if (forks) { - pid = fork(); + child_pid = fork(); if (pid < 0) { perror("failed to fork"); exit(-1); } - if (!pid) { + if (!child_pid) { close(child_ready_pipe[0]); close(go_pipe[1]); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); @@ -531,11 +529,6 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - child_pid = pid; - - if (!system_wide) - target_pid = pid; - close(child_ready_pipe[1]); close(go_pipe[0]); /* @@ -548,13 +541,17 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } + if (forks && target_pid == -1 && !system_wide) + pid = child_pid; + else + pid = target_pid; if ((!system_wide && !inherit) || profile_cpu != -1) { - open_counters(profile_cpu, target_pid, forks); + open_counters(profile_cpu, pid); } else { nr_cpus = read_cpu_map(); for (i = 0; i < nr_cpus; i++) - open_counters(cpumap[i], target_pid, forks); + open_counters(cpumap[i], pid); } if (file_new) { -- cgit v0.10.2 From d6d901c23a9c4c7361aa901b5b2dda69703dd5e0 Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Thu, 18 Mar 2010 11:36:05 -0300 Subject: perf events: Change perf parameter --pid to process-wide collection instead of thread-wide Parameter --pid (or -p) of perf currently means a thread-wide collection. For exmaple, if a process whose id is 8888 has 10 threads, 'perf top -p 8888' just collects the main thread statistics. That's misleading. Users are used to attach a whole process when debugging a process by gdb. To follow normal usage style, the patch change --pid to process-wide collection and add --tid (-t) to mean a thread-wide collection. Usage example is: # perf top -p 8888 # perf record -p 8888 -f sleep 10 # perf stat -p 8888 -f sleep 10 Above commands collect the statistics of all threads of process 8888. Signed-off-by: Zhang Yanmin Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Peter Zijlstra Cc: Sheng Yang Cc: Joerg Roedel Cc: Jes Sorensen Cc: Marcelo Tosatti Cc: Gleb Natapov Cc: zhiteng.huang@intel.com Cc: Zachary Amsden LKML-Reference: <1268922965-14774-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e2b35ad..bb5b23d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -27,7 +27,7 @@ #include #include -static int fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static long default_interval = 0; @@ -43,6 +43,9 @@ static int raw_samples = 0; static int system_wide = 0; static int profile_cpu = -1; static pid_t target_pid = -1; +static pid_t target_tid = -1; +static pid_t *all_tids = NULL; +static int thread_num = 0; static pid_t child_pid = -1; static int inherit = 1; static int force = 0; @@ -60,7 +63,7 @@ static struct timeval this_read; static u64 bytes_written = 0; -static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; +static struct pollfd *event_array; static int nr_poll = 0; static int nr_cpu = 0; @@ -77,7 +80,7 @@ struct mmap_data { unsigned int prev; }; -static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; +static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; static unsigned long mmap_read_head(struct mmap_data *md) { @@ -225,12 +228,13 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n return h_attr; } -static void create_counter(int counter, int cpu, pid_t pid) +static void create_counter(int counter, int cpu) { char *filter = filters[counter]; struct perf_event_attr *attr = attrs + counter; struct perf_header_attr *h_attr; int track = !counter; /* only the first counter needs these */ + int thread_index; int ret; struct { u64 count; @@ -280,115 +284,124 @@ static void create_counter(int counter, int cpu, pid_t pid) attr->enable_on_exec = 1; } + for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: - fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); - - if (fd[nr_cpu][counter] < 0) { - int err = errno; - - if (err == EPERM || err == EACCES) - die("Permission error - are you root?\n" - "\t Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n"); - else if (err == ENODEV && profile_cpu != -1) - die("No such device - did you specify an out-of-range profile CPU?\n"); + fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, + all_tids[thread_index], cpu, group_fd, 0); + + if (fd[nr_cpu][counter][thread_index] < 0) { + int err = errno; + + if (err == EPERM || err == EACCES) + die("Permission error - are you root?\n" + "\t Consider tweaking" + " /proc/sys/kernel/perf_event_paranoid.\n"); + else if (err == ENODEV && profile_cpu != -1) { + die("No such device - did you specify" + " an out-of-range profile CPU?\n"); + } - /* - * If it's cycles then fall back to hrtimer - * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: - */ - if (attr->type == PERF_TYPE_HARDWARE - && attr->config == PERF_COUNT_HW_CPU_CYCLES) { - - if (verbose) - warning(" ... trying to fall back to cpu-clock-ticks\n"); - attr->type = PERF_TYPE_SOFTWARE; - attr->config = PERF_COUNT_SW_CPU_CLOCK; - goto try_again; - } - printf("\n"); - error("perfcounter syscall returned with %d (%s)\n", - fd[nr_cpu][counter], strerror(err)); + /* + * If it's cycles then fall back to hrtimer + * based cpu-clock-tick sw counter, which + * is always available even if no PMU support: + */ + if (attr->type == PERF_TYPE_HARDWARE + && attr->config == PERF_COUNT_HW_CPU_CYCLES) { + + if (verbose) + warning(" ... trying to fall back to cpu-clock-ticks\n"); + attr->type = PERF_TYPE_SOFTWARE; + attr->config = PERF_COUNT_SW_CPU_CLOCK; + goto try_again; + } + printf("\n"); + error("perfcounter syscall returned with %d (%s)\n", + fd[nr_cpu][counter][thread_index], strerror(err)); #if defined(__i386__) || defined(__x86_64__) - if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) - die("No hardware sampling interrupt available. No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.\n"); + if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) + die("No hardware sampling interrupt available." + " No APIC? If so then you can boot the kernel" + " with the \"lapic\" boot parameter to" + " force-enable it.\n"); #endif - die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); - exit(-1); - } + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + exit(-1); + } - h_attr = get_header_attr(attr, counter); - if (h_attr == NULL) - die("nomem\n"); + h_attr = get_header_attr(attr, counter); + if (h_attr == NULL) + die("nomem\n"); - if (!file_new) { - if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { - fprintf(stderr, "incompatible append\n"); - exit(-1); + if (!file_new) { + if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { + fprintf(stderr, "incompatible append\n"); + exit(-1); + } } - } - if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) { - perror("Unable to read perf file descriptor\n"); - exit(-1); - } + if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { + perror("Unable to read perf file descriptor\n"); + exit(-1); + } - if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { - pr_warning("Not enough memory to add id\n"); - exit(-1); - } + if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { + pr_warning("Not enough memory to add id\n"); + exit(-1); + } - assert(fd[nr_cpu][counter] >= 0); - fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); + assert(fd[nr_cpu][counter][thread_index] >= 0); + fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_fd = fd[nr_cpu][counter]; - if (multiplex && multiplex_fd == -1) - multiplex_fd = fd[nr_cpu][counter]; + /* + * First counter acts as the group leader: + */ + if (group && group_fd == -1) + group_fd = fd[nr_cpu][counter][thread_index]; + if (multiplex && multiplex_fd == -1) + multiplex_fd = fd[nr_cpu][counter][thread_index]; - if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { + if (multiplex && fd[nr_cpu][counter][thread_index] != multiplex_fd) { - ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); - assert(ret != -1); - } else { - event_array[nr_poll].fd = fd[nr_cpu][counter]; - event_array[nr_poll].events = POLLIN; - nr_poll++; - - mmap_array[nr_cpu][counter].counter = counter; - mmap_array[nr_cpu][counter].prev = 0; - mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; - mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); - if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { - error("failed to mmap with %d (%s)\n", errno, strerror(errno)); - exit(-1); + ret = ioctl(fd[nr_cpu][counter][thread_index], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); + assert(ret != -1); + } else { + event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; + event_array[nr_poll].events = POLLIN; + nr_poll++; + + mmap_array[nr_cpu][counter][thread_index].counter = counter; + mmap_array[nr_cpu][counter][thread_index].prev = 0; + mmap_array[nr_cpu][counter][thread_index].mask = mmap_pages*page_size - 1; + mmap_array[nr_cpu][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); + if (mmap_array[nr_cpu][counter][thread_index].base == MAP_FAILED) { + error("failed to mmap with %d (%s)\n", errno, strerror(errno)); + exit(-1); + } } - } - if (filter != NULL) { - ret = ioctl(fd[nr_cpu][counter], - PERF_EVENT_IOC_SET_FILTER, filter); - if (ret) { - error("failed to set filter with %d (%s)\n", errno, - strerror(errno)); - exit(-1); + if (filter != NULL) { + ret = ioctl(fd[nr_cpu][counter][thread_index], + PERF_EVENT_IOC_SET_FILTER, filter); + if (ret) { + error("failed to set filter with %d (%s)\n", errno, + strerror(errno)); + exit(-1); + } } } } -static void open_counters(int cpu, pid_t pid) +static void open_counters(int cpu) { int counter; group_fd = -1; for (counter = 0; counter < nr_counters; counter++) - create_counter(counter, cpu, pid); + create_counter(counter, cpu); nr_cpu++; } @@ -529,6 +542,9 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } + if (!system_wide && target_tid == -1 && target_pid == -1) + all_tids[0] = child_pid; + close(child_ready_pipe[1]); close(go_pipe[0]); /* @@ -541,17 +557,12 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } - if (forks && target_pid == -1 && !system_wide) - pid = child_pid; - else - pid = target_pid; - if ((!system_wide && !inherit) || profile_cpu != -1) { - open_counters(profile_cpu, pid); + open_counters(profile_cpu); } else { nr_cpus = read_cpu_map(); for (i = 0; i < nr_cpus; i++) - open_counters(cpumap[i], pid); + open_counters(cpumap[i]); } if (file_new) { @@ -576,7 +587,7 @@ static int __cmd_record(int argc, const char **argv) } if (!system_wide && profile_cpu == -1) - event__synthesize_thread(target_pid, process_synthesized_event, + event__synthesize_thread(target_tid, process_synthesized_event, session); else event__synthesize_threads(process_synthesized_event, session); @@ -599,11 +610,16 @@ static int __cmd_record(int argc, const char **argv) for (;;) { int hits = samples; + int thread; for (i = 0; i < nr_cpu; i++) { for (counter = 0; counter < nr_counters; counter++) { - if (mmap_array[i][counter].base) - mmap_read(&mmap_array[i][counter]); + for (thread = 0; + thread < thread_num; thread++) { + if (mmap_array[i][counter][thread].base) + mmap_read(&mmap_array[i][counter][thread]); + } + } } @@ -616,8 +632,15 @@ static int __cmd_record(int argc, const char **argv) if (done) { for (i = 0; i < nr_cpu; i++) { - for (counter = 0; counter < nr_counters; counter++) - ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE); + for (counter = 0; + counter < nr_counters; + counter++) { + for (thread = 0; + thread < thread_num; + thread++) + ioctl(fd[i][counter][thread], + PERF_EVENT_IOC_DISABLE); + } } } } @@ -649,7 +672,9 @@ static const struct option options[] = { OPT_CALLBACK(0, "filter", NULL, "filter", "event filter", parse_filter), OPT_INTEGER('p', "pid", &target_pid, - "record events on existing pid"), + "record events on existing process id"), + OPT_INTEGER('t', "tid", &target_tid, + "record events on existing thread id"), OPT_INTEGER('r', "realtime", &realtime_prio, "collect data with this RT SCHED_FIFO priority"), OPT_BOOLEAN('R', "raw-samples", &raw_samples, @@ -690,10 +715,12 @@ static const struct option options[] = { int cmd_record(int argc, const char **argv, const char *prefix __used) { int counter; + int i,j; argc = parse_options(argc, argv, options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target_pid == -1 && !system_wide && profile_cpu == -1) + if (!argc && target_pid == -1 && target_tid == -1 && + !system_wide && profile_cpu == -1) usage_with_options(record_usage, options); symbol__init(); @@ -704,6 +731,37 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; } + if (target_pid != -1) { + target_tid = target_pid; + thread_num = find_all_tid(target_pid, &all_tids); + if (thread_num <= 0) { + fprintf(stderr, "Can't find all threads of pid %d\n", + target_pid); + usage_with_options(record_usage, options); + } + } else { + all_tids=malloc(sizeof(pid_t)); + if (!all_tids) + return -ENOMEM; + + all_tids[0] = target_tid; + thread_num = 1; + } + + for (i = 0; i < MAX_NR_CPUS; i++) { + for (j = 0; j < MAX_COUNTERS; j++) { + fd[i][j] = malloc(sizeof(int)*thread_num); + mmap_array[i][j] = malloc( + sizeof(struct mmap_data)*thread_num); + if (!fd[i][j] || !mmap_array[i][j]) + return -ENOMEM; + } + } + event_array = malloc( + sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); + if (!event_array) + return -ENOMEM; + /* * User specified count overrides default frequency. */ diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 5f41244..c92f90f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -46,6 +46,7 @@ #include "util/debug.h" #include "util/header.h" #include "util/cpumap.h" +#include "util/thread.h" #include #include @@ -74,10 +75,13 @@ static int run_count = 1; static int inherit = 1; static int scale = 1; static pid_t target_pid = -1; +static pid_t target_tid = -1; +static pid_t *all_tids = NULL; +static int thread_num = 0; static pid_t child_pid = -1; static int null_run = 0; -static int fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static int event_scaled[MAX_COUNTERS]; @@ -140,9 +144,10 @@ struct stats runtime_branches_stats; #define ERR_PERF_OPEN \ "Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n" -static void create_perf_stat_counter(int counter, int pid) +static void create_perf_stat_counter(int counter) { struct perf_event_attr *attr = attrs + counter; + int thread; if (scale) attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -152,10 +157,11 @@ static void create_perf_stat_counter(int counter, int pid) unsigned int cpu; for (cpu = 0; cpu < nr_cpus; cpu++) { - fd[cpu][counter] = sys_perf_event_open(attr, -1, cpumap[cpu], -1, 0); - if (fd[cpu][counter] < 0 && verbose) + fd[cpu][counter][0] = sys_perf_event_open(attr, + -1, cpumap[cpu], -1, 0); + if (fd[cpu][counter][0] < 0 && verbose) fprintf(stderr, ERR_PERF_OPEN, counter, - fd[cpu][counter], strerror(errno)); + fd[cpu][counter][0], strerror(errno)); } } else { attr->inherit = inherit; @@ -163,11 +169,14 @@ static void create_perf_stat_counter(int counter, int pid) attr->disabled = 1; attr->enable_on_exec = 1; } - - fd[0][counter] = sys_perf_event_open(attr, pid, -1, -1, 0); - if (fd[0][counter] < 0 && verbose) - fprintf(stderr, ERR_PERF_OPEN, counter, - fd[0][counter], strerror(errno)); + for (thread = 0; thread < thread_num; thread++) { + fd[0][counter][thread] = sys_perf_event_open(attr, + all_tids[thread], -1, -1, 0); + if (fd[0][counter][thread] < 0 && verbose) + fprintf(stderr, ERR_PERF_OPEN, counter, + fd[0][counter][thread], + strerror(errno)); + } } } @@ -192,25 +201,28 @@ static void read_counter(int counter) unsigned int cpu; size_t res, nv; int scaled; - int i; + int i, thread; count[0] = count[1] = count[2] = 0; nv = scale ? 3 : 1; for (cpu = 0; cpu < nr_cpus; cpu++) { - if (fd[cpu][counter] < 0) - continue; - - res = read(fd[cpu][counter], single_count, nv * sizeof(u64)); - assert(res == nv * sizeof(u64)); - - close(fd[cpu][counter]); - fd[cpu][counter] = -1; - - count[0] += single_count[0]; - if (scale) { - count[1] += single_count[1]; - count[2] += single_count[2]; + for (thread = 0; thread < thread_num; thread++) { + if (fd[cpu][counter][thread] < 0) + continue; + + res = read(fd[cpu][counter][thread], + single_count, nv * sizeof(u64)); + assert(res == nv * sizeof(u64)); + + close(fd[cpu][counter][thread]); + fd[cpu][counter][thread] = -1; + + count[0] += single_count[0]; + if (scale) { + count[1] += single_count[1]; + count[2] += single_count[2]; + } } } @@ -253,7 +265,6 @@ static int run_perf_stat(int argc __used, const char **argv) unsigned long long t0, t1; int status = 0; int counter; - int pid; int child_ready_pipe[2], go_pipe[2]; const bool forks = (argc > 0); char buf; @@ -299,6 +310,9 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } + if (target_tid == -1 && target_pid == -1 && !system_wide) + all_tids[0] = child_pid; + /* * Wait for the child to be ready to exec. */ @@ -309,12 +323,8 @@ static int run_perf_stat(int argc __used, const char **argv) close(child_ready_pipe[0]); } - if (target_pid == -1) - pid = child_pid; - else - pid = target_pid; for (counter = 0; counter < nr_counters; counter++) - create_perf_stat_counter(counter, pid); + create_perf_stat_counter(counter); /* * Enable counters and exec the command: @@ -433,12 +443,14 @@ static void print_stat(int argc, const char **argv) fprintf(stderr, "\n"); fprintf(stderr, " Performance counter stats for "); - if(target_pid == -1) { + if(target_pid == -1 && target_tid == -1) { fprintf(stderr, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(stderr, " %s", argv[i]); - }else - fprintf(stderr, "task pid \'%d", target_pid); + } else if (target_pid != -1) + fprintf(stderr, "process id \'%d", target_pid); + else + fprintf(stderr, "thread id \'%d", target_tid); fprintf(stderr, "\'"); if (run_count > 1) @@ -493,7 +505,9 @@ static const struct option options[] = { OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), OPT_INTEGER('p', "pid", &target_pid, - "stat events on existing pid"), + "stat events on existing process id"), + OPT_INTEGER('t', "tid", &target_tid, + "stat events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('c', "scale", &scale, @@ -510,10 +524,11 @@ static const struct option options[] = { int cmd_stat(int argc, const char **argv, const char *prefix __used) { int status; + int i,j; argc = parse_options(argc, argv, options, stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target_pid == -1) + if (!argc && target_pid == -1 && target_tid == -1) usage_with_options(stat_usage, options); if (run_count <= 0) usage_with_options(stat_usage, options); @@ -529,6 +544,31 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) else nr_cpus = 1; + if (target_pid != -1) { + target_tid = target_pid; + thread_num = find_all_tid(target_pid, &all_tids); + if (thread_num <= 0) { + fprintf(stderr, "Can't find all threads of pid %d\n", + target_pid); + usage_with_options(stat_usage, options); + } + } else { + all_tids=malloc(sizeof(pid_t)); + if (!all_tids) + return -ENOMEM; + + all_tids[0] = target_tid; + thread_num = 1; + } + + for (i = 0; i < MAX_NR_CPUS; i++) { + for (j = 0; j < MAX_COUNTERS; j++) { + fd[i][j] = malloc(sizeof(int)*thread_num); + if (!fd[i][j]) + return -ENOMEM; + } + } + /* * We dont want to block the signals - that would cause * child tasks to inherit that and Ctrl-C would not work. diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 887ebbf..5f3ac9f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -55,7 +55,7 @@ #include #include -static int fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static int system_wide = 0; @@ -65,6 +65,9 @@ static int count_filter = 5; static int print_entries; static int target_pid = -1; +static int target_tid = -1; +static pid_t *all_tids = NULL; +static int thread_num = 0; static int inherit = 0; static int profile_cpu = -1; static int nr_cpus = 0; @@ -524,13 +527,15 @@ static void print_sym_table(void) if (target_pid != -1) printf(" (target_pid: %d", target_pid); + else if (target_tid != -1) + printf(" (target_tid: %d", target_tid); else printf(" (all"); if (profile_cpu != -1) printf(", cpu: %d)\n", profile_cpu); else { - if (target_pid != -1) + if (target_tid != -1) printf(")\n"); else printf(", %d CPUs)\n", nr_cpus); @@ -1129,16 +1134,21 @@ static void perf_session__mmap_read_counter(struct perf_session *self, md->prev = old; } -static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; -static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; +static struct pollfd *event_array; +static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; static void perf_session__mmap_read(struct perf_session *self) { - int i, counter; + int i, counter, thread_index; for (i = 0; i < nr_cpus; i++) { for (counter = 0; counter < nr_counters; counter++) - perf_session__mmap_read_counter(self, &mmap_array[i][counter]); + for (thread_index = 0; + thread_index < thread_num; + thread_index++) { + perf_session__mmap_read_counter(self, + &mmap_array[i][counter][thread_index]); + } } } @@ -1149,9 +1159,10 @@ static void start_counter(int i, int counter) { struct perf_event_attr *attr; int cpu; + int thread_index; cpu = profile_cpu; - if (target_pid == -1 && profile_cpu == -1) + if (target_tid == -1 && profile_cpu == -1) cpu = cpumap[i]; attr = attrs + counter; @@ -1167,55 +1178,58 @@ static void start_counter(int i, int counter) attr->inherit = (cpu < 0) && inherit; attr->mmap = 1; + for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: - fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); - - if (fd[i][counter] < 0) { - int err = errno; + fd[i][counter][thread_index] = sys_perf_event_open(attr, + all_tids[thread_index], cpu, group_fd, 0); + + if (fd[i][counter][thread_index] < 0) { + int err = errno; + + if (err == EPERM || err == EACCES) + die("No permission - are you root?\n"); + /* + * If it's cycles then fall back to hrtimer + * based cpu-clock-tick sw counter, which + * is always available even if no PMU support: + */ + if (attr->type == PERF_TYPE_HARDWARE + && attr->config == PERF_COUNT_HW_CPU_CYCLES) { + + if (verbose) + warning(" ... trying to fall back to cpu-clock-ticks\n"); + + attr->type = PERF_TYPE_SOFTWARE; + attr->config = PERF_COUNT_SW_CPU_CLOCK; + goto try_again; + } + printf("\n"); + error("perfcounter syscall returned with %d (%s)\n", + fd[i][counter][thread_index], strerror(err)); + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + exit(-1); + } + assert(fd[i][counter][thread_index] >= 0); + fcntl(fd[i][counter][thread_index], F_SETFL, O_NONBLOCK); - if (err == EPERM || err == EACCES) - die("No permission - are you root?\n"); /* - * If it's cycles then fall back to hrtimer - * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: + * First counter acts as the group leader: */ - if (attr->type == PERF_TYPE_HARDWARE - && attr->config == PERF_COUNT_HW_CPU_CYCLES) { - - if (verbose) - warning(" ... trying to fall back to cpu-clock-ticks\n"); - - attr->type = PERF_TYPE_SOFTWARE; - attr->config = PERF_COUNT_SW_CPU_CLOCK; - goto try_again; - } - printf("\n"); - error("perfcounter syscall returned with %d (%s)\n", - fd[i][counter], strerror(err)); - die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); - exit(-1); + if (group && group_fd == -1) + group_fd = fd[i][counter][thread_index]; + + event_array[nr_poll].fd = fd[i][counter][thread_index]; + event_array[nr_poll].events = POLLIN; + nr_poll++; + + mmap_array[i][counter][thread_index].counter = counter; + mmap_array[i][counter][thread_index].prev = 0; + mmap_array[i][counter][thread_index].mask = mmap_pages*page_size - 1; + mmap_array[i][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, + PROT_READ, MAP_SHARED, fd[i][counter][thread_index], 0); + if (mmap_array[i][counter][thread_index].base == MAP_FAILED) + die("failed to mmap with %d (%s)\n", errno, strerror(errno)); } - assert(fd[i][counter] >= 0); - fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); - - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_fd = fd[i][counter]; - - event_array[nr_poll].fd = fd[i][counter]; - event_array[nr_poll].events = POLLIN; - nr_poll++; - - mmap_array[i][counter].counter = counter; - mmap_array[i][counter].prev = 0; - mmap_array[i][counter].mask = mmap_pages*page_size - 1; - mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, - PROT_READ, MAP_SHARED, fd[i][counter], 0); - if (mmap_array[i][counter].base == MAP_FAILED) - die("failed to mmap with %d (%s)\n", errno, strerror(errno)); } static int __cmd_top(void) @@ -1231,8 +1245,8 @@ static int __cmd_top(void) if (session == NULL) return -ENOMEM; - if (target_pid != -1) - event__synthesize_thread(target_pid, event__process, session); + if (target_tid != -1) + event__synthesize_thread(target_tid, event__process, session); else event__synthesize_threads(event__process, session); @@ -1243,7 +1257,7 @@ static int __cmd_top(void) } /* Wait for a minimal set of events before starting the snapshot */ - poll(event_array, nr_poll, 100); + poll(&event_array[0], nr_poll, 100); perf_session__mmap_read(session); @@ -1286,7 +1300,9 @@ static const struct option options[] = { OPT_INTEGER('c', "count", &default_interval, "event period to sample"), OPT_INTEGER('p', "pid", &target_pid, - "profile events on existing pid"), + "profile events on existing process id"), + OPT_INTEGER('t', "tid", &target_tid, + "profile events on existing thread id"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_INTEGER('C', "CPU", &profile_cpu, @@ -1327,6 +1343,7 @@ static const struct option options[] = { int cmd_top(int argc, const char **argv, const char *prefix __used) { int counter; + int i,j; page_size = sysconf(_SC_PAGE_SIZE); @@ -1334,8 +1351,39 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) if (argc) usage_with_options(top_usage, options); + if (target_pid != -1) { + target_tid = target_pid; + thread_num = find_all_tid(target_pid, &all_tids); + if (thread_num <= 0) { + fprintf(stderr, "Can't find all threads of pid %d\n", + target_pid); + usage_with_options(top_usage, options); + } + } else { + all_tids=malloc(sizeof(pid_t)); + if (!all_tids) + return -ENOMEM; + + all_tids[0] = target_tid; + thread_num = 1; + } + + for (i = 0; i < MAX_NR_CPUS; i++) { + for (j = 0; j < MAX_COUNTERS; j++) { + fd[i][j] = malloc(sizeof(int)*thread_num); + mmap_array[i][j] = malloc( + sizeof(struct mmap_data)*thread_num); + if (!fd[i][j] || !mmap_array[i][j]) + return -ENOMEM; + } + } + event_array = malloc( + sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); + if (!event_array) + return -ENOMEM; + /* CPU and PID are mutually exclusive */ - if (target_pid != -1 && profile_cpu != -1) { + if (target_tid > 0 && profile_cpu != -1) { printf("WARNING: PID switch overriding CPU\n"); sleep(1); profile_cpu = -1; @@ -1376,7 +1424,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) attrs[counter].sample_period = default_interval; } - if (target_pid != -1 || profile_cpu != -1) + if (target_tid != -1 || profile_cpu != -1) nr_cpus = 1; else nr_cpus = read_cpu_map(); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index fa96831..ea65062 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -7,6 +7,37 @@ #include "util.h" #include "debug.h" +int find_all_tid(int pid, pid_t ** all_tid) +{ + char name[256]; + int items; + struct dirent **namelist = NULL; + int ret = 0; + int i; + + sprintf(name, "/proc/%d/task", pid); + items = scandir(name, &namelist, NULL, NULL); + if (items <= 0) + return -ENOENT; + *all_tid = malloc(sizeof(pid_t) * items); + if (!*all_tid) { + ret = -ENOMEM; + goto failure; + } + + for (i = 0; i < items; i++) + (*all_tid)[i] = atoi(namelist[i]->d_name); + + ret = items; + +failure: + for (i=0; i Date: Wed, 17 Mar 2010 13:37:00 +0300 Subject: x86, apic: Allow to use certain functions without APIC built-in support In case even if the kernel is configured so that no APIC support is built-in we still may allow to use certain apic functions as dummy calls. In particular we start using it in perf-events code. Note that this is not that same as NOOP apic driver (which is used if APIC support is present but no physical APIC is available), this is for the case when we don't have apic code compiled in at all. Signed-off-by: Cyrill Gorcunov Cc: H. Peter Anvin Cc: Yinghai Lu Cc: Yinghai Lu LKML-Reference: <20100317104356.011052632@openvz.org> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index b4ac2cd..1fa03e0 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -373,6 +373,7 @@ extern atomic_t init_deasserted; extern int wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip); #endif +#ifdef CONFIG_X86_LOCAL_APIC static inline u32 apic_read(u32 reg) { return apic->read(reg); @@ -403,10 +404,19 @@ static inline u32 safe_apic_wait_icr_idle(void) return apic->safe_wait_icr_idle(); } +#else /* CONFIG_X86_LOCAL_APIC */ + +static inline u32 apic_read(u32 reg) { return 0; } +static inline void apic_write(u32 reg, u32 val) { } +static inline u64 apic_icr_read(void) { return 0; } +static inline void apic_icr_write(u32 low, u32 high) { } +static inline void apic_wait_icr_idle(void) { } +static inline u32 safe_apic_wait_icr_idle(void) { return 0; } + +#endif /* CONFIG_X86_LOCAL_APIC */ static inline void ack_APIC_irq(void) { -#ifdef CONFIG_X86_LOCAL_APIC /* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. @@ -414,7 +424,6 @@ static inline void ack_APIC_irq(void) /* Docs say use 0 for future compatibility */ apic_write(APIC_EOI, 0); -#endif } static inline unsigned default_get_apic_id(unsigned long x) -- cgit v0.10.2 From 7335f75e9ca166044e38a96abad422d8e6e364b5 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 17 Mar 2010 13:37:01 +0300 Subject: x86, perf: Use apic_write unconditionally Since apic_write() maps to a plain noop in the !CONFIG_X86_LOCAL_APIC case we're safe to remove this conditional compilation and clean up the code a bit. Signed-off-by: Cyrill Gorcunov Cc: fweisbec@gmail.com Cc: acme@redhat.com Cc: eranian@google.com Cc: peterz@infradead.org LKML-Reference: <20100317104356.232371479@openvz.org> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index c97d5b5..14eca80 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1136,7 +1136,6 @@ void set_perf_event_pending(void) void perf_events_lapic_init(void) { -#ifdef CONFIG_X86_LOCAL_APIC if (!x86_pmu.apic || !x86_pmu_initialized()) return; @@ -1144,7 +1143,6 @@ void perf_events_lapic_init(void) * Always use NMI for PMU */ apic_write(APIC_LVTPC, APIC_DM_NMI); -#endif } static int __kprobes @@ -1168,9 +1166,7 @@ perf_event_nmi_handler(struct notifier_block *self, regs = args->regs; -#ifdef CONFIG_X86_LOCAL_APIC apic_write(APIC_LVTPC, APIC_DM_NMI); -#endif /* * Can't rely on the handled return value to say it was our NMI, two * events could trigger 'simultaneously' raising two back-to-back NMIs. diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index a11ce73..0367889 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -363,10 +363,8 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) } if (handled) { -#ifdef CONFIG_X86_LOCAL_APIC /* p4 quirk: unmask it again */ apic_write(APIC_LVTPC, apic_read(APIC_LVTPC) & ~APIC_LVT_MASKED); -#endif inc_irq_stat(apic_perf_irqs); } -- cgit v0.10.2 From 55632770d7298835645489828af87f854c47749c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 18 Mar 2010 16:51:16 +0100 Subject: perf events: Fix false positive build warning with older GCC's gcc 4.2.1 produces: util/probe-event.c: In function 'add_perf_probe_events': util/probe-event.c:883: warning: 'tev' may be used uninitialized in this function make: *** [util/probe-event.o] Error 1 Newer GCCs get this right. To work it around, initialize the variable to NULL so that older GCCs see it as initialized too. Cc: Masami Hiramatsu Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f333269..c6603f3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -880,7 +880,7 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, int ntevs, bool allow_suffix) { int i, fd; - struct kprobe_trace_event *tev; + struct kprobe_trace_event *tev = NULL; char buf[64]; const char *event, *group; struct strlist *namelist; -- cgit v0.10.2 From f34edbc1cdb0f8f83d94e1d668dd6e41abf0defb Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 18 Mar 2010 18:33:07 +0800 Subject: perf, x86: Add a key to simplify template lookup in Pentium-4 PMU Currently, we use opcode(Event and Event-Selector) + emask to look up template in p4_templates. But cache events (L1-dcache-load-misses, LLC-load-misses, etc) use the same event(P4_REPLAY_EVENT) to do the counting, ie, they have the same opcode and emask. So we can not use current lookup mechanism to find the template for cache events. This patch introduces a "key", which is the index into p4_templates. The low 12 bits of CCCR are reserved, so we can hide the "key" in the low 12 bits of hwc->config. We extract the key from hwc->config and then quickly find the template. Signed-off-by: Lin Ming Reviewed-by: Cyrill Gorcunov Cc: Peter Zijlstra LKML-Reference: <1268908387.13901.127.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index b842b32..7d3406a 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -65,6 +65,7 @@ #define P4_CCCR_THREAD_SINGLE 0x00010000U #define P4_CCCR_THREAD_BOTH 0x00020000U #define P4_CCCR_THREAD_ANY 0x00030000U +#define P4_CCCR_RESERVED 0x00000fffU /* Non HT mask */ #define P4_CCCR_MASK \ @@ -116,7 +117,7 @@ #define p4_config_pack_escr(v) (((u64)(v)) << 32) #define p4_config_pack_cccr(v) (((u64)(v)) & 0xffffffffULL) #define p4_config_unpack_escr(v) (((u64)(v)) >> 32) -#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xffffffffULL) +#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xfffff000ULL) #define p4_config_unpack_emask(v) \ ({ \ @@ -126,6 +127,8 @@ t; \ }) +#define p4_config_unpack_key(v) (((u64)(v)) & P4_CCCR_RESERVED) + #define P4_CONFIG_HT_SHIFT 63 #define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT) diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 0367889..3e97ed3 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -18,6 +18,7 @@ struct p4_event_template { u32 opcode; /* ESCR event + CCCR selector */ u64 config; /* packed predefined bits */ int dep; /* upstream dependency event index */ + int key; /* index into p4_templates */ unsigned int emask; /* ESCR EventMask */ unsigned int escr_msr[2]; /* ESCR MSR for this event */ unsigned int cntr[2]; /* counter index (offset) */ @@ -39,38 +40,31 @@ static DEFINE_PER_CPU(struct p4_pmu_res, p4_pmu_config); */ struct p4_event_template p4_templates[] = { [0] = { - .opcode = P4_UOP_TYPE, - .config = 0, - .dep = -1, - .emask = - P4_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS) | - P4_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES), - .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, - .cntr = { 16, 17 }, - }, - [1] = { .opcode = P4_GLOBAL_POWER_EVENTS, .config = 0, .dep = -1, + .key = 0, .emask = P4_EVENT_ATTR(P4_GLOBAL_POWER_EVENTS, RUNNING), .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, .cntr = { 0, 2 }, }, - [2] = { + [1] = { .opcode = P4_INSTR_RETIRED, .config = 0, .dep = -1, /* needs front-end tagging */ + .key = 1, .emask = P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG) | P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG), .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, .cntr = { 12, 14 }, }, - [3] = { + [2] = { .opcode = P4_BSQ_CACHE_REFERENCE, .config = 0, .dep = -1, + .key = 2, .emask = P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) | P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) | @@ -81,10 +75,11 @@ struct p4_event_template p4_templates[] = { .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, .cntr = { 0, 2 }, }, - [4] = { + [3] = { .opcode = P4_BSQ_CACHE_REFERENCE, .config = 0, .dep = -1, + .key = 3, .emask = P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) | P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) | @@ -92,10 +87,11 @@ struct p4_event_template p4_templates[] = { .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, .cntr = { 0, 3 }, }, - [5] = { + [4] = { .opcode = P4_RETIRED_BRANCH_TYPE, .config = 0, .dep = -1, + .key = 4, .emask = P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CONDITIONAL) | P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CALL) | @@ -104,48 +100,38 @@ struct p4_event_template p4_templates[] = { .escr_msr = { MSR_P4_TBPU_ESCR0, MSR_P4_TBPU_ESCR1 }, .cntr = { 4, 6 }, }, - [6] = { + [5] = { .opcode = P4_MISPRED_BRANCH_RETIRED, .config = 0, .dep = -1, + .key = 5, .emask = P4_EVENT_ATTR(P4_MISPRED_BRANCH_RETIRED, NBOGUS), .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, .cntr = { 12, 14 }, }, - [7] = { + [6] = { .opcode = P4_FSB_DATA_ACTIVITY, .config = p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE), .dep = -1, + .key = 6, .emask = P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_DRV) | P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OWN), .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, .cntr = { 0, 2 }, }, -}; - -static struct p4_event_template *p4_event_map[PERF_COUNT_HW_MAX] = { - /* non-halted CPU clocks */ - [PERF_COUNT_HW_CPU_CYCLES] = &p4_templates[1], - - /* retired instructions: dep on tagging the FSB */ - [PERF_COUNT_HW_INSTRUCTIONS] = &p4_templates[2], - - /* cache hits */ - [PERF_COUNT_HW_CACHE_REFERENCES] = &p4_templates[3], - - /* cache misses */ - [PERF_COUNT_HW_CACHE_MISSES] = &p4_templates[4], - - /* branch instructions retired */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = &p4_templates[5], - - /* mispredicted branches retired */ - [PERF_COUNT_HW_BRANCH_MISSES] = &p4_templates[6], - - /* bus ready clocks (cpu is driving #DRDY_DRV\#DRDY_OWN): */ - [PERF_COUNT_HW_BUS_CYCLES] = &p4_templates[7], + [7] = { + .opcode = P4_UOP_TYPE, + .config = 0, + .dep = -1, + .key = 7, + .emask = + P4_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS) | + P4_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES), + .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, + .cntr = { 16, 17 }, + }, }; static u64 p4_pmu_event_map(int hw_event) @@ -153,11 +139,11 @@ static u64 p4_pmu_event_map(int hw_event) struct p4_event_template *tpl; u64 config; - if (hw_event > ARRAY_SIZE(p4_event_map)) { + if (hw_event > ARRAY_SIZE(p4_templates)) { printk_once(KERN_ERR "PMU: Incorrect event index\n"); return 0; } - tpl = p4_event_map[hw_event]; + tpl = &p4_templates[hw_event]; /* * fill config up according to @@ -167,6 +153,7 @@ static u64 p4_pmu_event_map(int hw_event) config |= p4_config_pack_escr(P4_EVENT_UNPACK_EVENT(tpl->opcode) << P4_EVNTSEL_EVENT_SHIFT); config |= p4_config_pack_escr(tpl->emask << P4_EVNTSEL_EVENTMASK_SHIFT); config |= p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(tpl->opcode) << P4_CCCR_ESCR_SELECT_SHIFT); + config |= p4_config_pack_cccr(hw_event & P4_CCCR_RESERVED); /* on HT machine we need a special bit */ if (p4_ht_active() && p4_ht_thread(raw_smp_processor_id())) @@ -187,17 +174,12 @@ static inline int p4_pmu_emask_match(unsigned int dst, unsigned int src) static struct p4_event_template *p4_pmu_template_lookup(u64 config) { - u32 opcode = p4_config_unpack_opcode(config); - unsigned int emask = p4_config_unpack_emask(config); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(p4_templates); i++) { - if (opcode == p4_templates[i].opcode && - p4_pmu_emask_match(emask, p4_templates[i].emask)) - return &p4_templates[i]; - } + int key = p4_config_unpack_key(config); - return NULL; + if (key < ARRAY_SIZE(p4_templates)) + return &p4_templates[key]; + else + return NULL; } /* @@ -564,7 +546,7 @@ static __initconst struct x86_pmu p4_pmu = { .perfctr = MSR_P4_BPU_PERFCTR0, .event_map = p4_pmu_event_map, .raw_event = p4_pmu_raw_event, - .max_events = ARRAY_SIZE(p4_event_map), + .max_events = ARRAY_SIZE(p4_templates), .get_event_constraints = x86_get_event_constraints, /* * IF HT disabled we may need to use all -- cgit v0.10.2 From cb7d6b5053e86598735d9af19930f5929f007b7f Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 18 Mar 2010 18:33:12 +0800 Subject: perf, x86: Add cache events for the Pentium-4 PMU Move the HT bit setting code from p4_pmu_event_map to p4_hw_config. So the cache events can get HT bit set correctly. Tested on my P4 desktop, below 6 cache events work: L1-dcache-load-misses LLC-load-misses dTLB-load-misses dTLB-store-misses iTLB-loads iTLB-load-misses Signed-off-by: Lin Ming Reviewed-by: Cyrill Gorcunov Cc: Peter Zijlstra LKML-Reference: <1268908392.13901.128.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 1cd58cd..aef562c 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -357,6 +357,8 @@ #define MSR_P4_U2L_ESCR0 0x000003b0 #define MSR_P4_U2L_ESCR1 0x000003b1 +#define MSR_P4_PEBS_MATRIX_VERT 0x000003f2 + /* Intel Core-based CPU performance counters */ #define MSR_CORE_PERF_FIXED_CTR0 0x00000309 #define MSR_CORE_PERF_FIXED_CTR1 0x0000030a diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 7d3406a..871249c 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -708,4 +708,14 @@ enum P4_EVENTS_ATTR { P4_MAKE_EVENT_ATTR(P4_INSTR_COMPLETED, BOGUS, 1), }; +enum { + KEY_P4_L1D_OP_READ_RESULT_MISS, + KEY_P4_LL_OP_READ_RESULT_MISS, + KEY_P4_DTLB_OP_READ_RESULT_MISS, + KEY_P4_DTLB_OP_WRITE_RESULT_MISS, + KEY_P4_ITLB_OP_READ_RESULT_ACCESS, + KEY_P4_ITLB_OP_READ_RESULT_MISS, + KEY_P4_UOP_TYPE, +}; + #endif /* PERF_EVENT_P4_H */ diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 3e97ed3..b7bf991 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -19,6 +19,11 @@ struct p4_event_template { u64 config; /* packed predefined bits */ int dep; /* upstream dependency event index */ int key; /* index into p4_templates */ + u64 msr; /* + * the high 32 bits set into MSR_IA32_PEBS_ENABLE and + * the low 32 bits set into MSR_P4_PEBS_MATRIX_VERT + * for cache events + */ unsigned int emask; /* ESCR EventMask */ unsigned int escr_msr[2]; /* ESCR MSR for this event */ unsigned int cntr[2]; /* counter index (offset) */ @@ -31,6 +36,67 @@ struct p4_pmu_res { static DEFINE_PER_CPU(struct p4_pmu_res, p4_pmu_config); +#define P4_CACHE_EVENT_CONFIG(event, bit) \ + p4_config_pack_escr(P4_EVENT_UNPACK_EVENT(event) << P4_EVNTSEL_EVENT_SHIFT) | \ + p4_config_pack_escr((event##_##bit) << P4_EVNTSEL_EVENTMASK_SHIFT) | \ + p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(event) << P4_CCCR_ESCR_SELECT_SHIFT) + +static __initconst u64 p4_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + /* 1stL_cache_load_miss_retired */ + [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) + | KEY_P4_L1D_OP_READ_RESULT_MISS, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + /* 2ndL_cache_load_miss_retired */ + [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) + | KEY_P4_LL_OP_READ_RESULT_MISS, + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + /* DTLB_load_miss_retired */ + [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) + | KEY_P4_DTLB_OP_READ_RESULT_MISS, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + /* DTLB_store_miss_retired */ + [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) + | KEY_P4_DTLB_OP_WRITE_RESULT_MISS, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + /* ITLB_reference.HIT */ + [ C(RESULT_ACCESS) ] = P4_CACHE_EVENT_CONFIG(P4_ITLB_REFERENCE, HIT) + | KEY_P4_ITLB_OP_READ_RESULT_ACCESS, + + /* ITLB_reference.MISS */ + [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_ITLB_REFERENCE, MISS) + | KEY_P4_ITLB_OP_READ_RESULT_MISS, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + /* * WARN: CCCR1 doesn't have a working enable bit so try to not * use it if possible @@ -121,11 +187,77 @@ struct p4_event_template p4_templates[] = { .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, .cntr = { 0, 2 }, }, - [7] = { + [KEY_P4_L1D_OP_READ_RESULT_MISS] = { + .opcode = P4_REPLAY_EVENT, + .config = 0, + .dep = -1, + .msr = (u64)(1 << 0 | 1 << 24) << 32 | (1 << 0), + .key = KEY_P4_L1D_OP_READ_RESULT_MISS, + .emask = + P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, + .cntr = { 16, 17 }, + }, + [KEY_P4_LL_OP_READ_RESULT_MISS] = { + .opcode = P4_REPLAY_EVENT, + .config = 0, + .dep = -1, + .msr = (u64)(1 << 1 | 1 << 24) << 32 | (1 << 0), + .key = KEY_P4_LL_OP_READ_RESULT_MISS, + .emask = + P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, + .cntr = { 16, 17 }, + }, + [KEY_P4_DTLB_OP_READ_RESULT_MISS] = { + .opcode = P4_REPLAY_EVENT, + .config = 0, + .dep = -1, + .msr = (u64)(1 << 2 | 1 << 24) << 32 | (1 << 0), + .key = KEY_P4_DTLB_OP_READ_RESULT_MISS, + .emask = + P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, + .cntr = { 16, 17 }, + }, + [KEY_P4_DTLB_OP_WRITE_RESULT_MISS] = { + .opcode = P4_REPLAY_EVENT, + .config = 0, + .dep = -1, + .msr = (u64)(1 << 2 | 1 << 24) << 32 | (1 << 1), + .key = KEY_P4_DTLB_OP_WRITE_RESULT_MISS, + .emask = + P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, + .cntr = { 16, 17 }, + }, + [KEY_P4_ITLB_OP_READ_RESULT_ACCESS] = { + .opcode = P4_ITLB_REFERENCE, + .config = 0, + .dep = -1, + .msr = 0, + .key = KEY_P4_ITLB_OP_READ_RESULT_ACCESS, + .emask = + P4_EVENT_ATTR(P4_ITLB_REFERENCE, HIT), + .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, + .cntr = { 0, 2 }, + }, + [KEY_P4_ITLB_OP_READ_RESULT_MISS] = { + .opcode = P4_ITLB_REFERENCE, + .config = 0, + .dep = -1, + .msr = 0, + .key = KEY_P4_ITLB_OP_READ_RESULT_MISS, + .emask = + P4_EVENT_ATTR(P4_ITLB_REFERENCE, MISS), + .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, + .cntr = { 0, 2 }, + }, + [KEY_P4_UOP_TYPE] = { .opcode = P4_UOP_TYPE, .config = 0, .dep = -1, - .key = 7, + .key = KEY_P4_UOP_TYPE, .emask = P4_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS) | P4_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES), @@ -155,10 +287,6 @@ static u64 p4_pmu_event_map(int hw_event) config |= p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(tpl->opcode) << P4_CCCR_ESCR_SELECT_SHIFT); config |= p4_config_pack_cccr(hw_event & P4_CCCR_RESERVED); - /* on HT machine we need a special bit */ - if (p4_ht_active() && p4_ht_thread(raw_smp_processor_id())) - config = p4_set_ht_bit(config); - return config; } @@ -211,6 +339,10 @@ static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) /* Count user and OS events unless not requested to */ hwc->config |= p4_config_pack_escr(p4_default_escr_conf(cpu, attr->exclude_kernel, attr->exclude_user)); + /* on HT machine we need a special bit */ + if (p4_ht_active() && p4_ht_thread(cpu)) + hwc->config = p4_set_ht_bit(hwc->config); + return 0; } @@ -271,6 +403,12 @@ static void p4_pmu_enable_event(struct perf_event *event) pr_crit("%s: Wrong index: %d\n", __func__, hwc->idx); return; } + + if (tpl->msr) { + (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, tpl->msr >> 32); + (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, tpl->msr & 0xffffffff); + } + escr_base = (u64)tpl->escr_msr[thread]; /* @@ -577,6 +715,9 @@ static __init int p4_pmu_init(void) return -ENODEV; } + memcpy(hw_cache_event_ids, p4_hw_cache_event_ids, + sizeof(hw_cache_event_ids)); + pr_cont("Netburst events, "); x86_pmu = p4_pmu; -- cgit v0.10.2 From 4b24a88b35e15e04bd8f2c5dda65b5dc8ebca05f Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 17 Mar 2010 23:21:01 +0200 Subject: perf_events: Fix resource leak in x86 __hw_perf_event_init() If reserve_pmc_hardware() succeeds but reserve_ds_buffers() fails, then we need to release_pmc_hardware. It won't be done by the destroy() callback because we return before setting it in case of error. Signed-off-by: Stephane Eranian Cc: Cc: peterz@infradead.org Cc: paulus@samba.org Cc: davem@davemloft.net Cc: fweisbec@gmail.com Cc: robert.richter@amd.com Cc: perfmon2-devel@lists.sf.net LKML-Reference: <4ba1568b.15185e0a.182a.7802@mx.google.com> Signed-off-by: Ingo Molnar -- arch/x86/kernel/cpu/perf_event.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 14eca80..f571f51 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -455,8 +455,11 @@ static int __hw_perf_event_init(struct perf_event *event) if (atomic_read(&active_events) == 0) { if (!reserve_pmc_hardware()) err = -EBUSY; - else + else { err = reserve_ds_buffers(); + if (err) + release_pmc_hardware(); + } } if (!err) atomic_inc(&active_events); -- cgit v0.10.2 From 9c8c6bad3137112d2c7bf3d215b736ee4215fa74 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 19 Mar 2010 00:12:56 +0300 Subject: x86, perf: Fix few cosmetic dabs for P4 pmu (comments and constantify) - A few ESCR have escaped fixing at previous attempt. - p4_escr_map is read only, make it const. Nothing serious. Signed-off-by: Cyrill Gorcunov Cc: Lin Ming LKML-Reference: <20100318211256.GH5062@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 871249c..2a1a57f 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -401,13 +401,13 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) #define P4_RETIRED_MISPRED_BRANCH_TYPE P4_EVENT_PACK(0x05, 0x02) /* * MSR_P4_TBPU_ESCR0: 4, 5 - * MSR_P4_TBPU_ESCR0: 6, 7 + * MSR_P4_TBPU_ESCR1: 6, 7 */ #define P4_RETIRED_BRANCH_TYPE P4_EVENT_PACK(0x04, 0x02) /* * MSR_P4_TBPU_ESCR0: 4, 5 - * MSR_P4_TBPU_ESCR0: 6, 7 + * MSR_P4_TBPU_ESCR1: 6, 7 */ #define P4_RESOURCE_STALL P4_EVENT_PACK(0x01, 0x01) diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index b7bf991..b8a811a 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -545,7 +545,7 @@ static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) } /* ESCRs are not sequential in memory so we need a map */ -static unsigned int p4_escr_map[ARCH_P4_TOTAL_ESCR] = { +static const unsigned int p4_escr_map[ARCH_P4_TOTAL_ESCR] = { MSR_P4_ALF_ESCR0, /* 0 */ MSR_P4_ALF_ESCR1, /* 1 */ MSR_P4_BPU_ESCR0, /* 2 */ -- cgit v0.10.2 From 40b7e05e17eef31ff30fe08dfc2424ef653a792c Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 19 Mar 2010 15:28:58 +0800 Subject: perf, x86: Fix key indexing in Pentium-4 PMU Index 0-6 in p4_templates are reserved for common hardware events. So p4_templates is arranged as below: 0 - 6: common hardware events 7 - N: cache events N+1 - ...: other raw events Reported-by: Cyrill Gorcunov Signed-off-by: Lin Ming Acked-by: Cyrill Gorcunov Cc: Peter Zijlstra LKML-Reference: <1268983738.13901.142.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 2a1a57f..facf961 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -709,7 +709,7 @@ enum P4_EVENTS_ATTR { }; enum { - KEY_P4_L1D_OP_READ_RESULT_MISS, + KEY_P4_L1D_OP_READ_RESULT_MISS = PERF_COUNT_HW_MAX, KEY_P4_LL_OP_READ_RESULT_MISS, KEY_P4_DTLB_OP_READ_RESULT_MISS, KEY_P4_DTLB_OP_WRITE_RESULT_MISS, -- cgit v0.10.2 From 301fde27c7fcd0380b02b175d547e894ff65d78a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 22 Mar 2010 13:09:33 -0300 Subject: perf: Fix orphan callchain branches Callchains have markers inside their capture to tell we enter a context (kernel, user, ...). Those are not displayed in the callchains but they are incidentally an active part of the radix tree where callchains are stored, just like any other address. If we have the two following callchains: addr1 -> addr2 -> user context -> addr3 addr1 -> addr2 -> user context -> addr4 addr1 -> addr2 -> addr 5 This is pretty common if addr1 and addr2 are part of an interrupt path, addr3 and addr4 are user addresses and addr5 is a kernel non interrupt path. This will be stored as follows in the tree: addr1 addr2 / \ / addr5 user context / \ addr3 addr4 But we ignore the context markers in the report, hence the addr3 and addr4 will appear as orphan branches: |--28.30%-- hrtimer_interrupt | smp_apic_timer_interrupt | apic_timer_interrupt | | <------------- here, no parent! | | | | | |--11.11%-- 0x7fae7bccb875 | | | | | |--11.11%-- 0xffffffffff60013b | | | | | |--11.11%-- __pthread_mutex_lock_internal | | | | | |--11.11%-- __errno_location Fix this by removing the context markers when we process the callchains to the tree. Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Frederic Weisbecker Cc: Paul Mackerras Cc: Peter Zijlstra Signed-off-by: Arnaldo Carvalho de Melo LKML-Reference: <1269274173-20328-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1f9f869..d609afb 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -83,6 +83,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, { struct symbol **syms = NULL, *parent = NULL; bool hit; + int err; struct hist_entry *he; struct event_stat_id *stats; struct perf_event_attr *attr; @@ -109,8 +110,11 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (symbol_conf.use_callchain) { if (!hit) callchain_init(&he->callchain); - append_chain(&he->callchain, data->callchain, syms); + err = append_chain(&he->callchain, data->callchain, syms); free(syms); + + if (err) + return err; } return 0; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index b3b7125..883844e 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, Frederic Weisbecker + * Copyright (C) 2009-2010, Frederic Weisbecker * * Handle the callchains from the stream in an ad-hoc radix tree and then * sort them in an rbtree. @@ -183,12 +183,23 @@ create_child(struct callchain_node *parent, bool inherit_children) return new; } + +struct resolved_ip { + u64 ip; + struct symbol *sym; +}; + +struct resolved_chain { + u64 nr; + struct resolved_ip ips[0]; +}; + + /* * Fill the node with callchain values */ static void -fill_node(struct callchain_node *node, struct ip_callchain *chain, - int start, struct symbol **syms) +fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) { unsigned int i; @@ -200,8 +211,8 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, perror("not enough memory for the code path tree"); return; } - call->ip = chain->ips[i]; - call->sym = syms[i]; + call->ip = chain->ips[i].ip; + call->sym = chain->ips[i].sym; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -210,13 +221,13 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, } static void -add_child(struct callchain_node *parent, struct ip_callchain *chain, - int start, struct symbol **syms) +add_child(struct callchain_node *parent, struct resolved_chain *chain, + int start) { struct callchain_node *new; new = create_child(parent, false); - fill_node(new, chain, start, syms); + fill_node(new, chain, start); new->children_hit = 0; new->hit = 1; @@ -228,9 +239,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, * Then create another child to host the given callchain of new branch */ static void -split_add_child(struct callchain_node *parent, struct ip_callchain *chain, - struct callchain_list *to_split, int idx_parents, int idx_local, - struct symbol **syms) +split_add_child(struct callchain_node *parent, struct resolved_chain *chain, + struct callchain_list *to_split, int idx_parents, int idx_local) { struct callchain_node *new; struct list_head *old_tail; @@ -257,7 +267,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, /* create a new child for the new branch if any */ if (idx_total < chain->nr) { parent->hit = 0; - add_child(parent, chain, idx_total, syms); + add_child(parent, chain, idx_total); parent->children_hit++; } else { parent->hit = 1; @@ -265,32 +275,33 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, } static int -__append_chain(struct callchain_node *root, struct ip_callchain *chain, - unsigned int start, struct symbol **syms); +__append_chain(struct callchain_node *root, struct resolved_chain *chain, + unsigned int start); static void -__append_chain_children(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms, unsigned int start) +__append_chain_children(struct callchain_node *root, + struct resolved_chain *chain, + unsigned int start) { struct callchain_node *rnode; /* lookup in childrens */ chain_for_each_child(rnode, root) { - unsigned int ret = __append_chain(rnode, chain, start, syms); + unsigned int ret = __append_chain(rnode, chain, start); if (!ret) goto inc_children_hit; } /* nothing in children, add to the current node */ - add_child(root, chain, start, syms); + add_child(root, chain, start); inc_children_hit: root->children_hit++; } static int -__append_chain(struct callchain_node *root, struct ip_callchain *chain, - unsigned int start, struct symbol **syms) +__append_chain(struct callchain_node *root, struct resolved_chain *chain, + unsigned int start) { struct callchain_list *cnode; unsigned int i = start; @@ -302,13 +313,19 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, * anywhere inside a function. */ list_for_each_entry(cnode, &root->val, list) { + struct symbol *sym; + if (i == chain->nr) break; - if (cnode->sym && syms[i]) { - if (cnode->sym->start != syms[i]->start) + + sym = chain->ips[i].sym; + + if (cnode->sym && sym) { + if (cnode->sym->start != sym->start) break; - } else if (cnode->ip != chain->ips[i]) + } else if (cnode->ip != chain->ips[i].ip) break; + if (!found) found = true; i++; @@ -320,7 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, /* we match only a part of the node. Split it and add the new chain */ if (i - start < root->val_nr) { - split_add_child(root, chain, cnode, start, i - start, syms); + split_add_child(root, chain, cnode, start, i - start); return 0; } @@ -331,15 +348,51 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, } /* We match the node and still have a part remaining */ - __append_chain_children(root, chain, syms, i); + __append_chain_children(root, chain, i); return 0; } -void append_chain(struct callchain_node *root, struct ip_callchain *chain, +static void +filter_context(struct ip_callchain *old, struct resolved_chain *new, + struct symbol **syms) +{ + int i, j = 0; + + for (i = 0; i < (int)old->nr; i++) { + if (old->ips[i] >= PERF_CONTEXT_MAX) + continue; + + new->ips[j].ip = old->ips[i]; + new->ips[j].sym = syms[i]; + j++; + } + + new->nr = j; +} + + +int append_chain(struct callchain_node *root, struct ip_callchain *chain, struct symbol **syms) { + struct resolved_chain *filtered; + if (!chain->nr) - return; - __append_chain_children(root, chain, syms, 0); + return 0; + + filtered = malloc(sizeof(*filtered) + + chain->nr * sizeof(struct resolved_ip)); + if (!filtered) + return -ENOMEM; + + filter_context(chain, filtered, syms); + + if (!filtered->nr) + goto end; + + __append_chain_children(root, filtered, 0); +end: + free(filtered); + + return 0; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index ad4626d..bbd76da 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -56,6 +56,6 @@ static inline u64 cumul_hits(struct callchain_node *node) } int register_callchain_param(struct callchain_param *param); -void append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms); +int append_chain(struct callchain_node *root, struct ip_callchain *chain, + struct symbol **syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c37da8b..5843a9c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -328,8 +328,6 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, left_margin); i = 0; list_for_each_entry(chain, &child->val, list) { - if (chain->ip >= PERF_CONTEXT_MAX) - continue; ret += ipchain__fprintf_graph(fp, chain, depth, new_depth_mask, i++, new_total, @@ -368,9 +366,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, int ret = 0; list_for_each_entry(chain, &self->val, list) { - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - if (!i++ && sort__first_dimension == SORT_SYM) continue; -- cgit v0.10.2 From f3a1f0ea9432ec395cd112f42201e8e523c07bc5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 13:10:25 -0300 Subject: perf newt: Properly restore the screen when error exiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show an OK message box with the last message sent via pr_err, etc. Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269274229-20442-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/perf.c b/tools/perf/perf.c index d2de839..2826e70 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -264,11 +264,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) set_debugfs_path(); status = p->fn(argc, argv, prefix); + exit_browser(status); + if (status) return status & 0xff; - exit_browser(); - /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 47b12a3..4b9aab7 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -77,10 +77,10 @@ static inline void setup_browser(void) { setup_pager(); } -static inline void exit_browser(void) {} +static inline void exit_browser(bool wait_for_ok __used) {} #else void setup_browser(void); -void exit_browser(void); +void exit_browser(bool wait_for_ok); #endif extern const char *editor_program; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2d19e7a..3c2ef95 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -170,18 +170,20 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtFormDestroy(form); } +static char browser__last_msg[1024]; + int browser__show_help(const char *format, va_list ap) { int ret; static int backlog; - static char msg[1024]; - ret = vsnprintf(msg + backlog, sizeof(msg) - backlog, format, ap); + ret = vsnprintf(browser__last_msg + backlog, + sizeof(browser__last_msg) - backlog, format, ap); backlog += ret; - if (msg[backlog - 1] == '\n') { + if (browser__last_msg[backlog - 1] == '\n') { newtPopHelpLine(); - newtPushHelpLine(msg); + newtPushHelpLine(browser__last_msg); newtRefresh(); backlog = 0; } @@ -200,8 +202,13 @@ void setup_browser(void) newtPushHelpLine(" "); } -void exit_browser(void) +void exit_browser(bool wait_for_ok) { - if (use_browser) + if (use_browser) { + if (wait_for_ok) { + char title[] = "Fatal Error", ok[] = "Ok"; + newtWinMessage(title, ok, browser__last_msg); + } newtFinished(); + } } -- cgit v0.10.2 From 4b4da7f76660ea8b5aa45615165c48f62167ffa8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 22 Mar 2010 13:10:26 -0300 Subject: perf probe: Cleanup debuginfo related code Cleanup debuginfo related code to eliminate fragile code which pointed by Ingo (Thanks!). 1) Invert logic of NO_DWARF_SUPPORT to DWARF_SUPPORT. 2) For removing assymetric/local variable ifdefs, introduce more helper functions. 3) Change options order to reduce the number of ifdefs. Reported-by: Ingo Molnar Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <1269274229-20442-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0abd25e..c991818 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -506,9 +506,8 @@ endif ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); - BASIC_CFLAGS += -DNO_DWARF_SUPPORT else - BASIC_CFLAGS += -I/usr/include/elfutils + BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT EXTLIBS += -lelf -ldw LIB_OBJS += util/probe-finder.o endif diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e0dafd9..cf2ffa5 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -109,7 +109,7 @@ static int opt_del_probe_event(const struct option *opt __used, return 0; } -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { @@ -126,7 +126,7 @@ static const char * const probe_usage[] = { "perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", "perf probe [] --del '[GROUP:]EVENT' ...", "perf probe --list", -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT "perf probe --line 'LINEDESC'", #endif NULL @@ -135,20 +135,16 @@ static const char * const probe_usage[] = { static const struct option options[] = { OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), -#ifndef NO_DWARF_SUPPORT - OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, - "file", "vmlinux pathname"), -#endif OPT_BOOLEAN('l', "list", ¶ms.list_events, "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, -#ifdef NO_DWARF_SUPPORT - "[EVENT=]FUNC[+OFF|%return] [ARG ...]", -#else +#ifdef DWARF_SUPPORT "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" " [ARG ...]", +#else + "[EVENT=]FUNC[+OFF|%return] [ARG ...]", #endif "probe point definition, where\n" "\t\tGROUP:\tGroup name (optional)\n" @@ -156,23 +152,25 @@ static const struct option options[] = { "\t\tFUNC:\tFunction name\n" "\t\tOFF:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" -#ifdef NO_DWARF_SUPPORT - "\t\tARG:\tProbe argument (only \n" -#else +#ifdef DWARF_SUPPORT "\t\tSRC:\tSource code path\n" "\t\tRL:\tRelative line number from function entry.\n" "\t\tAL:\tAbsolute line number in file.\n" "\t\tPT:\tLazy expression of line code.\n" "\t\tARG:\tProbe argument (local variable name or\n" -#endif "\t\t\tkprobe-tracer argument format.)\n", +#else + "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n", +#endif opt_add_probe_event), OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" " with existing name"), -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", "Show source code lines.", opt_show_lines), + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), #endif OPT__DRY_RUN(&probe_event_dry_run), OPT_END() @@ -211,7 +209,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) return 0; } -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT if (params.show_lines) { if (params.nevents != 0 || params.dellist) { pr_warning(" Error: Don't use --line with" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index c6603f3..3fc0be7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -42,7 +42,8 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include "parse-events.h" /* For debugfs_path */ +#include "trace-event.h" /* For __unused */ +#include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" #include "probe-finder.h" @@ -70,10 +71,11 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } +static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; -/* Initialize symbol maps for vmlinux */ +/* Initialize symbol maps and path of vmlinux */ static void init_vmlinux(void) { symbol_conf.sort_by_name = true; @@ -89,7 +91,7 @@ static void init_vmlinux(void) die("Failed to create kernel maps."); } -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT static int open_vmlinux(void) { if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { @@ -99,6 +101,176 @@ static int open_vmlinux(void) pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } + +static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) +{ + struct symbol *sym; + int fd, ret = 0; + + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tp->symbol, NULL); + if (sym) { + fd = open_vmlinux(); + ret = find_perf_probe_point(fd, sym->start + tp->offset, pp); + close(fd); + } + if (ret <= 0) { + pp->function = xstrdup(tp->symbol); + pp->offset = tp->offset; + } + pp->retprobe = tp->retprobe; +} + +/* Try to find perf_probe_event with debuginfo */ +static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) +{ + bool need_dwarf = perf_probe_event_need_dwarf(pev); + int fd, ntevs; + + fd = open_vmlinux(); + if (fd < 0) { + if (need_dwarf) + die("Could not open debuginfo file."); + + pr_debug("Could not open vmlinux. Try to use symbols.\n"); + return 0; + } + + /* Searching trace events corresponding to probe event */ + ntevs = find_kprobe_trace_events(fd, pev, tevs); + close(fd); + + if (ntevs > 0) /* Succeeded to find trace events */ + return ntevs; + + if (ntevs == 0) /* No error but failed to find probe point. */ + die("Probe point '%s' not found. - probe not added.", + synthesize_perf_probe_point(&pev->point)); + + /* Error path */ + if (need_dwarf) { + if (ntevs == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - " + "please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); + } + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); + return 0; + +} + +#define LINEBUF_SIZE 256 +#define NR_ADDITIONAL_LINES 2 + +static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +{ + char buf[LINEBUF_SIZE]; + const char *color = PERF_COLOR_BLUE; + + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%7u %s", l, buf); + else + color_fprintf(stdout, color, " %s", buf); + } + + while (strlen(buf) == LINEBUF_SIZE - 1 && + buf[LINEBUF_SIZE - 2] != '\n') { + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%s", buf); + else + color_fprintf(stdout, color, "%s", buf); + } + } + return; +error: + if (feof(fp)) + die("Source file is shorter than expected."); + else + die("File read error: %s", strerror(errno)); +} + +/* + * Show line-range always requires debuginfo to find source file and + * line number. + */ +void show_line_range(struct line_range *lr) +{ + unsigned int l = 1; + struct line_node *ln; + FILE *fp; + int fd, ret; + + /* Search a line range */ + init_vmlinux(); + fd = open_vmlinux(); + if (fd < 0) + die("Could not open debuginfo file."); + ret = find_line_range(fd, lr); + if (ret <= 0) + die("Source line is not found.\n"); + close(fd); + + setup_pager(); + + if (lr->function) + fprintf(stdout, "<%s:%d>\n", lr->function, + lr->start - lr->offset); + else + fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); + + fp = fopen(lr->path, "r"); + if (fp == NULL) + die("Failed to open %s: %s", lr->path, strerror(errno)); + /* Skip to starting line number */ + while (l < lr->start) + show_one_line(fp, l++, true, false); + + list_for_each_entry(ln, &lr->line_list, list) { + while (ln->line > l) + show_one_line(fp, (l++) - lr->offset, false, false); + show_one_line(fp, (l++) - lr->offset, false, true); + } + + if (lr->end == INT_MAX) + lr->end = l + NR_ADDITIONAL_LINES; + while (l < lr->end && !feof(fp)) + show_one_line(fp, (l++) - lr->offset, false, false); + + fclose(fp); +} + +#else /* !DWARF_SUPPORT */ + +static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) +{ + pp->function = xstrdup(tp->symbol); + pp->offset = tp->offset; + pp->retprobe = tp->retprobe; +} + +static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs __unused) +{ + if (perf_probe_event_need_dwarf(pev)) + die("Debuginfo-analysis is not supported"); + return 0; +} + +void show_line_range(struct line_range *lr __unused) +{ + die("Debuginfo-analysis is not supported"); +} + #endif void parse_line_range_desc(const char *arg, struct line_range *lr) @@ -592,32 +764,14 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, { char buf[64]; int i; -#ifndef NO_DWARF_SUPPORT - struct symbol *sym; - int fd, ret = 0; - - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], - tev->point.symbol, NULL); - if (sym) { - fd = open_vmlinux(); - ret = find_perf_probe_point(fd, sym->start + tev->point.offset, - &pev->point); - close(fd); - } - if (ret <= 0) { - pev->point.function = xstrdup(tev->point.symbol); - pev->point.offset = tev->point.offset; - } -#else - /* Convert trace_point to probe_point */ - pev->point.function = xstrdup(tev->point.symbol); - pev->point.offset = tev->point.offset; -#endif - pev->point.retprobe = tev->point.retprobe; + /* Convert event/group name */ pev->event = xstrdup(tev->event); pev->group = xstrdup(tev->group); + /* Convert trace_point to probe_point */ + convert_to_perf_probe_point(&tev->point, &pev->point); + /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -944,52 +1098,13 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs) { struct symbol *sym; - bool need_dwarf; -#ifndef NO_DWARF_SUPPORT - int fd; -#endif int ntevs = 0, i; struct kprobe_trace_event *tev; - need_dwarf = perf_probe_event_need_dwarf(pev); - - if (need_dwarf) -#ifdef NO_DWARF_SUPPORT - die("Debuginfo-analysis is not supported"); -#else /* !NO_DWARF_SUPPORT */ - pr_debug("Some probes require debuginfo.\n"); - - fd = open_vmlinux(); - if (fd < 0) { - if (need_dwarf) - die("Could not open debuginfo file."); - - pr_debug("Could not open vmlinux/module file." - " Try to use symbols.\n"); - goto end_dwarf; - } - - /* Searching probe points */ - ntevs = find_kprobe_trace_events(fd, pev, tevs); - - if (ntevs > 0) /* Found */ - goto found; - - if (ntevs == 0) /* No error but failed to find probe point. */ - die("Probe point '%s' not found. - probe not added.", - synthesize_perf_probe_point(&pev->point)); - - /* Error path */ - if (need_dwarf) { - if (ntevs == -ENOENT) - pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); - } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - -end_dwarf: -#endif /* !NO_DWARF_SUPPORT */ + /* Convert perf_probe_event with debuginfo */ + ntevs = try_to_find_kprobe_trace_events(pev, tevs); + if (ntevs > 0) + return ntevs; /* Allocate trace event buffer */ ntevs = 1; @@ -1012,10 +1127,7 @@ end_dwarf: if (!sym) die("Kernel symbol \'%s\' not found - probe not added.", tev->point.symbol); -#ifndef NO_DWARF_SUPPORT -found: - close(fd); -#endif + return ntevs; } @@ -1133,90 +1245,3 @@ void del_perf_probe_events(struct strlist *dellist) close(fd); } -#define LINEBUF_SIZE 256 -#define NR_ADDITIONAL_LINES 2 - -static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) -{ - char buf[LINEBUF_SIZE]; - const char *color = PERF_COLOR_BLUE; - - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%7u %s", l, buf); - else - color_fprintf(stdout, color, " %s", buf); - } - - while (strlen(buf) == LINEBUF_SIZE - 1 && - buf[LINEBUF_SIZE - 2] != '\n') { - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%s", buf); - else - color_fprintf(stdout, color, "%s", buf); - } - } - return; -error: - if (feof(fp)) - die("Source file is shorter than expected."); - else - die("File read error: %s", strerror(errno)); -} - -void show_line_range(struct line_range *lr) -{ - unsigned int l = 1; - struct line_node *ln; - FILE *fp; -#ifndef NO_DWARF_SUPPORT - int fd, ret; -#endif - - /* Search a line range */ - init_vmlinux(); -#ifndef NO_DWARF_SUPPORT - fd = open_vmlinux(); - if (fd < 0) - die("Could not open debuginfo file."); - ret = find_line_range(fd, lr); - if (ret <= 0) - die("Source line is not found.\n"); - close(fd); -#endif - - setup_pager(); - - if (lr->function) - fprintf(stdout, "<%s:%d>\n", lr->function, - lr->start - lr->offset); - else - fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); - - fp = fopen(lr->path, "r"); - if (fp == NULL) - die("Failed to open %s: %s", lr->path, strerror(errno)); - /* Skip to starting line number */ - while (l < lr->start) - show_one_line(fp, l++, true, false); - - list_for_each_entry(ln, &lr->line_list, list) { - while (ln->line > l) - show_one_line(fp, (l++) - lr->offset, false, false); - show_one_line(fp, (l++) - lr->offset, false, true); - } - - if (lr->end == INT_MAX) - lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp)) - show_one_line(fp, (l++) - lr->offset, false, false); - - fclose(fp); -} - - diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 2f2307d..3564f22 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -15,7 +15,7 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, struct kprobe_trace_event **tevs); @@ -57,6 +57,6 @@ struct line_finder { int found; }; -#endif /* NO_DWARF_SUPPORT */ +#endif /* DWARF_SUPPORT */ #endif /*_PROBE_FINDER_H */ -- cgit v0.10.2 From ca721e45b39209415d2288dbac3667b26d9d1def Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 22 Mar 2010 13:10:27 -0300 Subject: perf probe: Add NO_DWARF make option Add NO_DWARF make option for testing build without libdw. Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <1269274229-20442-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/Makefile b/tools/perf/Makefile index c991818..6903645 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -150,6 +150,8 @@ all:: # Define LDFLAGS=-static to build a static binary. # # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. +# +# Define NO_DWARF if you do not want debug-info analysis feature at all. PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @$(SHELL_PATH) util/PERF-VERSION-GEN @@ -507,10 +509,12 @@ endif ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); else +ifndef NO_DWARF BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT EXTLIBS += -lelf -ldw LIB_OBJS += util/probe-finder.o endif +endif ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); -- cgit v0.10.2 From 084ab9f862416b2ddb4bb9804884de19bf09774d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 13:10:28 -0300 Subject: perf stat: Better report failure to collect system wide stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: [acme@doppio linux-2.6-tip]$ perf stat -a sleep 1s Performance counter stats for 'sleep 1s': task-clock-msecs context-switches CPU-migrations page-faults cycles instructions branches branch-misses cache-references cache-misses 1.016998463 seconds time elapsed [acme@doppio linux-2.6-tip]$ Now: [acme@doppio linux-2.6-tip]$ perf stat -a sleep 1s No permission to collect system-wide stats. Consider tweaking /proc/sys/kernel/perf_event_paranoid. [acme@doppio linux-2.6-tip]$ Reported-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269274229-20442-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c92f90f..1036ca7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -144,10 +144,11 @@ struct stats runtime_branches_stats; #define ERR_PERF_OPEN \ "Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n" -static void create_perf_stat_counter(int counter) +static int create_perf_stat_counter(int counter) { struct perf_event_attr *attr = attrs + counter; int thread; + int ncreated = 0; if (scale) attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -159,9 +160,11 @@ static void create_perf_stat_counter(int counter) for (cpu = 0; cpu < nr_cpus; cpu++) { fd[cpu][counter][0] = sys_perf_event_open(attr, -1, cpumap[cpu], -1, 0); - if (fd[cpu][counter][0] < 0 && verbose) - fprintf(stderr, ERR_PERF_OPEN, counter, - fd[cpu][counter][0], strerror(errno)); + if (fd[cpu][counter][0] < 0) + pr_debug(ERR_PERF_OPEN, counter, + fd[cpu][counter][0], strerror(errno)); + else + ++ncreated; } } else { attr->inherit = inherit; @@ -172,12 +175,16 @@ static void create_perf_stat_counter(int counter) for (thread = 0; thread < thread_num; thread++) { fd[0][counter][thread] = sys_perf_event_open(attr, all_tids[thread], -1, -1, 0); - if (fd[0][counter][thread] < 0 && verbose) - fprintf(stderr, ERR_PERF_OPEN, counter, - fd[0][counter][thread], - strerror(errno)); + if (fd[0][counter][thread] < 0) + pr_debug(ERR_PERF_OPEN, counter, + fd[0][counter][thread], + strerror(errno)); + else + ++ncreated; } } + + return ncreated; } /* @@ -264,7 +271,7 @@ static int run_perf_stat(int argc __used, const char **argv) { unsigned long long t0, t1; int status = 0; - int counter; + int counter, ncreated = 0; int child_ready_pipe[2], go_pipe[2]; const bool forks = (argc > 0); char buf; @@ -324,7 +331,16 @@ static int run_perf_stat(int argc __used, const char **argv) } for (counter = 0; counter < nr_counters; counter++) - create_perf_stat_counter(counter); + ncreated += create_perf_stat_counter(counter); + + if (ncreated == 0) { + pr_err("No permission to collect %sstats.\n" + "Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n", + system_wide ? "system-wide " : ""); + if (child_pid != -1) + kill(child_pid, SIGTERM); + return -1; + } /* * Enable counters and exec the command: @@ -587,7 +603,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) status = run_perf_stat(argc, argv); } - print_stat(argc, argv); + if (status != -1) + print_stat(argc, argv); return status; } -- cgit v0.10.2 From 478b0973bf8c90db3677fbb8d812e2bdefc43d9b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 13:10:29 -0300 Subject: perf tools: Exit browser before printing usage when unkown option passed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If not the screen will get garbled when using newt. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269274229-20442-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index efebd5b..79dfa0c 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -500,6 +500,7 @@ int usage_with_options_internal(const char * const *usagestr, void usage_with_options(const char * const *usagestr, const struct option *opts) { + exit_browser(false); usage_with_options_internal(usagestr, opts, 0); exit(129); } -- cgit v0.10.2 From 4ded2b250f1fbba4e414d17dc55ee513485c0aa1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 22 Mar 2010 17:52:49 -0300 Subject: perf report: Implement Newt callgraphs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starts collapsed, allows annotating by pressing 'A' or 'a' on the symbol, be it the top level one or any of the symbols in the chains. It (ab)uses the only tree widget in newt, that is actually a checkbox tree that we use with just one option ('.'), end result is usable but we really need to create a custom widget tree so that we can use the data structures we have (hist_entry rb_tree + callchain rb_tree + lists), so that we reduce the memory footprint by not creating a mirror set of data structures in the newtCheckboxTree widget. Thanks to Frédéric Weisbacker for fixing the orphanage problem in 301fde2, without that we were tripping a newt bug (fix already sent to newt's maintainer). Signed-off-by: Arnaldo Carvalho de Melo Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269291169-29820-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3c2ef95..12b229b 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -28,11 +28,197 @@ static newtComponent newt_form__new(void) return self; } +/* + * When debugging newt problems it was useful to be able to "unroll" + * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate + * a source file with the sequence of calls to these methods, to then + * tweak the arrays to get the intended results, so I'm keeping this code + * here, may be useful again in the future. + */ +#undef NEWT_DEBUG + +static void newt_checkbox_tree__add(newtComponent tree, const char *str, + void *priv, int *indexes) +{ +#ifdef NEWT_DEBUG + /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ + int i = 0, len = 40 - strlen(str); + + fprintf(stderr, + "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", + len, len, " ", str, priv); + while (indexes[i] != NEWT_ARG_LAST) { + if (indexes[i] != NEWT_ARG_APPEND) + fprintf(stderr, " %d,", indexes[i]); + else + fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); + ++i; + } + fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); + fflush(stderr); +#endif + newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); +} + +static char *callchain_list__sym_name(struct callchain_list *self, + char *bf, size_t bfsize) +{ + if (self->sym) + return self->sym->name; + + snprintf(bf, bfsize, "%#Lx", self->ip); + return bf; +} + +static void __callchain__append_graph_browser(struct callchain_node *self, + newtComponent tree, u64 total, + int *indexes, int depth) +{ + struct rb_node *node; + u64 new_total, remaining; + int idx = 0; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = self->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&self->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + int first = true, printed = 0; + int chain_idx = -1; + remaining -= cumul; + + indexes[depth] = NEWT_ARG_APPEND; + indexes[depth + 1] = NEWT_ARG_LAST; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], + *alloc_str = NULL; + const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + + if (first) { + double percent = cumul * 100.0 / new_total; + + first = false; + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } else { + indexes[depth] = idx; + indexes[depth + 1] = NEWT_ARG_APPEND; + indexes[depth + 2] = NEWT_ARG_LAST; + ++chain_idx; + } + newt_checkbox_tree__add(tree, str, chain->sym, indexes); + free(alloc_str); + ++printed; + } + + indexes[depth] = idx; + if (chain_idx != -1) + indexes[depth + 1] = chain_idx; + if (printed != 0) + ++idx; + __callchain__append_graph_browser(child, tree, new_total, indexes, + depth + (chain_idx != -1 ? 2 : 1)); + node = next; + } +} + +static void callchain__append_graph_browser(struct callchain_node *self, + newtComponent tree, u64 total, + int *indexes, int parent_idx) +{ + struct callchain_list *chain; + int i = 0; + + indexes[1] = NEWT_ARG_APPEND; + indexes[2] = NEWT_ARG_LAST; + + list_for_each_entry(chain, &self->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *str; + + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + + if (!i++ && sort__first_dimension == SORT_SYM) + continue; + + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + newt_checkbox_tree__add(tree, str, chain->sym, indexes); + } + + indexes[1] = parent_idx; + indexes[2] = NEWT_ARG_APPEND; + indexes[3] = NEWT_ARG_LAST; + __callchain__append_graph_browser(self, tree, total, indexes, 2); +} + +static void hist_entry__append_callchain_browser(struct hist_entry *self, + newtComponent tree, u64 total, int parent_idx) +{ + struct rb_node *rb_node; + int indexes[1024] = { [0] = parent_idx, }; + int idx = 0; + struct callchain_node *chain; + + rb_node = rb_first(&self->sorted_chain); + while (rb_node) { + chain = rb_entry(rb_node, struct callchain_node, rb_node); + switch (callchain_param.mode) { + case CHAIN_FLAT: + break; + case CHAIN_GRAPH_ABS: /* falldown */ + case CHAIN_GRAPH_REL: + callchain__append_graph_browser(chain, tree, total, indexes, idx++); + break; + case CHAIN_NONE: + default: + break; + } + rb_node = rb_next(rb_node); + } +} + +/* + * FIXME: get lib/string.c linked with perf somehow + */ +static char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} + +static char *strim(char *s) +{ + size_t size; + char *end; + + s = skip_spaces(s); + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + return s; +} + static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent listbox, u64 total) + newtComponent tree, u64 total) { - char bf[1024]; - size_t len; + char bf[1024], *s; FILE *fp; if (symbol_conf.exclude_other && !self->parent) @@ -42,28 +228,46 @@ static size_t hist_entry__append_browser(struct hist_entry *self, if (fp == NULL) return 0; - len = hist_entry__fprintf(self, NULL, false, 0, fp, total); - + hist_entry__fprintf(self, NULL, false, 0, fp, total); fclose(fp); - newtListboxAppendEntry(listbox, bf, self); - return len; + + /* + * FIXME: We shouldn't need to trim, as the printing routines shouldn't + * add spaces it in the first place, the stdio output routines should + * call a __snprintf method instead of the current __print (that + * actually is a __fprintf) one, but get the raw string and _then_ add + * the newline, as this is a detail of stdio printing, not needed in + * other UIs, e.g. newt. + */ + s = strim(bf); + + if (symbol_conf.use_callchain) { + int indexes[2]; + + indexes[0] = NEWT_ARG_APPEND; + indexes[1] = NEWT_ARG_LAST; + newt_checkbox_tree__add(tree, s, self->sym, indexes); + } else + newtListboxAppendEntry(tree, s, self->sym); + + return strlen(s); } -static void hist_entry__annotate_browser(struct hist_entry *self) +static void symbol__annotate_browser(const struct symbol *self) { FILE *fp; int cols, rows; - newtComponent form, listbox; + newtComponent form, tree; struct newtExitStruct es; char *str; size_t line_len, max_line_len = 0; size_t max_usable_width; char *line = NULL; - if (self->sym == NULL) + if (self == NULL) return; - if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->sym->name) < 0) + if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0) return; fp = popen(str, "r"); @@ -72,7 +276,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) newtPushHelpLine("Press ESC to exit"); newtGetScreenSize(&cols, &rows); - listbox = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); + tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); while (!feof(fp)) { if (getline(&line, &line_len, fp) < 0 || !line_len) @@ -82,7 +286,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (line_len > max_line_len) max_line_len = line_len; - newtListboxAppendEntry(listbox, line, NULL); + newtListboxAppendEntry(tree, line, NULL); } fclose(fp); free(line); @@ -91,11 +295,11 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (max_line_len > max_usable_width) max_line_len = max_usable_width; - newtListboxSetWidth(listbox, max_line_len); + newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->name); form = newt_form__new(); - newtFormAddComponents(form, listbox, NULL); + newtFormAddComponents(form, tree, NULL); newtFormRun(form, &es); newtFormDestroy(form); @@ -110,25 +314,27 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, { struct sort_entry *se; struct rb_node *nd; + char seq[] = "."; unsigned int width; char *col_width = symbol_conf.col_width_list_str; - int rows; - size_t max_len = 0; + int rows, cols, idx; + int max_len = 0; char str[1024]; - newtComponent form, listbox; + newtComponent form, tree; struct newtExitStruct es; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); - newtGetScreenSize(NULL, &rows); - - form = newt_form__new(); + newtGetScreenSize(&cols, &rows); - listbox = newtListbox(1, 1, rows - 2, (NEWT_FLAG_SCROLL | - NEWT_FLAG_BORDER | - NEWT_FLAG_RETURNEXIT)); + if (symbol_conf.use_callchain) + tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) @@ -147,27 +353,48 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, } } + idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - size_t len = hist_entry__append_browser(h, listbox, session_total); + int len = hist_entry__append_browser(h, tree, session_total); if (len > max_len) max_len = len; + if (symbol_conf.use_callchain) { + hist_entry__append_callchain_browser(h, tree, session_total, idx++); + if (idx > 3300) + break; + } } - newtListboxSetWidth(listbox, max_len); - newtFormAddComponents(form, listbox, NULL); + if (max_len > cols) + max_len = cols - 3; + + if (!symbol_conf.use_callchain) + newtListboxSetWidth(tree, max_len); + + newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), + rows - 5, "Report"); + form = newt_form__new(); + newtFormAddHotKey(form, 'A'); + newtFormAddHotKey(form, 'a'); + newtFormAddComponents(form, tree, NULL); while (1) { - struct hist_entry *selection; + const struct symbol *selection; newtFormRun(form, &es); - if (es.reason == NEWT_EXIT_HOTKEY) + if (es.reason == NEWT_EXIT_HOTKEY && + toupper(es.u.key) != 'A') break; - selection = newtListboxGetCurrent(listbox); - hist_entry__annotate_browser(selection); + if (!symbol_conf.use_callchain) + selection = newtListboxGetCurrent(tree); + else + selection = newtCheckboxTreeGetCurrent(tree); + symbol__annotate_browser(selection); } newtFormDestroy(form); + newtPopWindow(); } static char browser__last_msg[1024]; -- cgit v0.10.2 From 88978e562302c836c1c4597700c79d971e93abc0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 23 Mar 2010 14:33:58 -0300 Subject: perf archive: Explain how to use the generated tarball MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [root@doppio ~]# perf archive Now please run: $ tar xvf perf.data.tar.bz2 -C ~/.debug wherever you need to run 'perf report' on. [root@doppio ~]# Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269365638-10223-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh index 910468e..2e7a4f4 100644 --- a/tools/perf/perf-archive.sh +++ b/tools/perf/perf-archive.sh @@ -30,4 +30,7 @@ done tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST rm -f $MANIFEST $BUILDIDS +echo -e "Now please run:\n" +echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" +echo "wherever you need to run 'perf report' on." exit 0 -- cgit v0.10.2 From d814f30105798b6677ecb73ed61d691ff96dada9 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 24 Mar 2010 12:09:26 +0800 Subject: x86, perf: Add raw events support for the P4 PMU The adding of raw event support lead to complete code refactoring. I hope is became more readable then it was. The list of changes: 1) The 64bit config field is enough to hold all information we need to track event details. To achieve it we used *own* enum for events selection in ESCR register and map this key into proper value at moment of event enabling. For the same reason we use 12LSB bits in CCCR register -- to track which exactly cache trace event was requested. And we cear this bits at real 'write' moment. 2) There is no per-cpu area reserved for P4 PMU anymore. We don't need it. All is held by config. 3) Now we may use any available counter, ie we try to grab any possible counter. v2: - Lin Ming reported the lack of ESCR selector in CCCR for cache events v3: - Don't loose cache event codes at config unpacking procedure, we may need it one day so no obscure hack behind our back, better to clear reserved bits explicitly when needed (thanks Ming for pointing out) - Lin Ming fixed misplaced opcodes in cache events Signed-off-by: Cyrill Gorcunov Tested-by: Lin Ming Signed-off-by: Lin Ming Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian Cc: Robert Richter Cc: Frederic Weisbecker Cc: Cyrill Gorcunov Cc: Peter Zijlstra LKML-Reference: <1269403766.3409.6.camel@minggr.sh.intel.com> [ v4: did a few whitespace fixlets ] Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index facf961..b05400a 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -15,38 +15,40 @@ * perf-MSRs are not shared and every thread has its * own perf-MSRs set) */ -#define ARCH_P4_TOTAL_ESCR (46) -#define ARCH_P4_RESERVED_ESCR (2) /* IQ_ESCR(0,1) not always present */ -#define ARCH_P4_MAX_ESCR (ARCH_P4_TOTAL_ESCR - ARCH_P4_RESERVED_ESCR) -#define ARCH_P4_MAX_CCCR (18) -#define ARCH_P4_MAX_COUNTER (ARCH_P4_MAX_CCCR / 2) - -#define P4_EVNTSEL_EVENT_MASK 0x7e000000U -#define P4_EVNTSEL_EVENT_SHIFT 25 -#define P4_EVNTSEL_EVENTMASK_MASK 0x01fffe00U -#define P4_EVNTSEL_EVENTMASK_SHIFT 9 -#define P4_EVNTSEL_TAG_MASK 0x000001e0U -#define P4_EVNTSEL_TAG_SHIFT 5 -#define P4_EVNTSEL_TAG_ENABLE 0x00000010U -#define P4_EVNTSEL_T0_OS 0x00000008U -#define P4_EVNTSEL_T0_USR 0x00000004U -#define P4_EVNTSEL_T1_OS 0x00000002U -#define P4_EVNTSEL_T1_USR 0x00000001U +#define ARCH_P4_TOTAL_ESCR (46) +#define ARCH_P4_RESERVED_ESCR (2) /* IQ_ESCR(0,1) not always present */ +#define ARCH_P4_MAX_ESCR (ARCH_P4_TOTAL_ESCR - ARCH_P4_RESERVED_ESCR) +#define ARCH_P4_MAX_CCCR (18) +#define ARCH_P4_MAX_COUNTER (ARCH_P4_MAX_CCCR / 2) + +#define P4_ESCR_EVENT_MASK 0x7e000000U +#define P4_ESCR_EVENT_SHIFT 25 +#define P4_ESCR_EVENTMASK_MASK 0x01fffe00U +#define P4_ESCR_EVENTMASK_SHIFT 9 +#define P4_ESCR_TAG_MASK 0x000001e0U +#define P4_ESCR_TAG_SHIFT 5 +#define P4_ESCR_TAG_ENABLE 0x00000010U +#define P4_ESCR_T0_OS 0x00000008U +#define P4_ESCR_T0_USR 0x00000004U +#define P4_ESCR_T1_OS 0x00000002U +#define P4_ESCR_T1_USR 0x00000001U + +#define P4_ESCR_EVENT(v) ((v) << P4_ESCR_EVENT_SHIFT) +#define P4_ESCR_EMASK(v) ((v) << P4_ESCR_EVENTMASK_SHIFT) +#define P4_ESCR_TAG(v) ((v) << P4_ESCR_TAG_SHIFT) /* Non HT mask */ -#define P4_EVNTSEL_MASK \ - (P4_EVNTSEL_EVENT_MASK | \ - P4_EVNTSEL_EVENTMASK_MASK | \ - P4_EVNTSEL_TAG_MASK | \ - P4_EVNTSEL_TAG_ENABLE | \ - P4_EVNTSEL_T0_OS | \ - P4_EVNTSEL_T0_USR) +#define P4_ESCR_MASK \ + (P4_ESCR_EVENT_MASK | \ + P4_ESCR_EVENTMASK_MASK | \ + P4_ESCR_TAG_MASK | \ + P4_ESCR_TAG_ENABLE | \ + P4_ESCR_T0_OS | \ + P4_ESCR_T0_USR) /* HT mask */ -#define P4_EVNTSEL_MASK_HT \ - (P4_EVNTSEL_MASK | \ - P4_EVNTSEL_T1_OS | \ - P4_EVNTSEL_T1_USR) +#define P4_ESCR_MASK_HT \ + (P4_ESCR_MASK | P4_ESCR_T1_OS | P4_ESCR_T1_USR) #define P4_CCCR_OVF 0x80000000U #define P4_CCCR_CASCADE 0x40000000U @@ -56,7 +58,6 @@ #define P4_CCCR_EDGE 0x01000000U #define P4_CCCR_THRESHOLD_MASK 0x00f00000U #define P4_CCCR_THRESHOLD_SHIFT 20 -#define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) #define P4_CCCR_COMPLEMENT 0x00080000U #define P4_CCCR_COMPARE 0x00040000U #define P4_CCCR_ESCR_SELECT_MASK 0x0000e000U @@ -67,6 +68,13 @@ #define P4_CCCR_THREAD_ANY 0x00030000U #define P4_CCCR_RESERVED 0x00000fffU +#define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) +#define P4_CCCR_ESEL(v) ((v) << P4_CCCR_ESCR_SELECT_SHIFT) + +/* Custom bits in reerved CCCR area */ +#define P4_CCCR_CACHE_OPS_MASK 0x0000003fU + + /* Non HT mask */ #define P4_CCCR_MASK \ (P4_CCCR_OVF | \ @@ -81,25 +89,11 @@ P4_CCCR_ENABLE) /* HT mask */ -#define P4_CCCR_MASK_HT \ - (P4_CCCR_MASK | \ - P4_CCCR_THREAD_ANY) +#define P4_CCCR_MASK_HT (P4_CCCR_MASK | P4_CCCR_THREAD_ANY) -/* - * format is 32 bit: ee ss aa aa - * where - * ee - 8 bit event - * ss - 8 bit selector - * aa aa - 16 bits reserved for tags/attributes - */ -#define P4_EVENT_PACK(event, selector) (((event) << 24) | ((selector) << 16)) -#define P4_EVENT_UNPACK_EVENT(packed) (((packed) >> 24) & 0xff) -#define P4_EVENT_UNPACK_SELECTOR(packed) (((packed) >> 16) & 0xff) -#define P4_EVENT_PACK_ATTR(attr) ((attr)) -#define P4_EVENT_UNPACK_ATTR(packed) ((packed) & 0xffff) -#define P4_MAKE_EVENT_ATTR(class, name, bit) class##_##name = (1 << bit) -#define P4_EVENT_ATTR(class, name) class##_##name -#define P4_EVENT_ATTR_STR(class, name) __stringify(class##_##name) +#define P4_GEN_ESCR_EMASK(class, name, bit) \ + class##__##name = ((1 << bit) << P4_ESCR_EVENTMASK_SHIFT) +#define P4_ESCR_EMASK_BIT(class, name) class##__##name /* * config field is 64bit width and consists of @@ -117,35 +111,29 @@ #define p4_config_pack_escr(v) (((u64)(v)) << 32) #define p4_config_pack_cccr(v) (((u64)(v)) & 0xffffffffULL) #define p4_config_unpack_escr(v) (((u64)(v)) >> 32) -#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xfffff000ULL) +#define p4_config_unpack_cccr(v) (((u64)(v)) & 0xffffffffULL) #define p4_config_unpack_emask(v) \ ({ \ u32 t = p4_config_unpack_escr((v)); \ - t &= P4_EVNTSEL_EVENTMASK_MASK; \ - t >>= P4_EVNTSEL_EVENTMASK_SHIFT; \ + t = t & P4_ESCR_EVENTMASK_MASK; \ + t = t >> P4_ESCR_EVENTMASK_SHIFT; \ + t; \ + }) + +#define p4_config_unpack_event(v) \ + ({ \ + u32 t = p4_config_unpack_escr((v)); \ + t = t & P4_ESCR_EVENT_MASK; \ + t = t >> P4_ESCR_EVENT_SHIFT; \ t; \ }) -#define p4_config_unpack_key(v) (((u64)(v)) & P4_CCCR_RESERVED) +#define p4_config_unpack_cache_event(v) (((u64)(v)) & P4_CCCR_CACHE_OPS_MASK) #define P4_CONFIG_HT_SHIFT 63 #define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT) -static inline u32 p4_config_unpack_opcode(u64 config) -{ - u32 e, s; - - /* - * we don't care about HT presence here since - * event opcode doesn't depend on it - */ - e = (p4_config_unpack_escr(config) & P4_EVNTSEL_EVENT_MASK) >> P4_EVNTSEL_EVENT_SHIFT; - s = (p4_config_unpack_cccr(config) & P4_CCCR_ESCR_SELECT_MASK) >> P4_CCCR_ESCR_SELECT_SHIFT; - - return P4_EVENT_PACK(e, s); -} - static inline bool p4_is_event_cascaded(u64 config) { u32 cccr = p4_config_unpack_cccr(config); @@ -212,19 +200,73 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) if (!p4_ht_thread(cpu)) { if (!exclude_os) - escr |= P4_EVNTSEL_T0_OS; + escr |= P4_ESCR_T0_OS; if (!exclude_usr) - escr |= P4_EVNTSEL_T0_USR; + escr |= P4_ESCR_T0_USR; } else { if (!exclude_os) - escr |= P4_EVNTSEL_T1_OS; + escr |= P4_ESCR_T1_OS; if (!exclude_usr) - escr |= P4_EVNTSEL_T1_USR; + escr |= P4_ESCR_T1_USR; } return escr; } +enum P4_EVENTS { + P4_EVENT_TC_DELIVER_MODE, + P4_EVENT_BPU_FETCH_REQUEST, + P4_EVENT_ITLB_REFERENCE, + P4_EVENT_MEMORY_CANCEL, + P4_EVENT_MEMORY_COMPLETE, + P4_EVENT_LOAD_PORT_REPLAY, + P4_EVENT_STORE_PORT_REPLAY, + P4_EVENT_MOB_LOAD_REPLAY, + P4_EVENT_PAGE_WALK_TYPE, + P4_EVENT_BSQ_CACHE_REFERENCE, + P4_EVENT_IOQ_ALLOCATION, + P4_EVENT_IOQ_ACTIVE_ENTRIES, + P4_EVENT_FSB_DATA_ACTIVITY, + P4_EVENT_BSQ_ALLOCATION, + P4_EVENT_BSQ_ACTIVE_ENTRIES, + P4_EVENT_SSE_INPUT_ASSIST, + P4_EVENT_PACKED_SP_UOP, + P4_EVENT_PACKED_DP_UOP, + P4_EVENT_SCALAR_SP_UOP, + P4_EVENT_SCALAR_DP_UOP, + P4_EVENT_64BIT_MMX_UOP, + P4_EVENT_128BIT_MMX_UOP, + P4_EVENT_X87_FP_UOP, + P4_EVENT_TC_MISC, + P4_EVENT_GLOBAL_POWER_EVENTS, + P4_EVENT_TC_MS_XFER, + P4_EVENT_UOP_QUEUE_WRITES, + P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, + P4_EVENT_RETIRED_BRANCH_TYPE, + P4_EVENT_RESOURCE_STALL, + P4_EVENT_WC_BUFFER, + P4_EVENT_B2B_CYCLES, + P4_EVENT_BNR, + P4_EVENT_SNOOP, + P4_EVENT_RESPONSE, + P4_EVENT_FRONT_END_EVENT, + P4_EVENT_EXECUTION_EVENT, + P4_EVENT_REPLAY_EVENT, + P4_EVENT_INSTR_RETIRED, + P4_EVENT_UOPS_RETIRED, + P4_EVENT_UOP_TYPE, + P4_EVENT_BRANCH_RETIRED, + P4_EVENT_MISPRED_BRANCH_RETIRED, + P4_EVENT_X87_ASSIST, + P4_EVENT_MACHINE_CLEAR, + P4_EVENT_INSTR_COMPLETED, +}; + +#define P4_OPCODE(event) event##_OPCODE +#define P4_OPCODE_ESEL(opcode) ((opcode & 0x00ff) >> 0) +#define P4_OPCODE_EVNT(opcode) ((opcode & 0xff00) >> 8) +#define P4_OPCODE_PACK(event, sel) (((event) << 8) | sel) + /* * Comments below the event represent ESCR restriction * for this event and counter index per ESCR @@ -238,484 +280,515 @@ static inline u32 p4_default_escr_conf(int cpu, int exclude_os, int exclude_usr) * working so that we should not use this CCCR and respective * counter as result */ -#define P4_TC_DELIVER_MODE P4_EVENT_PACK(0x01, 0x01) +enum P4_EVENT_OPCODES { + P4_OPCODE(P4_EVENT_TC_DELIVER_MODE) = P4_OPCODE_PACK(0x01, 0x01), /* * MSR_P4_TC_ESCR0: 4, 5 * MSR_P4_TC_ESCR1: 6, 7 */ -#define P4_BPU_FETCH_REQUEST P4_EVENT_PACK(0x03, 0x00) + P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST) = P4_OPCODE_PACK(0x03, 0x00), /* * MSR_P4_BPU_ESCR0: 0, 1 * MSR_P4_BPU_ESCR1: 2, 3 */ -#define P4_ITLB_REFERENCE P4_EVENT_PACK(0x18, 0x03) + P4_OPCODE(P4_EVENT_ITLB_REFERENCE) = P4_OPCODE_PACK(0x18, 0x03), /* * MSR_P4_ITLB_ESCR0: 0, 1 * MSR_P4_ITLB_ESCR1: 2, 3 */ -#define P4_MEMORY_CANCEL P4_EVENT_PACK(0x02, 0x05) + P4_OPCODE(P4_EVENT_MEMORY_CANCEL) = P4_OPCODE_PACK(0x02, 0x05), /* * MSR_P4_DAC_ESCR0: 8, 9 * MSR_P4_DAC_ESCR1: 10, 11 */ -#define P4_MEMORY_COMPLETE P4_EVENT_PACK(0x08, 0x02) + P4_OPCODE(P4_EVENT_MEMORY_COMPLETE) = P4_OPCODE_PACK(0x08, 0x02), /* * MSR_P4_SAAT_ESCR0: 8, 9 * MSR_P4_SAAT_ESCR1: 10, 11 */ -#define P4_LOAD_PORT_REPLAY P4_EVENT_PACK(0x04, 0x02) + P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY) = P4_OPCODE_PACK(0x04, 0x02), /* * MSR_P4_SAAT_ESCR0: 8, 9 * MSR_P4_SAAT_ESCR1: 10, 11 */ -#define P4_STORE_PORT_REPLAY P4_EVENT_PACK(0x05, 0x02) + P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY) = P4_OPCODE_PACK(0x05, 0x02), /* * MSR_P4_SAAT_ESCR0: 8, 9 * MSR_P4_SAAT_ESCR1: 10, 11 */ -#define P4_MOB_LOAD_REPLAY P4_EVENT_PACK(0x03, 0x02) + P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY) = P4_OPCODE_PACK(0x03, 0x02), /* * MSR_P4_MOB_ESCR0: 0, 1 * MSR_P4_MOB_ESCR1: 2, 3 */ -#define P4_PAGE_WALK_TYPE P4_EVENT_PACK(0x01, 0x04) + P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE) = P4_OPCODE_PACK(0x01, 0x04), /* * MSR_P4_PMH_ESCR0: 0, 1 * MSR_P4_PMH_ESCR1: 2, 3 */ -#define P4_BSQ_CACHE_REFERENCE P4_EVENT_PACK(0x0c, 0x07) + P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE) = P4_OPCODE_PACK(0x0c, 0x07), /* * MSR_P4_BSU_ESCR0: 0, 1 * MSR_P4_BSU_ESCR1: 2, 3 */ -#define P4_IOQ_ALLOCATION P4_EVENT_PACK(0x03, 0x06) + P4_OPCODE(P4_EVENT_IOQ_ALLOCATION) = P4_OPCODE_PACK(0x03, 0x06), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_IOQ_ACTIVE_ENTRIES P4_EVENT_PACK(0x1a, 0x06) + P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x1a, 0x06), /* * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_FSB_DATA_ACTIVITY P4_EVENT_PACK(0x17, 0x06) + P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY) = P4_OPCODE_PACK(0x17, 0x06), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_BSQ_ALLOCATION P4_EVENT_PACK(0x05, 0x07) + P4_OPCODE(P4_EVENT_BSQ_ALLOCATION) = P4_OPCODE_PACK(0x05, 0x07), /* * MSR_P4_BSU_ESCR0: 0, 1 */ -#define P4_BSQ_ACTIVE_ENTRIES P4_EVENT_PACK(0x06, 0x07) + P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES) = P4_OPCODE_PACK(0x06, 0x07), /* * NOTE: no ESCR name in docs, it's guessed * MSR_P4_BSU_ESCR1: 2, 3 */ -#define P4_SSE_INPUT_ASSIST P4_EVENT_PACK(0x34, 0x01) + P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST) = P4_OPCODE_PACK(0x34, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_PACKED_SP_UOP P4_EVENT_PACK(0x08, 0x01) + P4_OPCODE(P4_EVENT_PACKED_SP_UOP) = P4_OPCODE_PACK(0x08, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_PACKED_DP_UOP P4_EVENT_PACK(0x0c, 0x01) + P4_OPCODE(P4_EVENT_PACKED_DP_UOP) = P4_OPCODE_PACK(0x0c, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_SCALAR_SP_UOP P4_EVENT_PACK(0x0a, 0x01) + P4_OPCODE(P4_EVENT_SCALAR_SP_UOP) = P4_OPCODE_PACK(0x0a, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_SCALAR_DP_UOP P4_EVENT_PACK(0x0e, 0x01) + P4_OPCODE(P4_EVENT_SCALAR_DP_UOP) = P4_OPCODE_PACK(0x0e, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_64BIT_MMX_UOP P4_EVENT_PACK(0x02, 0x01) + P4_OPCODE(P4_EVENT_64BIT_MMX_UOP) = P4_OPCODE_PACK(0x02, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_128BIT_MMX_UOP P4_EVENT_PACK(0x1a, 0x01) + P4_OPCODE(P4_EVENT_128BIT_MMX_UOP) = P4_OPCODE_PACK(0x1a, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_X87_FP_UOP P4_EVENT_PACK(0x04, 0x01) + P4_OPCODE(P4_EVENT_X87_FP_UOP) = P4_OPCODE_PACK(0x04, 0x01), /* * MSR_P4_FIRM_ESCR0: 8, 9 * MSR_P4_FIRM_ESCR1: 10, 11 */ -#define P4_TC_MISC P4_EVENT_PACK(0x06, 0x01) + P4_OPCODE(P4_EVENT_TC_MISC) = P4_OPCODE_PACK(0x06, 0x01), /* * MSR_P4_TC_ESCR0: 4, 5 * MSR_P4_TC_ESCR1: 6, 7 */ -#define P4_GLOBAL_POWER_EVENTS P4_EVENT_PACK(0x13, 0x06) + P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS) = P4_OPCODE_PACK(0x13, 0x06), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_TC_MS_XFER P4_EVENT_PACK(0x05, 0x00) + P4_OPCODE(P4_EVENT_TC_MS_XFER) = P4_OPCODE_PACK(0x05, 0x00), /* * MSR_P4_MS_ESCR0: 4, 5 * MSR_P4_MS_ESCR1: 6, 7 */ -#define P4_UOP_QUEUE_WRITES P4_EVENT_PACK(0x09, 0x00) + P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES) = P4_OPCODE_PACK(0x09, 0x00), /* * MSR_P4_MS_ESCR0: 4, 5 * MSR_P4_MS_ESCR1: 6, 7 */ -#define P4_RETIRED_MISPRED_BRANCH_TYPE P4_EVENT_PACK(0x05, 0x02) + P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x05, 0x02), /* * MSR_P4_TBPU_ESCR0: 4, 5 * MSR_P4_TBPU_ESCR1: 6, 7 */ -#define P4_RETIRED_BRANCH_TYPE P4_EVENT_PACK(0x04, 0x02) + P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE) = P4_OPCODE_PACK(0x04, 0x02), /* * MSR_P4_TBPU_ESCR0: 4, 5 * MSR_P4_TBPU_ESCR1: 6, 7 */ -#define P4_RESOURCE_STALL P4_EVENT_PACK(0x01, 0x01) + P4_OPCODE(P4_EVENT_RESOURCE_STALL) = P4_OPCODE_PACK(0x01, 0x01), /* * MSR_P4_ALF_ESCR0: 12, 13, 16 * MSR_P4_ALF_ESCR1: 14, 15, 17 */ -#define P4_WC_BUFFER P4_EVENT_PACK(0x05, 0x05) + P4_OPCODE(P4_EVENT_WC_BUFFER) = P4_OPCODE_PACK(0x05, 0x05), /* * MSR_P4_DAC_ESCR0: 8, 9 * MSR_P4_DAC_ESCR1: 10, 11 */ -#define P4_B2B_CYCLES P4_EVENT_PACK(0x16, 0x03) + P4_OPCODE(P4_EVENT_B2B_CYCLES) = P4_OPCODE_PACK(0x16, 0x03), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_BNR P4_EVENT_PACK(0x08, 0x03) + P4_OPCODE(P4_EVENT_BNR) = P4_OPCODE_PACK(0x08, 0x03), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_SNOOP P4_EVENT_PACK(0x06, 0x03) + P4_OPCODE(P4_EVENT_SNOOP) = P4_OPCODE_PACK(0x06, 0x03), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_RESPONSE P4_EVENT_PACK(0x04, 0x03) + P4_OPCODE(P4_EVENT_RESPONSE) = P4_OPCODE_PACK(0x04, 0x03), /* * MSR_P4_FSB_ESCR0: 0, 1 * MSR_P4_FSB_ESCR1: 2, 3 */ -#define P4_FRONT_END_EVENT P4_EVENT_PACK(0x08, 0x05) + P4_OPCODE(P4_EVENT_FRONT_END_EVENT) = P4_OPCODE_PACK(0x08, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_EXECUTION_EVENT P4_EVENT_PACK(0x0c, 0x05) + P4_OPCODE(P4_EVENT_EXECUTION_EVENT) = P4_OPCODE_PACK(0x0c, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_REPLAY_EVENT P4_EVENT_PACK(0x09, 0x05) + P4_OPCODE(P4_EVENT_REPLAY_EVENT) = P4_OPCODE_PACK(0x09, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_INSTR_RETIRED P4_EVENT_PACK(0x02, 0x04) + P4_OPCODE(P4_EVENT_INSTR_RETIRED) = P4_OPCODE_PACK(0x02, 0x04), /* * MSR_P4_CRU_ESCR0: 12, 13, 16 * MSR_P4_CRU_ESCR1: 14, 15, 17 */ -#define P4_UOPS_RETIRED P4_EVENT_PACK(0x01, 0x04) + P4_OPCODE(P4_EVENT_UOPS_RETIRED) = P4_OPCODE_PACK(0x01, 0x04), /* * MSR_P4_CRU_ESCR0: 12, 13, 16 * MSR_P4_CRU_ESCR1: 14, 15, 17 */ -#define P4_UOP_TYPE P4_EVENT_PACK(0x02, 0x02) + P4_OPCODE(P4_EVENT_UOP_TYPE) = P4_OPCODE_PACK(0x02, 0x02), /* * MSR_P4_RAT_ESCR0: 12, 13, 16 * MSR_P4_RAT_ESCR1: 14, 15, 17 */ -#define P4_BRANCH_RETIRED P4_EVENT_PACK(0x06, 0x05) + P4_OPCODE(P4_EVENT_BRANCH_RETIRED) = P4_OPCODE_PACK(0x06, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_MISPRED_BRANCH_RETIRED P4_EVENT_PACK(0x03, 0x04) + P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED) = P4_OPCODE_PACK(0x03, 0x04), /* * MSR_P4_CRU_ESCR0: 12, 13, 16 * MSR_P4_CRU_ESCR1: 14, 15, 17 */ -#define P4_X87_ASSIST P4_EVENT_PACK(0x03, 0x05) + P4_OPCODE(P4_EVENT_X87_ASSIST) = P4_OPCODE_PACK(0x03, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_MACHINE_CLEAR P4_EVENT_PACK(0x02, 0x05) + P4_OPCODE(P4_EVENT_MACHINE_CLEAR) = P4_OPCODE_PACK(0x02, 0x05), /* * MSR_P4_CRU_ESCR2: 12, 13, 16 * MSR_P4_CRU_ESCR3: 14, 15, 17 */ -#define P4_INSTR_COMPLETED P4_EVENT_PACK(0x07, 0x04) + P4_OPCODE(P4_EVENT_INSTR_COMPLETED) = P4_OPCODE_PACK(0x07, 0x04), /* * MSR_P4_CRU_ESCR0: 12, 13, 16 * MSR_P4_CRU_ESCR1: 14, 15, 17 */ +}; /* - * a caller should use P4_EVENT_ATTR helper to - * pick the attribute needed, for example + * a caller should use P4_ESCR_EMASK_NAME helper to + * pick the EventMask needed, for example * - * P4_EVENT_ATTR(P4_TC_DELIVER_MODE, DD) + * P4_ESCR_EMASK_NAME(P4_EVENT_TC_DELIVER_MODE, DD) */ -enum P4_EVENTS_ATTR { - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DD, 0), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DB, 1), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, DI, 2), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BD, 3), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BB, 4), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, BI, 5), - P4_MAKE_EVENT_ATTR(P4_TC_DELIVER_MODE, ID, 6), - - P4_MAKE_EVENT_ATTR(P4_BPU_FETCH_REQUEST, TCMISS, 0), - - P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, HIT, 0), - P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, MISS, 1), - P4_MAKE_EVENT_ATTR(P4_ITLB_REFERENCE, HIT_UK, 2), - - P4_MAKE_EVENT_ATTR(P4_MEMORY_CANCEL, ST_RB_FULL, 2), - P4_MAKE_EVENT_ATTR(P4_MEMORY_CANCEL, 64K_CONF, 3), - - P4_MAKE_EVENT_ATTR(P4_MEMORY_COMPLETE, LSC, 0), - P4_MAKE_EVENT_ATTR(P4_MEMORY_COMPLETE, SSC, 1), - - P4_MAKE_EVENT_ATTR(P4_LOAD_PORT_REPLAY, SPLIT_LD, 1), - - P4_MAKE_EVENT_ATTR(P4_STORE_PORT_REPLAY, SPLIT_ST, 1), - - P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, NO_STA, 1), - P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, NO_STD, 3), - P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, PARTIAL_DATA, 4), - P4_MAKE_EVENT_ATTR(P4_MOB_LOAD_REPLAY, UNALGN_ADDR, 5), - - P4_MAKE_EVENT_ATTR(P4_PAGE_WALK_TYPE, DTMISS, 0), - P4_MAKE_EVENT_ATTR(P4_PAGE_WALK_TYPE, ITMISS, 1), - - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITS, 0), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITE, 1), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITM, 2), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITS, 3), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITE, 4), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITM, 5), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_MISS, 8), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_MISS, 9), - P4_MAKE_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, WR_2ndL_MISS, 10), - - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, DEFAULT, 0), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, ALL_READ, 5), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, ALL_WRITE, 6), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_UC, 7), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WC, 8), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WT, 9), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WP, 10), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, MEM_WB, 11), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, OWN, 13), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, OTHER, 14), - P4_MAKE_EVENT_ATTR(P4_IOQ_ALLOCATION, PREFETCH, 15), - - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, DEFAULT, 0), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, ALL_READ, 5), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, ALL_WRITE, 6), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_UC, 7), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WC, 8), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WT, 9), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WP, 10), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, MEM_WB, 11), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, OWN, 13), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, OTHER, 14), - P4_MAKE_EVENT_ATTR(P4_IOQ_ACTIVE_ENTRIES, PREFETCH, 15), - - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_DRV, 0), - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OWN, 1), - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OTHER, 2), - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_DRV, 3), - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_OWN, 4), - P4_MAKE_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DBSY_OTHER, 5), - - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_TYPE0, 0), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_TYPE1, 1), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LEN0, 2), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LEN1, 3), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_IO_TYPE, 5), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_LOCK_TYPE, 6), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_CACHE_TYPE, 7), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_SPLIT_TYPE, 8), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_DEM_TYPE, 9), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, REQ_ORD_TYPE, 10), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE0, 11), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE1, 12), - P4_MAKE_EVENT_ATTR(P4_BSQ_ALLOCATION, MEM_TYPE2, 13), - - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_TYPE0, 0), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_TYPE1, 1), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LEN0, 2), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LEN1, 3), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_IO_TYPE, 5), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_LOCK_TYPE, 6), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_CACHE_TYPE, 7), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_SPLIT_TYPE, 8), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_DEM_TYPE, 9), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, REQ_ORD_TYPE, 10), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE0, 11), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE1, 12), - P4_MAKE_EVENT_ATTR(P4_BSQ_ACTIVE_ENTRIES, MEM_TYPE2, 13), - - P4_MAKE_EVENT_ATTR(P4_SSE_INPUT_ASSIST, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_PACKED_SP_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_PACKED_DP_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_SCALAR_SP_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_SCALAR_DP_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_64BIT_MMX_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_128BIT_MMX_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_X87_FP_UOP, ALL, 15), - - P4_MAKE_EVENT_ATTR(P4_TC_MISC, FLUSH, 4), - - P4_MAKE_EVENT_ATTR(P4_GLOBAL_POWER_EVENTS, RUNNING, 0), - - P4_MAKE_EVENT_ATTR(P4_TC_MS_XFER, CISC, 0), - - P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_TC_BUILD, 0), - P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_TC_DELIVER, 1), - P4_MAKE_EVENT_ATTR(P4_UOP_QUEUE_WRITES, FROM_ROM, 2), - - P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, CONDITIONAL, 1), - P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, CALL, 2), - P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, RETURN, 3), - P4_MAKE_EVENT_ATTR(P4_RETIRED_MISPRED_BRANCH_TYPE, INDIRECT, 4), - - P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CONDITIONAL, 1), - P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CALL, 2), - P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, RETURN, 3), - P4_MAKE_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, INDIRECT, 4), - - P4_MAKE_EVENT_ATTR(P4_RESOURCE_STALL, SBFULL, 5), - - P4_MAKE_EVENT_ATTR(P4_WC_BUFFER, WCB_EVICTS, 0), - P4_MAKE_EVENT_ATTR(P4_WC_BUFFER, WCB_FULL_EVICTS, 1), - - P4_MAKE_EVENT_ATTR(P4_FRONT_END_EVENT, NBOGUS, 0), - P4_MAKE_EVENT_ATTR(P4_FRONT_END_EVENT, BOGUS, 1), - - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS0, 0), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS1, 1), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS2, 2), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, NBOGUS3, 3), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS0, 4), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS1, 5), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS2, 6), - P4_MAKE_EVENT_ATTR(P4_EXECUTION_EVENT, BOGUS3, 7), - - P4_MAKE_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS, 0), - P4_MAKE_EVENT_ATTR(P4_REPLAY_EVENT, BOGUS, 1), - - P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG, 0), - P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSTAG, 1), - P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG, 2), - P4_MAKE_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSTAG, 3), - - P4_MAKE_EVENT_ATTR(P4_UOPS_RETIRED, NBOGUS, 0), - P4_MAKE_EVENT_ATTR(P4_UOPS_RETIRED, BOGUS, 1), +enum P4_ESCR_EMASKS { + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DD, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DB, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, DI, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BD, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BB, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, BI, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_TC_DELIVER_MODE, ID, 6), + + P4_GEN_ESCR_EMASK(P4_EVENT_BPU_FETCH_REQUEST, TCMISS, 0), + + P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, MISS, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_ITLB_REFERENCE, HIT_UK, 2), + + P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, ST_RB_FULL, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_CANCEL, 64K_CONF, 3), + + P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, LSC, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_MEMORY_COMPLETE, SSC, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_LOAD_PORT_REPLAY, SPLIT_LD, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_STORE_PORT_REPLAY, SPLIT_ST, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STA, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, NO_STD, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, PARTIAL_DATA, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_MOB_LOAD_REPLAY, UNALGN_ADDR, 5), + + P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, DTMISS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_PAGE_WALK_TYPE, ITMISS, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS, 8), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS, 9), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS, 10), + + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, DEFAULT, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_READ, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, ALL_WRITE, 6), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_UC, 7), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WC, 8), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WT, 9), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WP, 10), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, MEM_WB, 11), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OWN, 13), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, OTHER, 14), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ALLOCATION, PREFETCH, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, DEFAULT, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_READ, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, ALL_WRITE, 6), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_UC, 7), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WC, 8), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WT, 9), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WP, 10), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, MEM_WB, 11), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OWN, 13), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, OTHER, 14), + P4_GEN_ESCR_EMASK(P4_EVENT_IOQ_ACTIVE_ENTRIES, PREFETCH, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OTHER, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_DRV, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OWN, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_FSB_DATA_ACTIVITY, DBSY_OTHER, 5), + + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE0, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_TYPE1, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN0, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LEN1, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_IO_TYPE, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_LOCK_TYPE, 6), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_CACHE_TYPE, 7), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_SPLIT_TYPE, 8), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_DEM_TYPE, 9), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, REQ_ORD_TYPE, 10), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE0, 11), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE1, 12), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ALLOCATION, MEM_TYPE2, 13), + + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE0, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_TYPE1, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN0, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LEN1, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_IO_TYPE, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_LOCK_TYPE, 6), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_CACHE_TYPE, 7), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_SPLIT_TYPE, 8), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_DEM_TYPE, 9), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, REQ_ORD_TYPE, 10), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE0, 11), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE1, 12), + P4_GEN_ESCR_EMASK(P4_EVENT_BSQ_ACTIVE_ENTRIES, MEM_TYPE2, 13), + + P4_GEN_ESCR_EMASK(P4_EVENT_SSE_INPUT_ASSIST, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_SP_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_PACKED_DP_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_SP_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_SCALAR_DP_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_64BIT_MMX_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_128BIT_MMX_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_X87_FP_UOP, ALL, 15), + + P4_GEN_ESCR_EMASK(P4_EVENT_TC_MISC, FLUSH, 4), + + P4_GEN_ESCR_EMASK(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING, 0), + + P4_GEN_ESCR_EMASK(P4_EVENT_TC_MS_XFER, CISC, 0), + + P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_BUILD, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_TC_DELIVER, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_UOP_QUEUE_WRITES, FROM_ROM, 2), + + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CONDITIONAL, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, CALL, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, RETURN, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE, INDIRECT, 4), + + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, CALL, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT, 4), + + P4_GEN_ESCR_EMASK(P4_EVENT_RESOURCE_STALL, SBFULL, 5), + + P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_EVICTS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_WC_BUFFER, WCB_FULL_EVICTS, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, NBOGUS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_FRONT_END_EVENT, BOGUS, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS0, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS1, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS2, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, NBOGUS3, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS0, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS1, 5), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS2, 6), + P4_GEN_ESCR_EMASK(P4_EVENT_EXECUTION_EVENT, BOGUS3, 7), + + P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, NBOGUS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_REPLAY_EVENT, BOGUS, 1), + + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, NBOGUSTAG, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSNTAG, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_RETIRED, BOGUSTAG, 3), + + P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, NBOGUS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_UOPS_RETIRED, BOGUS, 1), - P4_MAKE_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS, 1), - P4_MAKE_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES, 2), - - P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMNP, 0), - P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMNM, 1), - P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMTP, 2), - P4_MAKE_EVENT_ATTR(P4_BRANCH_RETIRED, MMTM, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGLOADS, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_UOP_TYPE, TAGSTORES, 2), + + P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNP, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMNM, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTP, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_BRANCH_RETIRED, MMTM, 3), - P4_MAKE_EVENT_ATTR(P4_MISPRED_BRANCH_RETIRED, NBOGUS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS, 0), - P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, FPSU, 0), - P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, FPSO, 1), - P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, POAO, 2), - P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, POAU, 3), - P4_MAKE_EVENT_ATTR(P4_X87_ASSIST, PREA, 4), + P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSU, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, FPSO, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAO, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, POAU, 3), + P4_GEN_ESCR_EMASK(P4_EVENT_X87_ASSIST, PREA, 4), - P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, CLEAR, 0), - P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, MOCLEAR, 1), - P4_MAKE_EVENT_ATTR(P4_MACHINE_CLEAR, SMCLEAR, 2), + P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, CLEAR, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, MOCLEAR, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_MACHINE_CLEAR, SMCLEAR, 2), - P4_MAKE_EVENT_ATTR(P4_INSTR_COMPLETED, NBOGUS, 0), - P4_MAKE_EVENT_ATTR(P4_INSTR_COMPLETED, BOGUS, 1), + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, NBOGUS, 0), + P4_GEN_ESCR_EMASK(P4_EVENT_INSTR_COMPLETED, BOGUS, 1), }; -enum { - KEY_P4_L1D_OP_READ_RESULT_MISS = PERF_COUNT_HW_MAX, - KEY_P4_LL_OP_READ_RESULT_MISS, - KEY_P4_DTLB_OP_READ_RESULT_MISS, - KEY_P4_DTLB_OP_WRITE_RESULT_MISS, - KEY_P4_ITLB_OP_READ_RESULT_ACCESS, - KEY_P4_ITLB_OP_READ_RESULT_MISS, - KEY_P4_UOP_TYPE, +/* P4 PEBS: stale for a while */ +#define P4_PEBS_METRIC_MASK 0x00001fffU +#define P4_PEBS_UOB_TAG 0x01000000U +#define P4_PEBS_ENABLE 0x02000000U + +/* Replay metrics for MSR_IA32_PEBS_ENABLE and MSR_P4_PEBS_MATRIX_VERT */ +#define P4_PEBS__1stl_cache_load_miss_retired 0x3000001 +#define P4_PEBS__2ndl_cache_load_miss_retired 0x3000002 +#define P4_PEBS__dtlb_load_miss_retired 0x3000004 +#define P4_PEBS__dtlb_store_miss_retired 0x3000004 +#define P4_PEBS__dtlb_all_miss_retired 0x3000004 +#define P4_PEBS__tagged_mispred_branch 0x3018000 +#define P4_PEBS__mob_load_replay_retired 0x3000200 +#define P4_PEBS__split_load_retired 0x3000400 +#define P4_PEBS__split_store_retired 0x3000400 + +#define P4_VERT__1stl_cache_load_miss_retired 0x0000001 +#define P4_VERT__2ndl_cache_load_miss_retired 0x0000001 +#define P4_VERT__dtlb_load_miss_retired 0x0000001 +#define P4_VERT__dtlb_store_miss_retired 0x0000002 +#define P4_VERT__dtlb_all_miss_retired 0x0000003 +#define P4_VERT__tagged_mispred_branch 0x0000010 +#define P4_VERT__mob_load_replay_retired 0x0000001 +#define P4_VERT__split_load_retired 0x0000001 +#define P4_VERT__split_store_retired 0x0000002 + +enum P4_CACHE_EVENTS { + P4_CACHE__NONE, + + P4_CACHE__1stl_cache_load_miss_retired, + P4_CACHE__2ndl_cache_load_miss_retired, + P4_CACHE__dtlb_load_miss_retired, + P4_CACHE__dtlb_store_miss_retired, + P4_CACHE__itlb_reference_hit, + P4_CACHE__itlb_reference_miss, + + P4_CACHE__MAX }; #endif /* PERF_EVENT_P4_H */ diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index b8a811a..f8fe069 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -11,35 +11,281 @@ #include +#define P4_CNTR_LIMIT 3 /* * array indices: 0,1 - HT threads, used with HT enabled cpu */ -struct p4_event_template { - u32 opcode; /* ESCR event + CCCR selector */ - u64 config; /* packed predefined bits */ - int dep; /* upstream dependency event index */ - int key; /* index into p4_templates */ - u64 msr; /* - * the high 32 bits set into MSR_IA32_PEBS_ENABLE and - * the low 32 bits set into MSR_P4_PEBS_MATRIX_VERT - * for cache events - */ - unsigned int emask; /* ESCR EventMask */ - unsigned int escr_msr[2]; /* ESCR MSR for this event */ - unsigned int cntr[2]; /* counter index (offset) */ +struct p4_event_bind { + unsigned int opcode; /* Event code and ESCR selector */ + unsigned int escr_msr[2]; /* ESCR MSR for this event */ + unsigned char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on abscence */ }; -struct p4_pmu_res { - /* maps hw_conf::idx into template for ESCR sake */ - struct p4_event_template *tpl[ARCH_P4_MAX_CCCR]; +struct p4_cache_event_bind { + unsigned int metric_pebs; + unsigned int metric_vert; }; -static DEFINE_PER_CPU(struct p4_pmu_res, p4_pmu_config); +#define P4_GEN_CACHE_EVENT_BIND(name) \ + [P4_CACHE__##name] = { \ + .metric_pebs = P4_PEBS__##name, \ + .metric_vert = P4_VERT__##name, \ + } + +static struct p4_cache_event_bind p4_cache_event_bind_map[] = { + P4_GEN_CACHE_EVENT_BIND(1stl_cache_load_miss_retired), + P4_GEN_CACHE_EVENT_BIND(2ndl_cache_load_miss_retired), + P4_GEN_CACHE_EVENT_BIND(dtlb_load_miss_retired), + P4_GEN_CACHE_EVENT_BIND(dtlb_store_miss_retired), +}; + +/* + * Note that we don't use CCCR1 here, there is an + * exception for P4_BSQ_ALLOCATION but we just have + * no workaround + * + * consider this binding as resources which particular + * event may borrow, it doesn't contain EventMask, + * Tags and friends -- they are left to a caller + */ +static struct p4_event_bind p4_event_bind_map[] = { + [P4_EVENT_TC_DELIVER_MODE] = { + .opcode = P4_OPCODE(P4_EVENT_TC_DELIVER_MODE), + .escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_BPU_FETCH_REQUEST] = { + .opcode = P4_OPCODE(P4_EVENT_BPU_FETCH_REQUEST), + .escr_msr = { MSR_P4_BPU_ESCR0, MSR_P4_BPU_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_ITLB_REFERENCE] = { + .opcode = P4_OPCODE(P4_EVENT_ITLB_REFERENCE), + .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_MEMORY_CANCEL] = { + .opcode = P4_OPCODE(P4_EVENT_MEMORY_CANCEL), + .escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_MEMORY_COMPLETE] = { + .opcode = P4_OPCODE(P4_EVENT_MEMORY_COMPLETE), + .escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_LOAD_PORT_REPLAY] = { + .opcode = P4_OPCODE(P4_EVENT_LOAD_PORT_REPLAY), + .escr_msr = { MSR_P4_SAAT_ESCR0, MSR_P4_SAAT_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_STORE_PORT_REPLAY] = { + .opcode = P4_OPCODE(P4_EVENT_STORE_PORT_REPLAY), + .escr_msr = { MSR_P4_SAAT_ESCR0 , MSR_P4_SAAT_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_MOB_LOAD_REPLAY] = { + .opcode = P4_OPCODE(P4_EVENT_MOB_LOAD_REPLAY), + .escr_msr = { MSR_P4_MOB_ESCR0, MSR_P4_MOB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_PAGE_WALK_TYPE] = { + .opcode = P4_OPCODE(P4_EVENT_PAGE_WALK_TYPE), + .escr_msr = { MSR_P4_PMH_ESCR0, MSR_P4_PMH_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_BSQ_CACHE_REFERENCE] = { + .opcode = P4_OPCODE(P4_EVENT_BSQ_CACHE_REFERENCE), + .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_IOQ_ALLOCATION] = { + .opcode = P4_OPCODE(P4_EVENT_IOQ_ALLOCATION), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_IOQ_ACTIVE_ENTRIES] = { /* shared ESCR */ + .opcode = P4_OPCODE(P4_EVENT_IOQ_ACTIVE_ENTRIES), + .escr_msr = { MSR_P4_FSB_ESCR1, MSR_P4_FSB_ESCR1 }, + .cntr = { {2, -1, -1}, {3, -1, -1} }, + }, + [P4_EVENT_FSB_DATA_ACTIVITY] = { + .opcode = P4_OPCODE(P4_EVENT_FSB_DATA_ACTIVITY), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_BSQ_ALLOCATION] = { /* shared ESCR, broken CCCR1 */ + .opcode = P4_OPCODE(P4_EVENT_BSQ_ALLOCATION), + .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR0 }, + .cntr = { {0, -1, -1}, {1, -1, -1} }, + }, + [P4_EVENT_BSQ_ACTIVE_ENTRIES] = { /* shared ESCR */ + .opcode = P4_OPCODE(P4_EVENT_BSQ_ACTIVE_ENTRIES), + .escr_msr = { MSR_P4_BSU_ESCR1 , MSR_P4_BSU_ESCR1 }, + .cntr = { {2, -1, -1}, {3, -1, -1} }, + }, + [P4_EVENT_SSE_INPUT_ASSIST] = { + .opcode = P4_OPCODE(P4_EVENT_SSE_INPUT_ASSIST), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_PACKED_SP_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_PACKED_SP_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_PACKED_DP_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_PACKED_DP_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_SCALAR_SP_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_SCALAR_SP_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_SCALAR_DP_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_SCALAR_DP_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_64BIT_MMX_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_64BIT_MMX_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_128BIT_MMX_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_128BIT_MMX_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_X87_FP_UOP] = { + .opcode = P4_OPCODE(P4_EVENT_X87_FP_UOP), + .escr_msr = { MSR_P4_FIRM_ESCR0, MSR_P4_FIRM_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_TC_MISC] = { + .opcode = P4_OPCODE(P4_EVENT_TC_MISC), + .escr_msr = { MSR_P4_TC_ESCR0, MSR_P4_TC_ESCR1 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_GLOBAL_POWER_EVENTS] = { + .opcode = P4_OPCODE(P4_EVENT_GLOBAL_POWER_EVENTS), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_TC_MS_XFER] = { + .opcode = P4_OPCODE(P4_EVENT_TC_MS_XFER), + .escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_UOP_QUEUE_WRITES] = { + .opcode = P4_OPCODE(P4_EVENT_UOP_QUEUE_WRITES), + .escr_msr = { MSR_P4_MS_ESCR0, MSR_P4_MS_ESCR1 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE] = { + .opcode = P4_OPCODE(P4_EVENT_RETIRED_MISPRED_BRANCH_TYPE), + .escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR0 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_RETIRED_BRANCH_TYPE] = { + .opcode = P4_OPCODE(P4_EVENT_RETIRED_BRANCH_TYPE), + .escr_msr = { MSR_P4_TBPU_ESCR0 , MSR_P4_TBPU_ESCR1 }, + .cntr = { {4, 5, -1}, {6, 7, -1} }, + }, + [P4_EVENT_RESOURCE_STALL] = { + .opcode = P4_OPCODE(P4_EVENT_RESOURCE_STALL), + .escr_msr = { MSR_P4_ALF_ESCR0, MSR_P4_ALF_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_WC_BUFFER] = { + .opcode = P4_OPCODE(P4_EVENT_WC_BUFFER), + .escr_msr = { MSR_P4_DAC_ESCR0, MSR_P4_DAC_ESCR1 }, + .cntr = { {8, 9, -1}, {10, 11, -1} }, + }, + [P4_EVENT_B2B_CYCLES] = { + .opcode = P4_OPCODE(P4_EVENT_B2B_CYCLES), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_BNR] = { + .opcode = P4_OPCODE(P4_EVENT_BNR), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_SNOOP] = { + .opcode = P4_OPCODE(P4_EVENT_SNOOP), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_RESPONSE] = { + .opcode = P4_OPCODE(P4_EVENT_RESPONSE), + .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, + .cntr = { {0, -1, -1}, {2, -1, -1} }, + }, + [P4_EVENT_FRONT_END_EVENT] = { + .opcode = P4_OPCODE(P4_EVENT_FRONT_END_EVENT), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_EXECUTION_EVENT] = { + .opcode = P4_OPCODE(P4_EVENT_EXECUTION_EVENT), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_REPLAY_EVENT] = { + .opcode = P4_OPCODE(P4_EVENT_REPLAY_EVENT), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_INSTR_RETIRED] = { + .opcode = P4_OPCODE(P4_EVENT_INSTR_RETIRED), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_UOPS_RETIRED] = { + .opcode = P4_OPCODE(P4_EVENT_UOPS_RETIRED), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_UOP_TYPE] = { + .opcode = P4_OPCODE(P4_EVENT_UOP_TYPE), + .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_BRANCH_RETIRED] = { + .opcode = P4_OPCODE(P4_EVENT_BRANCH_RETIRED), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_MISPRED_BRANCH_RETIRED] = { + .opcode = P4_OPCODE(P4_EVENT_MISPRED_BRANCH_RETIRED), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_X87_ASSIST] = { + .opcode = P4_OPCODE(P4_EVENT_X87_ASSIST), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_MACHINE_CLEAR] = { + .opcode = P4_OPCODE(P4_EVENT_MACHINE_CLEAR), + .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR3 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, + [P4_EVENT_INSTR_COMPLETED] = { + .opcode = P4_OPCODE(P4_EVENT_INSTR_COMPLETED), + .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, + .cntr = { {12, 13, 16}, {14, 15, 17} }, + }, +}; -#define P4_CACHE_EVENT_CONFIG(event, bit) \ - p4_config_pack_escr(P4_EVENT_UNPACK_EVENT(event) << P4_EVNTSEL_EVENT_SHIFT) | \ - p4_config_pack_escr((event##_##bit) << P4_EVNTSEL_EVENTMASK_SHIFT) | \ - p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(event) << P4_CCCR_ESCR_SELECT_SHIFT) +#define P4_GEN_CACHE_EVENT(event, bit, cache_event) \ + p4_config_pack_escr(P4_ESCR_EVENT(event) | \ + P4_ESCR_EMASK_BIT(event, bit)) | \ + p4_config_pack_cccr(cache_event | \ + P4_CCCR_ESEL(P4_OPCODE_ESEL(P4_OPCODE(event)))) static __initconst u64 p4_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] @@ -49,42 +295,35 @@ static __initconst u64 p4_hw_cache_event_ids [ C(L1D ) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = 0x0, - /* 1stL_cache_load_miss_retired */ - [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) - | KEY_P4_L1D_OP_READ_RESULT_MISS, + [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, + P4_CACHE__1stl_cache_load_miss_retired), }, }, [ C(LL ) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = 0x0, - /* 2ndL_cache_load_miss_retired */ - [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) - | KEY_P4_LL_OP_READ_RESULT_MISS, + [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, + P4_CACHE__2ndl_cache_load_miss_retired), }, - }, +}, [ C(DTLB) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = 0x0, - /* DTLB_load_miss_retired */ - [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) - | KEY_P4_DTLB_OP_READ_RESULT_MISS, + [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, + P4_CACHE__dtlb_load_miss_retired), }, [ C(OP_WRITE) ] = { [ C(RESULT_ACCESS) ] = 0x0, - /* DTLB_store_miss_retired */ - [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_REPLAY_EVENT, NBOGUS) - | KEY_P4_DTLB_OP_WRITE_RESULT_MISS, + [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_REPLAY_EVENT, NBOGUS, + P4_CACHE__dtlb_store_miss_retired), }, }, [ C(ITLB) ] = { [ C(OP_READ) ] = { - /* ITLB_reference.HIT */ - [ C(RESULT_ACCESS) ] = P4_CACHE_EVENT_CONFIG(P4_ITLB_REFERENCE, HIT) - | KEY_P4_ITLB_OP_READ_RESULT_ACCESS, - - /* ITLB_reference.MISS */ - [ C(RESULT_MISS) ] = P4_CACHE_EVENT_CONFIG(P4_ITLB_REFERENCE, MISS) - | KEY_P4_ITLB_OP_READ_RESULT_MISS, + [ C(RESULT_ACCESS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, HIT, + P4_CACHE__itlb_reference_hit), + [ C(RESULT_MISS) ] = P4_GEN_CACHE_EVENT(P4_EVENT_ITLB_REFERENCE, MISS, + P4_CACHE__itlb_reference_miss), }, [ C(OP_WRITE) ] = { [ C(RESULT_ACCESS) ] = -1, @@ -97,220 +336,90 @@ static __initconst u64 p4_hw_cache_event_ids }, }; -/* - * WARN: CCCR1 doesn't have a working enable bit so try to not - * use it if possible - * - * Also as only we start to support raw events we will need to - * append _all_ P4_EVENT_PACK'ed events here - */ -struct p4_event_template p4_templates[] = { - [0] = { - .opcode = P4_GLOBAL_POWER_EVENTS, - .config = 0, - .dep = -1, - .key = 0, - .emask = - P4_EVENT_ATTR(P4_GLOBAL_POWER_EVENTS, RUNNING), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { 0, 2 }, - }, - [1] = { - .opcode = P4_INSTR_RETIRED, - .config = 0, - .dep = -1, /* needs front-end tagging */ - .key = 1, - .emask = - P4_EVENT_ATTR(P4_INSTR_RETIRED, NBOGUSNTAG) | - P4_EVENT_ATTR(P4_INSTR_RETIRED, BOGUSNTAG), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { 12, 14 }, - }, - [2] = { - .opcode = P4_BSQ_CACHE_REFERENCE, - .config = 0, - .dep = -1, - .key = 2, - .emask = - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_HITM) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITS) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITE) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_HITM), - .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, - .cntr = { 0, 2 }, - }, - [3] = { - .opcode = P4_BSQ_CACHE_REFERENCE, - .config = 0, - .dep = -1, - .key = 3, - .emask = - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) | - P4_EVENT_ATTR(P4_BSQ_CACHE_REFERENCE, WR_2ndL_MISS), - .escr_msr = { MSR_P4_BSU_ESCR0, MSR_P4_BSU_ESCR1 }, - .cntr = { 0, 3 }, - }, - [4] = { - .opcode = P4_RETIRED_BRANCH_TYPE, - .config = 0, - .dep = -1, - .key = 4, - .emask = - P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CONDITIONAL) | - P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, CALL) | - P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, RETURN) | - P4_EVENT_ATTR(P4_RETIRED_BRANCH_TYPE, INDIRECT), - .escr_msr = { MSR_P4_TBPU_ESCR0, MSR_P4_TBPU_ESCR1 }, - .cntr = { 4, 6 }, - }, - [5] = { - .opcode = P4_MISPRED_BRANCH_RETIRED, - .config = 0, - .dep = -1, - .key = 5, - .emask = - P4_EVENT_ATTR(P4_MISPRED_BRANCH_RETIRED, NBOGUS), - .escr_msr = { MSR_P4_CRU_ESCR0, MSR_P4_CRU_ESCR1 }, - .cntr = { 12, 14 }, - }, - [6] = { - .opcode = P4_FSB_DATA_ACTIVITY, - .config = p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE), - .dep = -1, - .key = 6, - .emask = - P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_DRV) | - P4_EVENT_ATTR(P4_FSB_DATA_ACTIVITY, DRDY_OWN), - .escr_msr = { MSR_P4_FSB_ESCR0, MSR_P4_FSB_ESCR1 }, - .cntr = { 0, 2 }, - }, - [KEY_P4_L1D_OP_READ_RESULT_MISS] = { - .opcode = P4_REPLAY_EVENT, - .config = 0, - .dep = -1, - .msr = (u64)(1 << 0 | 1 << 24) << 32 | (1 << 0), - .key = KEY_P4_L1D_OP_READ_RESULT_MISS, - .emask = - P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, - .cntr = { 16, 17 }, - }, - [KEY_P4_LL_OP_READ_RESULT_MISS] = { - .opcode = P4_REPLAY_EVENT, - .config = 0, - .dep = -1, - .msr = (u64)(1 << 1 | 1 << 24) << 32 | (1 << 0), - .key = KEY_P4_LL_OP_READ_RESULT_MISS, - .emask = - P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, - .cntr = { 16, 17 }, - }, - [KEY_P4_DTLB_OP_READ_RESULT_MISS] = { - .opcode = P4_REPLAY_EVENT, - .config = 0, - .dep = -1, - .msr = (u64)(1 << 2 | 1 << 24) << 32 | (1 << 0), - .key = KEY_P4_DTLB_OP_READ_RESULT_MISS, - .emask = - P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, - .cntr = { 16, 17 }, - }, - [KEY_P4_DTLB_OP_WRITE_RESULT_MISS] = { - .opcode = P4_REPLAY_EVENT, - .config = 0, - .dep = -1, - .msr = (u64)(1 << 2 | 1 << 24) << 32 | (1 << 1), - .key = KEY_P4_DTLB_OP_WRITE_RESULT_MISS, - .emask = - P4_EVENT_ATTR(P4_REPLAY_EVENT, NBOGUS), - .escr_msr = { MSR_P4_CRU_ESCR2, MSR_P4_CRU_ESCR2 }, - .cntr = { 16, 17 }, - }, - [KEY_P4_ITLB_OP_READ_RESULT_ACCESS] = { - .opcode = P4_ITLB_REFERENCE, - .config = 0, - .dep = -1, - .msr = 0, - .key = KEY_P4_ITLB_OP_READ_RESULT_ACCESS, - .emask = - P4_EVENT_ATTR(P4_ITLB_REFERENCE, HIT), - .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, - .cntr = { 0, 2 }, - }, - [KEY_P4_ITLB_OP_READ_RESULT_MISS] = { - .opcode = P4_ITLB_REFERENCE, - .config = 0, - .dep = -1, - .msr = 0, - .key = KEY_P4_ITLB_OP_READ_RESULT_MISS, - .emask = - P4_EVENT_ATTR(P4_ITLB_REFERENCE, MISS), - .escr_msr = { MSR_P4_ITLB_ESCR0, MSR_P4_ITLB_ESCR1 }, - .cntr = { 0, 2 }, - }, - [KEY_P4_UOP_TYPE] = { - .opcode = P4_UOP_TYPE, - .config = 0, - .dep = -1, - .key = KEY_P4_UOP_TYPE, - .emask = - P4_EVENT_ATTR(P4_UOP_TYPE, TAGLOADS) | - P4_EVENT_ATTR(P4_UOP_TYPE, TAGSTORES), - .escr_msr = { MSR_P4_RAT_ESCR0, MSR_P4_RAT_ESCR1 }, - .cntr = { 16, 17 }, - }, +static u64 p4_general_events[PERF_COUNT_HW_MAX] = { + /* non-halted CPU clocks */ + [PERF_COUNT_HW_CPU_CYCLES] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) | + P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)), + + /* + * retired instructions + * in a sake of simplicity we don't use the FSB tagging + */ + [PERF_COUNT_HW_INSTRUCTIONS] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_INSTR_RETIRED) | + P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, NBOGUSNTAG) | + P4_ESCR_EMASK_BIT(P4_EVENT_INSTR_RETIRED, BOGUSNTAG)), + + /* cache hits */ + [PERF_COUNT_HW_CACHE_REFERENCES] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITS) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITE) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_HITM) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITS) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITE) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_HITM)), + + /* cache misses */ + [PERF_COUNT_HW_CACHE_MISSES] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_BSQ_CACHE_REFERENCE) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_2ndL_MISS) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, RD_3rdL_MISS) | + P4_ESCR_EMASK_BIT(P4_EVENT_BSQ_CACHE_REFERENCE, WR_2ndL_MISS)), + + /* branch instructions retired */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_RETIRED_BRANCH_TYPE) | + P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CONDITIONAL) | + P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, CALL) | + P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, RETURN) | + P4_ESCR_EMASK_BIT(P4_EVENT_RETIRED_BRANCH_TYPE, INDIRECT)), + + /* mispredicted branches retired */ + [PERF_COUNT_HW_BRANCH_MISSES] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_MISPRED_BRANCH_RETIRED) | + P4_ESCR_EMASK_BIT(P4_EVENT_MISPRED_BRANCH_RETIRED, NBOGUS)), + + /* bus ready clocks (cpu is driving #DRDY_DRV\#DRDY_OWN): */ + [PERF_COUNT_HW_BUS_CYCLES] = + p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_FSB_DATA_ACTIVITY) | + P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_DRV) | + P4_ESCR_EMASK_BIT(P4_EVENT_FSB_DATA_ACTIVITY, DRDY_OWN)) | + p4_config_pack_cccr(P4_CCCR_EDGE | P4_CCCR_COMPARE), }; +static struct p4_event_bind *p4_config_get_bind(u64 config) +{ + unsigned int evnt = p4_config_unpack_event(config); + struct p4_event_bind *bind = NULL; + + if (evnt < ARRAY_SIZE(p4_event_bind_map)) + bind = &p4_event_bind_map[evnt]; + + return bind; +} + static u64 p4_pmu_event_map(int hw_event) { - struct p4_event_template *tpl; + struct p4_event_bind *bind; + unsigned int esel; u64 config; - if (hw_event > ARRAY_SIZE(p4_templates)) { - printk_once(KERN_ERR "PMU: Incorrect event index\n"); + if (hw_event > ARRAY_SIZE(p4_general_events)) { + printk_once(KERN_ERR "P4 PMU: Bad index: %i\n", hw_event); return 0; } - tpl = &p4_templates[hw_event]; - /* - * fill config up according to - * a predefined event template - */ - config = tpl->config; - config |= p4_config_pack_escr(P4_EVENT_UNPACK_EVENT(tpl->opcode) << P4_EVNTSEL_EVENT_SHIFT); - config |= p4_config_pack_escr(tpl->emask << P4_EVNTSEL_EVENTMASK_SHIFT); - config |= p4_config_pack_cccr(P4_EVENT_UNPACK_SELECTOR(tpl->opcode) << P4_CCCR_ESCR_SELECT_SHIFT); - config |= p4_config_pack_cccr(hw_event & P4_CCCR_RESERVED); + config = p4_general_events[hw_event]; + bind = p4_config_get_bind(config); + esel = P4_OPCODE_ESEL(bind->opcode); + config |= p4_config_pack_cccr(P4_CCCR_ESEL(esel)); return config; } /* - * Note that we still have 5 events (from global events SDM list) - * intersected in opcode+emask bits so we will need another - * scheme there do distinguish templates. - */ -static inline int p4_pmu_emask_match(unsigned int dst, unsigned int src) -{ - return dst & src; -} - -static struct p4_event_template *p4_pmu_template_lookup(u64 config) -{ - int key = p4_config_unpack_key(config); - - if (key < ARRAY_SIZE(p4_templates)) - return &p4_templates[key]; - else - return NULL; -} - -/* * We don't control raw events so it's up to the caller * to pass sane values (and we don't count the thread number * on HT machine but allow HT-compatible specifics to be @@ -319,13 +428,14 @@ static struct p4_event_template *p4_pmu_template_lookup(u64 config) static u64 p4_pmu_raw_event(u64 hw_event) { return hw_event & - (p4_config_pack_escr(P4_EVNTSEL_MASK_HT) | + (p4_config_pack_escr(P4_ESCR_MASK_HT) | p4_config_pack_cccr(P4_CCCR_MASK_HT)); } static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) { int cpu = raw_smp_processor_id(); + u32 escr, cccr; /* * the reason we use cpu that early is that: if we get scheduled @@ -333,13 +443,10 @@ static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) * specific flags in config (and will save some cpu cycles) */ - /* CCCR by default */ - hwc->config = p4_config_pack_cccr(p4_default_cccr_conf(cpu)); + cccr = p4_default_cccr_conf(cpu); + escr = p4_default_escr_conf(cpu, attr->exclude_kernel, attr->exclude_user); + hwc->config = p4_config_pack_escr(escr) | p4_config_pack_cccr(cccr); - /* Count user and OS events unless not requested to */ - hwc->config |= p4_config_pack_escr(p4_default_escr_conf(cpu, attr->exclude_kernel, - attr->exclude_user)); - /* on HT machine we need a special bit */ if (p4_ht_active() && p4_ht_thread(cpu)) hwc->config = p4_set_ht_bit(hwc->config); @@ -368,7 +475,7 @@ static inline void p4_pmu_disable_event(struct perf_event *event) */ (void)checking_wrmsrl(hwc->config_base + hwc->idx, (u64)(p4_config_unpack_cccr(hwc->config)) & - ~P4_CCCR_ENABLE & ~P4_CCCR_OVF); + ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED); } static void p4_pmu_disable_all(void) @@ -389,27 +496,14 @@ static void p4_pmu_enable_event(struct perf_event *event) struct hw_perf_event *hwc = &event->hw; int thread = p4_ht_config_thread(hwc->config); u64 escr_conf = p4_config_unpack_escr(p4_clear_ht_bit(hwc->config)); - u64 escr_base; - struct p4_event_template *tpl; - struct p4_pmu_res *c; + unsigned int idx = p4_config_unpack_event(hwc->config); + unsigned int idx_cache = p4_config_unpack_cache_event(hwc->config); + struct p4_event_bind *bind; + struct p4_cache_event_bind *bind_cache; + u64 escr_addr, cccr; - /* - * some preparation work from per-cpu private fields - * since we need to find out which ESCR to use - */ - c = &__get_cpu_var(p4_pmu_config); - tpl = c->tpl[hwc->idx]; - if (!tpl) { - pr_crit("%s: Wrong index: %d\n", __func__, hwc->idx); - return; - } - - if (tpl->msr) { - (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, tpl->msr >> 32); - (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, tpl->msr & 0xffffffff); - } - - escr_base = (u64)tpl->escr_msr[thread]; + bind = &p4_event_bind_map[idx]; + escr_addr = (u64)bind->escr_msr[thread]; /* * - we dont support cascaded counters yet @@ -418,9 +512,27 @@ static void p4_pmu_enable_event(struct perf_event *event) WARN_ON_ONCE(p4_is_event_cascaded(hwc->config)); WARN_ON_ONCE(hwc->idx == 1); - (void)checking_wrmsrl(escr_base, escr_conf); + /* we need a real Event value */ + escr_conf &= ~P4_ESCR_EVENT_MASK; + escr_conf |= P4_ESCR_EVENT(P4_OPCODE_EVNT(bind->opcode)); + + cccr = p4_config_unpack_cccr(hwc->config); + + /* + * it could be Cache event so that we need to + * set metrics into additional MSRs + */ + BUILD_BUG_ON(P4_CACHE__MAX > P4_CCCR_CACHE_OPS_MASK); + if (idx_cache > P4_CACHE__NONE && + idx_cache < ARRAY_SIZE(p4_cache_event_bind_map)) { + bind_cache = &p4_cache_event_bind_map[idx_cache]; + (void)checking_wrmsrl(MSR_IA32_PEBS_ENABLE, (u64)bind_cache->metric_pebs); + (void)checking_wrmsrl(MSR_P4_PEBS_MATRIX_VERT, (u64)bind_cache->metric_vert); + } + + (void)checking_wrmsrl(escr_addr, escr_conf); (void)checking_wrmsrl(hwc->config_base + hwc->idx, - (u64)(p4_config_unpack_cccr(hwc->config)) | P4_CCCR_ENABLE); + (cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE); } static void p4_pmu_enable_all(void) @@ -516,13 +628,13 @@ static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) if (p4_ht_thread(cpu)) { cccr &= ~P4_CCCR_OVF_PMI_T0; cccr |= P4_CCCR_OVF_PMI_T1; - if (escr & P4_EVNTSEL_T0_OS) { - escr &= ~P4_EVNTSEL_T0_OS; - escr |= P4_EVNTSEL_T1_OS; + if (escr & P4_ESCR_T0_OS) { + escr &= ~P4_ESCR_T0_OS; + escr |= P4_ESCR_T1_OS; } - if (escr & P4_EVNTSEL_T0_USR) { - escr &= ~P4_EVNTSEL_T0_USR; - escr |= P4_EVNTSEL_T1_USR; + if (escr & P4_ESCR_T0_USR) { + escr &= ~P4_ESCR_T0_USR; + escr |= P4_ESCR_T1_USR; } hwc->config = p4_config_pack_escr(escr); hwc->config |= p4_config_pack_cccr(cccr); @@ -530,13 +642,13 @@ static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) } else { cccr &= ~P4_CCCR_OVF_PMI_T1; cccr |= P4_CCCR_OVF_PMI_T0; - if (escr & P4_EVNTSEL_T1_OS) { - escr &= ~P4_EVNTSEL_T1_OS; - escr |= P4_EVNTSEL_T0_OS; + if (escr & P4_ESCR_T1_OS) { + escr &= ~P4_ESCR_T1_OS; + escr |= P4_ESCR_T0_OS; } - if (escr & P4_EVNTSEL_T1_USR) { - escr &= ~P4_EVNTSEL_T1_USR; - escr |= P4_EVNTSEL_T0_USR; + if (escr & P4_ESCR_T1_USR) { + escr &= ~P4_ESCR_T1_USR; + escr |= P4_ESCR_T0_USR; } hwc->config = p4_config_pack_escr(escr); hwc->config |= p4_config_pack_cccr(cccr); @@ -606,66 +718,56 @@ static int p4_get_escr_idx(unsigned int addr) return -1; } +static int p4_next_cntr(int thread, unsigned long *used_mask, + struct p4_event_bind *bind) +{ + int i = 0, j; + + for (i = 0; i < P4_CNTR_LIMIT; i++) { + j = bind->cntr[thread][i++]; + if (j == -1 || !test_bit(j, used_mask)) + return j; + } + + return -1; +} + static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) { unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; unsigned long escr_mask[BITS_TO_LONGS(ARCH_P4_TOTAL_ESCR)]; - - struct hw_perf_event *hwc; - struct p4_event_template *tpl; - struct p4_pmu_res *c; int cpu = raw_smp_processor_id(); - int escr_idx, thread, i, num; + struct hw_perf_event *hwc; + struct p4_event_bind *bind; + unsigned int i, thread, num; + int cntr_idx, escr_idx; bitmap_zero(used_mask, X86_PMC_IDX_MAX); bitmap_zero(escr_mask, ARCH_P4_TOTAL_ESCR); - c = &__get_cpu_var(p4_pmu_config); - /* - * Firstly find out which resource events are going - * to use, if ESCR+CCCR tuple is already borrowed - * then get out of here - */ for (i = 0, num = n; i < n; i++, num--) { + hwc = &cpuc->event_list[i]->hw; - tpl = p4_pmu_template_lookup(hwc->config); - if (!tpl) - goto done; thread = p4_ht_thread(cpu); - escr_idx = p4_get_escr_idx(tpl->escr_msr[thread]); - if (escr_idx == -1) - goto done; + bind = p4_config_get_bind(hwc->config); + escr_idx = p4_get_escr_idx(bind->escr_msr[thread]); - /* already allocated and remains on the same cpu */ if (hwc->idx != -1 && !p4_should_swap_ts(hwc->config, cpu)) { + cntr_idx = hwc->idx; if (assign) assign[i] = hwc->idx; - /* upstream dependent event */ - if (unlikely(tpl->dep != -1)) - printk_once(KERN_WARNING "PMU: Dep events are " - "not implemented yet\n"); goto reserve; } - /* it may be already borrowed */ - if (test_bit(tpl->cntr[thread], used_mask) || - test_bit(escr_idx, escr_mask)) + cntr_idx = p4_next_cntr(thread, used_mask, bind); + if (cntr_idx == -1 || test_bit(escr_idx, escr_mask)) goto done; - /* - * ESCR+CCCR+COUNTERs are available to use lets swap - * thread specific bits, push assigned bits - * back and save template into per-cpu - * area (which will allow us to find out the ESCR - * to be used at moment of "enable event via real MSR") - */ p4_pmu_swap_config_ts(hwc, cpu); - if (assign) { - assign[i] = tpl->cntr[thread]; - c->tpl[assign[i]] = tpl; - } + if (assign) + assign[i] = cntr_idx; reserve: - set_bit(tpl->cntr[thread], used_mask); + set_bit(cntr_idx, used_mask); set_bit(escr_idx, escr_mask); } @@ -684,7 +786,7 @@ static __initconst struct x86_pmu p4_pmu = { .perfctr = MSR_P4_BPU_PERFCTR0, .event_map = p4_pmu_event_map, .raw_event = p4_pmu_raw_event, - .max_events = ARRAY_SIZE(p4_templates), + .max_events = ARRAY_SIZE(p4_general_events), .get_event_constraints = x86_get_event_constraints, /* * IF HT disabled we may need to use all @@ -716,7 +818,7 @@ static __init int p4_pmu_init(void) } memcpy(hw_cache_event_ids, p4_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); + sizeof(hw_cache_event_ids)); pr_cont("Netburst events, "); -- cgit v0.10.2 From 53c540195724b52422da067a31ef6916d2c70202 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:14 -0300 Subject: perf report: Add a popup menu to ask what operation is to be performed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now it presents a menu with these options: +------------------------------+ | Annotate CURRENT_SYMBOL_NAME | | Exit | +------------------------------+ If the highlighted (current) symbol is not annotatable only the "Exit" option will appear. Also add a confirmation dialog when ESC is pressed on the top level to avoid exiting the application by pressing one too many ESC key. To get to the menu just press the -> (Right navigation key), to exit just press ESC. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 12b229b..a3465a0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -28,6 +28,47 @@ static newtComponent newt_form__new(void) return self; } +static int popup_menu(int argc, const char *argv[]) +{ + struct newtExitStruct es; + int i, rc = -1, max_len = 5; + newtComponent listbox, form = newt_form__new(); + + if (form == NULL) + return -1; + + listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); + if (listbox == NULL) + goto out_destroy_form; + + newtFormAddComponents(form, listbox, NULL); + + for (i = 0; i < argc; ++i) { + int len = strlen(argv[i]); + if (len > max_len) + max_len = len; + if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) + goto out_destroy_form; + } + + newtCenteredWindow(max_len, argc, NULL); + newtFormRun(form, &es); + rc = newtListboxGetCurrent(listbox) - NULL; + if (es.reason == NEWT_EXIT_HOTKEY) + rc = -1; + newtPopWindow(); +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +static bool dialog_yesno(const char *msg) +{ + /* newtWinChoice should really be accepting const char pointers... */ + char yes[] = "Yes", no[] = "No"; + return newtWinChoice(NULL, no, yes, (char *)msg) == 2; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -309,6 +350,19 @@ out_free_str: free(str); } +static const void *newt__symbol_tree_get_current(newtComponent self) +{ + if (symbol_conf.use_callchain) + return newtCheckboxTreeGetCurrent(self); + return newtListboxGetCurrent(self); +} + +static void perf_session__selection(newtComponent self, void *data) +{ + const struct symbol **symbol_ptr = data; + *symbol_ptr = newt__symbol_tree_get_current(self); +} + void perf_session__browse_hists(struct rb_root *hists, u64 session_total, const char *helpline) { @@ -322,6 +376,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, char str[1024]; newtComponent form, tree; struct newtExitStruct es; + const struct symbol *selection; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -336,6 +391,8 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT)); + newtComponentAddCallback(tree, perf_session__selection, &selection); + list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; @@ -377,20 +434,43 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, form = newt_form__new(); newtFormAddHotKey(form, 'A'); newtFormAddHotKey(form, 'a'); + newtFormAddHotKey(form, NEWT_KEY_RIGHT); newtFormAddComponents(form, tree, NULL); + selection = newt__symbol_tree_get_current(tree); while (1) { - const struct symbol *selection; + char annotate[512]; + const char *options[2]; + int nr_options = 0, choice; newtFormRun(form, &es); - if (es.reason == NEWT_EXIT_HOTKEY && - toupper(es.u.key) != 'A') + if (es.reason == NEWT_EXIT_HOTKEY) { + if (toupper(es.u.key) == 'A') { + symbol__annotate_browser(selection); + continue; + } + if (es.u.key == NEWT_KEY_ESCAPE || + toupper(es.u.key) == 'Q' || + es.u.key == CTRL('c')) { + if (dialog_yesno("Do you really want to exit?")) + break; + else + continue; + } + } + + if (selection != NULL) { + snprintf(annotate, sizeof(annotate), + "Annotate %s", selection->name); + options[nr_options++] = annotate; + } + + options[nr_options++] = "Exit"; + choice = popup_menu(nr_options, options); + if (choice == nr_options - 1) break; - if (!symbol_conf.use_callchain) - selection = newtListboxGetCurrent(tree); - else - selection = newtCheckboxTreeGetCurrent(tree); - symbol__annotate_browser(selection); + else if (selection != NULL && choice >= 0) + symbol__annotate_browser(selection); } newtFormDestroy(form); -- cgit v0.10.2 From 96415e4d3f5fdf9cdb12eedfcbc58152b1e1458c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:15 -0300 Subject: perf symbols: Avoid unnecessary symbol loading when dso list is specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were performing the full thread__find_addr_location operation, i.e. resolving to a map/dso _and_ loading its symbols when we can optimize it by first calling thread__find_addr_map to find just the map/dso, check if it is one that we are interested in (passed via --dsos/-d in 'perf annotate', 'perf report', etc) and if not avoid loading the symtab. Nice speedup when we know which DSO we're interested in. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 705ec63..c2808ad 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -513,24 +513,32 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, - self->ip.ip, al, filter); + thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, + self->ip.ip, al); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); - /* - * 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. - */ - if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) - dso__calc_col_width(al->map->dso); - - if (symbol_conf.dso_list && - (!al->map || !al->map->dso || - !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || - (al->map->dso->short_name != al->map->dso->long_name && - strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) - goto out_filtered; + al->sym = NULL; + + if (al->map) { + if (symbol_conf.dso_list && + (!al->map || !al->map->dso || + !(strlist__has_entry(symbol_conf.dso_list, + al->map->dso->short_name) || + (al->map->dso->short_name != al->map->dso->long_name && + strlist__has_entry(symbol_conf.dso_list, + al->map->dso->long_name))))) + goto out_filtered; + /* + * 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. + */ + if (!sort_dso.elide && !al->map->dso->slen_calculated) + dso__calc_col_width(al->map->dso); + + al->sym = map__find_symbol(al->map, al->addr, filter); + } if (symbol_conf.sym_list && al->sym && !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) -- cgit v0.10.2 From ac73c5a9c1767b2771e6d2b5accafdef89db04c2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:16 -0300 Subject: perf annotate: Allow specifying DSOs where to look for symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the same parameter as in 'perf report', allowing to specify just one and disambiguate between DSOs that have the symbol of interest. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 45d1466..ce9b1ef 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -591,6 +591,8 @@ static const char * const annotate_usage[] = { static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", + "only consider symbols in these dsos"), OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", "symbol to annotate"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), -- cgit v0.10.2 From 59fd53062f71011a68d03f4cd0ba93d822ac3249 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:17 -0300 Subject: perf tools: Introduce struct map_symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That will be in both struct hist_entry and struct callchain_list, so that the TUI can store a pointer to the pair (map, symbol) in the trees where hist_entries and callchain_lists are present, to allow precise annotation instead of looking for the first symbol with the selected name. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ce9b1ef..887e8e0 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -69,13 +69,13 @@ static int sym__alloc_hist(struct symbol *self) static int annotate__hist_hit(struct hist_entry *he, u64 ip) { unsigned int sym_size, offset; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct sym_priv *priv; struct sym_hist *h; he->count++; - if (!sym || !he->map) + if (!sym || !he->ms.map) return 0; priv = symbol__priv(sym); @@ -85,7 +85,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) sym_size = sym->end - sym->start; offset = ip - sym->start; - pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); + pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); if (offset >= sym_size) return 0; @@ -94,8 +94,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, - he->sym->name, ip, ip - he->sym->start, h->ip[offset]); + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, + he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); return 0; } @@ -187,7 +187,7 @@ static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, static int parse_line(FILE *file, struct hist_entry *he, struct list_head *head) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; struct objdump_line *objdump_line; char *line = NULL, *tmp, *tmp2; size_t line_len; @@ -226,7 +226,7 @@ static int parse_line(FILE *file, struct hist_entry *he, } if (line_ip != -1) { - u64 start = map__rip_2objdump(he->map, sym->start); + u64 start = map__rip_2objdump(he->ms.map, sym->start); offset = line_ip - start; } @@ -244,7 +244,7 @@ static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; static const char *prev_line; static const char *prev_color; @@ -327,7 +327,7 @@ static void insert_source_line(struct sym_ext *sym_ext) static void free_source_line(struct hist_entry *he, int len) { - struct sym_priv *priv = symbol__priv(he->sym); + struct sym_priv *priv = symbol__priv(he->ms.sym); struct sym_ext *sym_ext = priv->ext; int i; @@ -346,7 +346,7 @@ static void free_source_line(struct hist_entry *he, int len) static void get_source_line(struct hist_entry *he, int len, const char *filename) { - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; u64 start; int i; char cmd[PATH_MAX * 2]; @@ -361,7 +361,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) if (!priv->ext) return; - start = he->map->unmap_ip(he->map, sym->start); + start = he->ms.map->unmap_ip(he->ms.map, sym->start); for (i = 0; i < len; i++) { char *path = NULL; @@ -425,7 +425,7 @@ static void print_summary(const char *filename) static void hist_entry__print_hits(struct hist_entry *self) { - struct symbol *sym = self->sym; + struct symbol *sym = self->ms.sym; struct sym_priv *priv = symbol__priv(sym); struct sym_hist *h = priv->hist; u64 len = sym->end - sym->start, offset; @@ -439,9 +439,9 @@ static void hist_entry__print_hits(struct hist_entry *self) static void annotate_sym(struct hist_entry *he) { - struct map *map = he->map; + struct map *map = he->ms.map; struct dso *dso = map->dso; - struct symbol *sym = he->sym; + struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; char command[PATH_MAX*2]; @@ -526,17 +526,17 @@ static void perf_session__find_annotations(struct perf_session *self) struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; - if (he->sym == NULL) + if (he->ms.sym == NULL) continue; - priv = symbol__priv(he->sym); + priv = symbol__priv(he->ms.sym); if (priv->hist == NULL) continue; annotate_sym(he); /* * Since we have a hist_entry per IP for the same symbol, free - * he->sym->hist to signal we already processed this symbol. + * he->ms.sym->hist to signal we already processed this symbol. */ free(priv->hist); priv->hist = NULL; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5843a9c..4eefb52 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -22,8 +22,10 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct hist_entry *he; struct hist_entry entry = { .thread = al->thread, - .map = al->map, - .sym = al->sym, + .ms = { + .map = al->map, + .sym = al->sym, + }, .ip = al->addr, .level = al->level, .count = count, @@ -654,7 +656,7 @@ print_entries: if (symbol_conf.use_callchain) ret += hist_entry__fprintf_callchain(h, fp, session_total); - if (h->map == NULL && verbose > 1) { + if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index a3465a0..25cd2e1 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -287,9 +287,9 @@ static size_t hist_entry__append_browser(struct hist_entry *self, indexes[0] = NEWT_ARG_APPEND; indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, self->sym, indexes); + newt_checkbox_tree__add(tree, s, self->ms.sym, indexes); } else - newtListboxAppendEntry(tree, s, self->sym); + newtListboxAppendEntry(tree, s, self->ms.sym); return strlen(s); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cb0f327..9b80c13 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -131,8 +131,8 @@ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) int64_t sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) { - struct dso *dso_l = left->map ? left->map->dso : NULL; - struct dso *dso_r = right->map ? right->map->dso : NULL; + struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; + struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; const char *dso_name_l, *dso_name_r; if (!dso_l || !dso_r) @@ -152,9 +152,9 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) size_t sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) { - if (self->map && self->map->dso) { - const char *dso_name = !verbose ? self->map->dso->short_name : - self->map->dso->long_name; + if (self->ms.map && self->ms.map->dso) { + const char *dso_name = !verbose ? self->ms.map->dso->short_name : + self->ms.map->dso->long_name; return repsep_fprintf(fp, "%-*s", width, dso_name); } @@ -168,11 +168,11 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) { u64 ip_l, ip_r; - if (left->sym == right->sym) + if (left->ms.sym == right->ms.sym) return 0; - ip_l = left->sym ? left->sym->start : left->ip; - ip_r = right->sym ? right->sym->start : right->ip; + ip_l = left->ms.sym ? left->ms.sym->start : left->ip; + ip_r = right->ms.sym ? right->ms.sym->start : right->ip; return (int64_t)(ip_r - ip_l); } @@ -184,13 +184,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) size_t ret = 0; if (verbose) { - char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; + char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); } ret += repsep_fprintf(fp, "[%c] ", self->level); - if (self->sym) - ret += repsep_fprintf(fp, "%s", self->sym->name); + if (self->ms.sym) + ret += repsep_fprintf(fp, "%s", self->ms.sym->name); else ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 753f9ea..5985686 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -45,8 +45,7 @@ struct hist_entry { struct rb_node rb_node; u64 count; struct thread *thread; - struct map *map; - struct symbol *sym; + struct map_symbol ms; u64 ip; char level; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0da2455..a4a894b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -88,6 +88,11 @@ struct ref_reloc_sym { u64 unrelocated_addr; }; +struct map_symbol { + struct map *map; + struct symbol *sym; +}; + struct addr_location { struct thread *thread; struct map *map; -- cgit v0.10.2 From b3c9ac0846c654dea4df095999ee202e8b4cb253 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:18 -0300 Subject: perf callchains: Store the map together with the symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need this to know where a symbol in a callchain came from, for various reasons, among them precise annotation from a TUI/GUI tool. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d609afb..6ab1698 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -81,7 +81,8 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, struct sample_data *data) { - struct symbol **syms = NULL, *parent = NULL; + struct map_symbol *syms = NULL; + struct symbol *parent = NULL; bool hit; int err; struct hist_entry *he; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 883844e..db628af 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -185,8 +185,8 @@ create_child(struct callchain_node *parent, bool inherit_children) struct resolved_ip { - u64 ip; - struct symbol *sym; + u64 ip; + struct map_symbol ms; }; struct resolved_chain { @@ -212,7 +212,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) return; } call->ip = chain->ips[i].ip; - call->sym = chain->ips[i].sym; + call->ms = chain->ips[i].ms; list_add_tail(&call->list, &node->val); } node->val_nr = chain->nr - start; @@ -318,10 +318,10 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, if (i == chain->nr) break; - sym = chain->ips[i].sym; + sym = chain->ips[i].ms.sym; - if (cnode->sym && sym) { - if (cnode->sym->start != sym->start) + if (cnode->ms.sym && sym) { + if (cnode->ms.sym->start != sym->start) break; } else if (cnode->ip != chain->ips[i].ip) break; @@ -353,9 +353,8 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain, return 0; } -static void -filter_context(struct ip_callchain *old, struct resolved_chain *new, - struct symbol **syms) +static void filter_context(struct ip_callchain *old, struct resolved_chain *new, + struct map_symbol *syms) { int i, j = 0; @@ -364,7 +363,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, continue; new->ips[j].ip = old->ips[i]; - new->ips[j].sym = syms[i]; + new->ips[j].ms = syms[i]; j++; } @@ -373,7 +372,7 @@ filter_context(struct ip_callchain *old, struct resolved_chain *new, int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms) + struct map_symbol *syms) { struct resolved_chain *filtered; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index bbd76da..8a7e8bb 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -39,7 +39,7 @@ struct callchain_param { struct callchain_list { u64 ip; - struct symbol *sym; + struct map_symbol ms; struct list_head list; }; @@ -57,5 +57,5 @@ static inline u64 cumul_hits(struct callchain_node *node) int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, - struct symbol **syms); + struct map_symbol *syms); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4eefb52..09e09e7 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -260,8 +260,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, } else ret += fprintf(fp, "%s", " "); } - if (chain->sym) - ret += fprintf(fp, "%s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, "%s\n", chain->ms.sym->name); else ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); @@ -280,7 +280,7 @@ static void init_rem_hits(void) } strcpy(rem_sq_bracket->name, "[...]"); - rem_hits.sym = rem_sq_bracket; + rem_hits.ms.sym = rem_sq_bracket; } static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, @@ -382,8 +382,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, } else ret += callchain__fprintf_left_margin(fp, left_margin); - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); } @@ -408,8 +408,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, list_for_each_entry(chain, &self->val, list) { if (chain->ip >= PERF_CONTEXT_MAX) continue; - if (chain->sym) - ret += fprintf(fp, " %s\n", chain->sym->name); + if (chain->ms.sym) + ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 25cd2e1..f6953ca 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -104,8 +104,8 @@ static void newt_checkbox_tree__add(newtComponent tree, const char *str, static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { - if (self->sym) - return self->sym->name; + if (self->ms.sym) + return self->ms.sym->name; snprintf(bf, bfsize, "%#Lx", self->ip); return bf; @@ -157,7 +157,7 @@ static void __callchain__append_graph_browser(struct callchain_node *self, indexes[depth + 2] = NEWT_ARG_LAST; ++chain_idx; } - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); free(alloc_str); ++printed; } @@ -193,7 +193,7 @@ static void callchain__append_graph_browser(struct callchain_node *self, continue; str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, chain->sym, indexes); + newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); } indexes[1] = parent_idx; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index eed1cb8..2cef373 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -117,13 +117,13 @@ static bool symbol__match_parent_regex(struct symbol *sym) return 0; } -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent) +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; - struct symbol **syms = NULL; + struct map_symbol *syms = NULL; unsigned int i; if (symbol_conf.use_callchain) { @@ -160,7 +160,8 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, *parent = al.sym; if (!symbol_conf.use_callchain) break; - syms[i] = al.sym; + syms[i].map = al.map; + syms[i].sym = al.sym; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 34d7339..631f815 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -57,10 +57,10 @@ int __perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self, struct perf_event_ops *event_ops); -struct symbol **perf_session__resolve_callchain(struct perf_session *self, - struct thread *thread, - struct ip_callchain *chain, - struct symbol **parent); +struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent); bool perf_session__has_traces(struct perf_session *self, const char *msg); -- cgit v0.10.2 From d5679ae4d2d4369477dc3b60027cca222aef33e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 24 Mar 2010 16:40:19 -0300 Subject: perf report: Pass the DSO to 'perf annotate' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we ensure that the symbol asked for annotation really is in the DSO we are interested in. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269459619-982-6-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f6953ca..e99bcc8 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -157,7 +157,7 @@ static void __callchain__append_graph_browser(struct callchain_node *self, indexes[depth + 2] = NEWT_ARG_LAST; ++chain_idx; } - newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); + newt_checkbox_tree__add(tree, str, &chain->ms, indexes); free(alloc_str); ++printed; } @@ -193,7 +193,7 @@ static void callchain__append_graph_browser(struct callchain_node *self, continue; str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, chain->ms.sym, indexes); + newt_checkbox_tree__add(tree, str, &chain->ms, indexes); } indexes[1] = parent_idx; @@ -287,14 +287,14 @@ static size_t hist_entry__append_browser(struct hist_entry *self, indexes[0] = NEWT_ARG_APPEND; indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, self->ms.sym, indexes); + newt_checkbox_tree__add(tree, s, &self->ms, indexes); } else - newtListboxAppendEntry(tree, s, self->ms.sym); + newtListboxAppendEntry(tree, s, &self->ms); return strlen(s); } -static void symbol__annotate_browser(const struct symbol *self) +static void map_symbol__annotate_browser(const struct map_symbol *self) { FILE *fp; int cols, rows; @@ -305,10 +305,11 @@ static void symbol__annotate_browser(const struct symbol *self) size_t max_usable_width; char *line = NULL; - if (self == NULL) + if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0) + if (asprintf(&str, "perf annotate -d \"%s\" %s 2>&1 | expand", + self->map->dso->name, self->sym->name) < 0) return; fp = popen(str, "r"); @@ -338,7 +339,7 @@ static void symbol__annotate_browser(const struct symbol *self) newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->name); + newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); newtFormAddComponents(form, tree, NULL); @@ -359,7 +360,7 @@ static const void *newt__symbol_tree_get_current(newtComponent self) static void perf_session__selection(newtComponent self, void *data) { - const struct symbol **symbol_ptr = data; + const struct map_symbol **symbol_ptr = data; *symbol_ptr = newt__symbol_tree_get_current(self); } @@ -376,7 +377,7 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, char str[1024]; newtComponent form, tree; struct newtExitStruct es; - const struct symbol *selection; + const struct map_symbol *selection; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -416,11 +417,8 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, int len = hist_entry__append_browser(h, tree, session_total); if (len > max_len) max_len = len; - if (symbol_conf.use_callchain) { + if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, tree, session_total, idx++); - if (idx > 3300) - break; - } } if (max_len > cols) @@ -441,14 +439,12 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, while (1) { char annotate[512]; const char *options[2]; - int nr_options = 0, choice; + int nr_options = 0, choice = 0; newtFormRun(form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') { - symbol__annotate_browser(selection); - continue; - } + if (toupper(es.u.key) == 'A') + goto do_annotate; if (es.u.key == NEWT_KEY_ESCAPE || toupper(es.u.key) == 'Q' || es.u.key == CTRL('c')) { @@ -459,9 +455,9 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, } } - if (selection != NULL) { + if (selection->sym != NULL) { snprintf(annotate, sizeof(annotate), - "Annotate %s", selection->name); + "Annotate %s", selection->sym->name); options[nr_options++] = annotate; } @@ -469,8 +465,17 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, choice = popup_menu(nr_options, options); if (choice == nr_options - 1) break; - else if (selection != NULL && choice >= 0) - symbol__annotate_browser(selection); +do_annotate: + if (selection->sym != NULL && choice >= 0) { + if (selection->map->dso->origin == DSO__ORIG_KERNEL) { + newtPopHelpLine(); + newtPushHelpLine("No vmlinux file found, can't " + "annotate with just a " + "kallsyms file"); + continue; + } + map_symbol__annotate_browser(selection); + } } newtFormDestroy(form); -- cgit v0.10.2 From b177f63f5226e75280855bbcd106e677250778bd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Mar 2010 19:58:57 -0300 Subject: perf symbols: Pass the mmap parameters instead of using mmap_event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To reduce the coupling of the symbol system with the rest of perf. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269557941-15617-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index c2808ad..052eaec 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -393,7 +393,8 @@ int event__process_mmap(event_t *self, struct perf_session *session) } thread = perf_session__findnew(session, self->mmap.pid); - map = map__new(&self->mmap, MAP__FUNCTION, + map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, + self->mmap.pid, self->mmap.filename, MAP__FUNCTION, session->cwd, session->cwdlen); if (thread == NULL || map == NULL) diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e509cd5..a9b4227 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,4 +1,3 @@ -#include "event.h" #include "symbol.h" #include #include @@ -38,13 +37,12 @@ void map__init(struct map *self, enum map_type type, RB_CLEAR_NODE(&self->rb_node); } -struct map *map__new(struct mmap_event *event, enum map_type type, - char *cwd, int cwdlen) +struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, + enum map_type type, char *cwd, int cwdlen) { struct map *self = malloc(sizeof(*self)); if (self != NULL) { - const char *filename = event->filename; char newfilename[PATH_MAX]; struct dso *dso; int anon; @@ -62,7 +60,7 @@ struct map *map__new(struct mmap_event *event, enum map_type type, anon = is_anon_memory(filename); if (anon) { - snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); + snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); filename = newfilename; } @@ -70,8 +68,7 @@ struct map *map__new(struct mmap_event *event, enum map_type type, if (dso == NULL) goto out_delete; - map__init(self, type, event->start, event->start + event->len, - event->pgoff, dso); + map__init(self, type, start, start + len, pgoff, dso); if (anon) { set_identity: diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b756368..a4a5bc4 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -68,14 +68,13 @@ u64 map__rip_2objdump(struct map *map, u64 rip); u64 map__objdump_2ip(struct map *map, u64 addr); struct symbol; -struct mmap_event; typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); -struct map *map__new(struct mmap_event *event, enum map_type, - char *cwd, int cwdlen); +struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, + enum map_type type, char *cwd, int cwdlen); void map__delete(struct map *self); struct map *map__clone(struct map *self); int map__overlap(struct map *l, struct map *r); -- cgit v0.10.2 From 4b8cf84624e9a58a21aaac3d064222092ae234e0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Mar 2010 19:58:58 -0300 Subject: perf symbols: Move map related routines to map.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thru series of refactorings functions were being renamed but not moved to map.c to reduce patch noise, now lets have them in the same place so that use of the symbol system by tools can be constrained to building and linking fewer source files: symbol.c, map.c and rbtree.c. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269557941-15617-3-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a9b4227..9f2963f 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,8 +1,9 @@ #include "symbol.h" +#include #include #include #include -#include "debug.h" +#include "map.h" const char *map_type__name[MAP__NR_TYPES] = { [MAP__FUNCTION] = "Functions", @@ -232,3 +233,84 @@ u64 map__objdump_2ip(struct map *map, u64 addr) map->unmap_ip(map, addr); /* RIP -> IP */ return ip; } + +struct symbol *map_groups__find_symbol(struct map_groups *self, + enum map_type type, u64 addr, + symbol_filter_t filter) +{ + struct map *map = map_groups__find(self, type, addr); + + if (map != NULL) + return map__find_symbol(map, map->map_ip(map, addr), filter); + + return NULL; +} + +static u64 map__reloc_map_ip(struct map *map, u64 ip) +{ + return ip + (s64)map->pgoff; +} + +static u64 map__reloc_unmap_ip(struct map *map, u64 ip) +{ + return ip - (s64)map->pgoff; +} + +void map__reloc_vmlinux(struct map *self) +{ + struct kmap *kmap = map__kmap(self); + s64 reloc; + + if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) + return; + + reloc = (kmap->ref_reloc_sym->unrelocated_addr - + kmap->ref_reloc_sym->addr); + + if (!reloc) + return; + + self->map_ip = map__reloc_map_ip; + self->unmap_ip = map__reloc_unmap_ip; + self->pgoff = reloc; +} + +void maps__insert(struct rb_root *maps, struct map *map) +{ + struct rb_node **p = &maps->rb_node; + struct rb_node *parent = NULL; + const u64 ip = map->start; + struct map *m; + + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); + if (ip < m->start) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&map->rb_node, parent, p); + rb_insert_color(&map->rb_node, maps); +} + +struct map *maps__find(struct rb_root *maps, u64 ip) +{ + struct rb_node **p = &maps->rb_node; + struct rb_node *parent = NULL; + struct map *m; + + while (*p != NULL) { + parent = *p; + m = rb_entry(parent, struct map, rb_node); + if (ip < m->start) + p = &(*p)->rb_left; + else if (ip > m->end) + p = &(*p)->rb_right; + else + return m; + } + + return NULL; +} diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index a4a5bc4..6a703fa 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -4,7 +4,8 @@ #include #include #include -#include +#include +#include "types.h" enum map_type { MAP__FUNCTION = 0, @@ -90,4 +91,48 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); +struct map_groups { + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; +}; + +size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, FILE *fp); +void maps__insert(struct rb_root *maps, struct map *map); +struct map *maps__find(struct rb_root *maps, u64 addr); +void map_groups__init(struct map_groups *self); +size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); + +static inline void map_groups__insert(struct map_groups *self, struct map *map) +{ + maps__insert(&self->maps[map->type], map); +} + +static inline struct map *map_groups__find(struct map_groups *self, + enum map_type type, u64 addr) +{ + return maps__find(&self->maps[type], addr); +} + +struct symbol *map_groups__find_symbol(struct map_groups *self, + enum map_type type, u64 addr, + symbol_filter_t filter); + +static inline struct symbol *map_groups__find_function(struct map_groups *self, + u64 addr, + symbol_filter_t filter) +{ + return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); +} + +struct map *map_groups__find_by_name(struct map_groups *self, + enum map_type type, const char *name); +int __map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES], + struct dso *kernel); +int map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES]); +struct map *map_groups__new_module(struct map_groups *self, u64 start, + const char *filename); + #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 2cef373..76b4ac6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -544,32 +544,3 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, return 0; } - -static u64 map__reloc_map_ip(struct map *map, u64 ip) -{ - return ip + (s64)map->pgoff; -} - -static u64 map__reloc_unmap_ip(struct map *map, u64 ip) -{ - return ip - (s64)map->pgoff; -} - -void map__reloc_vmlinux(struct map *self) -{ - struct kmap *kmap = map__kmap(self); - s64 reloc; - - if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) - return; - - reloc = (kmap->ref_reloc_sym->unrelocated_addr - - kmap->ref_reloc_sym->addr); - - if (!reloc) - return; - - self->map_ip = map__reloc_map_ip; - self->unmap_ip = map__reloc_unmap_ip; - self->pgoff = reloc; -} diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index ea65062..9bbe27d 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -272,46 +272,6 @@ static int map_groups__fixup_overlappings(struct map_groups *self, return 0; } -void maps__insert(struct rb_root *maps, struct map *map) -{ - struct rb_node **p = &maps->rb_node; - struct rb_node *parent = NULL; - const u64 ip = map->start; - struct map *m; - - while (*p != NULL) { - parent = *p; - m = rb_entry(parent, struct map, rb_node); - if (ip < m->start) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&map->rb_node, parent, p); - rb_insert_color(&map->rb_node, maps); -} - -struct map *maps__find(struct rb_root *maps, u64 ip) -{ - struct rb_node **p = &maps->rb_node; - struct rb_node *parent = NULL; - struct map *m; - - while (*p != NULL) { - parent = *p; - m = rb_entry(parent, struct map, rb_node); - if (ip < m->start) - p = &(*p)->rb_left; - else if (ip > m->end) - p = &(*p)->rb_right; - else - return m; - } - - return NULL; -} - void thread__insert_map(struct thread *self, struct map *map) { map_groups__fixup_overlappings(&self->mg, map); @@ -367,16 +327,3 @@ size_t perf_session__fprintf(struct perf_session *self, FILE *fp) return ret; } - -struct symbol *map_groups__find_symbol(struct map_groups *self, - enum map_type type, u64 addr, - symbol_filter_t filter) -{ - struct map *map = map_groups__find(self, type, addr); - - if (map != NULL) - return map__find_symbol(map, map->map_ip(map, addr), filter); - - return NULL; -} - diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index a81426a..9c488fc 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -5,14 +5,6 @@ #include #include "symbol.h" -struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; -}; - -size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, FILE *fp); - struct thread { struct rb_node rb_node; struct map_groups mg; @@ -23,30 +15,16 @@ struct thread { int comm_len; }; +struct perf_session; + int find_all_tid(int pid, pid_t ** all_tid); -void map_groups__init(struct map_groups *self); int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *self, struct thread *parent); -size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); size_t perf_session__fprintf(struct perf_session *self, FILE *fp); -void maps__insert(struct rb_root *maps, struct map *map); -struct map *maps__find(struct rb_root *maps, u64 addr); - -static inline void map_groups__insert(struct map_groups *self, struct map *map) -{ - maps__insert(&self->maps[map->type], map); -} - -static inline struct map *map_groups__find(struct map_groups *self, - enum map_type type, u64 addr) -{ - return maps__find(&self->maps[type], addr); -} - static inline struct map *thread__find_map(struct thread *self, enum map_type type, u64 addr) { @@ -63,26 +41,4 @@ void thread__find_addr_location(struct thread *self, enum map_type type, u64 addr, struct addr_location *al, symbol_filter_t filter); -struct symbol *map_groups__find_symbol(struct map_groups *self, - enum map_type type, u64 addr, - symbol_filter_t filter); - -static inline struct symbol *map_groups__find_function(struct map_groups *self, - u64 addr, - symbol_filter_t filter) -{ - return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); -} - -struct map *map_groups__find_by_name(struct map_groups *self, - enum map_type type, const char *name); - -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel); -int map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES]); - -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename); #endif /* __PERF_THREAD_H */ -- cgit v0.10.2 From 618038df3588fdfcaccfd40057f36ce792bee252 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Mar 2010 19:58:59 -0300 Subject: perf tools: Move __used from perf.h to linux/compiler.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like in the kernel and also to remove the need to include perf.h in the symbol subsystem. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269557941-15617-4-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/perf.h b/tools/perf/perf.h index aa78615..ec21274 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -106,8 +106,6 @@ static inline unsigned long long rdclock(void) #define __user #define asmlinkage -#define __used __attribute__((__unused__)) - #define unlikely(x) __builtin_expect(!!(x), 0) #define min(x, y) ({ \ typeof(x) _min1 = (x); \ diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index dfb0713..791f9dd 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -7,4 +7,6 @@ #define __user #define __attribute_const__ +#define __used __attribute__((__unused__)) + #endif -- cgit v0.10.2 From 5aab621b7bf024608f0c089e21656e7fe875a150 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Mar 2010 19:59:00 -0300 Subject: perf symbols: Move hex2u64 and strxfrchar to symbol.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly used in symbol.c so move them there to reduce the number of files needed to use the symbol system. Also do some header adjustments with the same intent. Signed-off-by: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1269557941-15617-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index a201445..435781e 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -5,6 +5,7 @@ #include "parse-events.h" #include "exec_cmd.h" #include "string.h" +#include "symbol.h" #include "cache.h" #include "header.h" #include "debugfs.h" diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index a175949..d438924 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -1,49 +1,6 @@ #include "string.h" #include "util.h" -static int hex(char ch) -{ - if ((ch >= '0') && (ch <= '9')) - return ch - '0'; - if ((ch >= 'a') && (ch <= 'f')) - return ch - 'a' + 10; - if ((ch >= 'A') && (ch <= 'F')) - return ch - 'A' + 10; - return -1; -} - -/* - * While we find nice hex chars, build a long_val. - * Return number of chars processed. - */ -int hex2u64(const char *ptr, u64 *long_val) -{ - const char *p = ptr; - *long_val = 0; - - while (*p) { - const int hex_val = hex(*p); - - if (hex_val < 0) - break; - - *long_val = (*long_val << 4) | hex_val; - p++; - } - - return p - ptr; -} - -char *strxfrchar(char *s, char from, char to) -{ - char *p = s; - - while ((p = strchr(p, from)) != NULL) - *p++ = to; - - return s; -} - #define K 1024LL /* * perf_atoll() diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 542e44d..7005824 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h @@ -4,8 +4,6 @@ #include #include "types.h" -int hex2u64(const char *ptr, u64 *val); -char *strxfrchar(char *s, char from, char to); s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); void argv_free(char **argv); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 3eb9de4..f3d4151 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,13 +1,19 @@ -#include "util.h" -#include "../perf.h" -#include "sort.h" -#include "string.h" +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "symbol.h" -#include "thread.h" +#include "strlist.h" -#include "debug.h" - -#include #include #include #include @@ -114,8 +120,8 @@ static void map_groups__fixup_end(struct map_groups *self) static struct symbol *symbol__new(u64 start, u64 len, const char *name) { size_t namelen = strlen(name) + 1; - struct symbol *self = zalloc(symbol_conf.priv_size + - sizeof(*self) + namelen); + struct symbol *self = calloc(1, (symbol_conf.priv_size + + sizeof(*self) + namelen)); if (self == NULL) return NULL; @@ -166,7 +172,7 @@ static void dso__set_basename(struct dso *self) struct dso *dso__new(const char *name) { - struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); + struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1); if (self != NULL) { int i; @@ -1382,13 +1388,13 @@ static int dso__kernel_module_get_build_id(struct dso *self) return 0; } -static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) +static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) { struct dirent *dent; - DIR *dir = opendir(dirname); + DIR *dir = opendir(dir_name); if (!dir) { - pr_debug("%s: cannot open %s dir\n", __func__, dirname); + pr_debug("%s: cannot open %s dir\n", __func__, dir_name); return -1; } @@ -1401,7 +1407,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirna continue; snprintf(path, sizeof(path), "%s/%s", - dirname, dent->d_name); + dir_name, dent->d_name); if (map_groups__set_modules_path_dir(self, path) < 0) goto failure; } else { @@ -1421,7 +1427,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirna continue; snprintf(path, sizeof(path), "%s/%s", - dirname, dent->d_name); + dir_name, dent->d_name); long_name = strdup(path); if (long_name == NULL) @@ -1458,8 +1464,8 @@ static int map_groups__set_modules_path(struct map_groups *self) */ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) { - struct map *self = zalloc(sizeof(*self) + - (dso->kernel ? sizeof(struct kmap) : 0)); + struct map *self = calloc(1, (sizeof(*self) + + (dso->kernel ? sizeof(struct kmap) : 0))); if (self != NULL) { /* * ->end will be filled after we load all the symbols @@ -1963,3 +1969,46 @@ int map_groups__create_kernel_maps(struct map_groups *self, map_groups__fixup_end(self); return 0; } + +static int hex(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + if ((ch >= 'A') && (ch <= 'F')) + return ch - 'A' + 10; + return -1; +} + +/* + * While we find nice hex chars, build a long_val. + * Return number of chars processed. + */ +int hex2u64(const char *ptr, u64 *long_val) +{ + const char *p = ptr; + *long_val = 0; + + while (*p) { + const int hex_val = hex(*p); + + if (hex_val < 0) + break; + + *long_val = (*long_val << 4) | hex_val; + p++; + } + + return p - ptr; +} + +char *strxfrchar(char *s, char from, char to) +{ + char *p = s; + + while ((p = strchr(p, from)) != NULL) + *p++ = to; + + return s; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index a4a894b..757fae3 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -3,10 +3,11 @@ #include #include -#include "types.h" +#include +#include "map.h" #include #include -#include "event.h" +#include #define DEBUG_CACHE_DIR ".debug" @@ -29,6 +30,9 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, #endif #endif +int hex2u64(const char *ptr, u64 *val); +char *strxfrchar(char *s, char from, char to); + /* * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: @@ -44,6 +48,8 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ #endif +#define BUILD_ID_SIZE 20 + struct symbol { struct rb_node rb_node; u64 start; -- cgit v0.10.2 From 5a10317483f606106395814ee2fdaa2f1256a3b3 Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Thu, 25 Mar 2010 19:59:01 -0300 Subject: perf record: Zero out mmap_array to fix segfault Reported-by: Li Zefan Tested-by: Li Zefan Signed-off-by: Zhang Yanmin Signed-off-by: Arnaldo Carvalho de Melo LKML-Reference: <1269557941-15617-6-git-send-email-acme@infradead.org> Cc: Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index bb5b23d..60ecdd3 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -751,7 +751,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) for (i = 0; i < MAX_NR_CPUS; i++) { for (j = 0; j < MAX_COUNTERS; j++) { fd[i][j] = malloc(sizeof(int)*thread_num); - mmap_array[i][j] = malloc( + mmap_array[i][j] = zalloc( sizeof(struct mmap_data)*thread_num); if (!fd[i][j] || !mmap_array[i][j]) return -ENOMEM; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5f3ac9f..4abdd9b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1371,7 +1371,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) for (i = 0; i < MAX_NR_CPUS; i++) { for (j = 0; j < MAX_COUNTERS; j++) { fd[i][j] = malloc(sizeof(int)*thread_num); - mmap_array[i][j] = malloc( + mmap_array[i][j] = zalloc( sizeof(struct mmap_data)*thread_num); if (!fd[i][j] || !mmap_array[i][j]) return -ENOMEM; -- cgit v0.10.2 From 7c5ecaf7666617889f337296c610815b519abfa9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 25 Mar 2010 14:51:49 +0100 Subject: perf, x86: Clean up debugctlmsr bit definitions Move all debugctlmsr thingies into msr-index.h Signed-off-by: Peter Zijlstra LKML-Reference: <20100325135413.861425293@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index aef562c..06e4cf0 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -71,11 +71,14 @@ #define MSR_IA32_LASTINTTOIP 0x000001de /* DEBUGCTLMSR bits (others vary by model): */ -#define _DEBUGCTLMSR_LBR 0 /* last branch recording */ -#define _DEBUGCTLMSR_BTF 1 /* single-step on branches */ - -#define DEBUGCTLMSR_LBR (1UL << _DEBUGCTLMSR_LBR) -#define DEBUGCTLMSR_BTF (1UL << _DEBUGCTLMSR_BTF) +#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch recording */ +#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on branches */ +#define DEBUGCTLMSR_TR (1UL << 6) +#define DEBUGCTLMSR_BTS (1UL << 7) +#define DEBUGCTLMSR_BTINT (1UL << 8) +#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9) +#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10) +#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11) #define MSR_IA32_MC0_CTL 0x00000400 #define MSR_IA32_MC0_STATUS 0x00000401 diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index c59678a..2fea362 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -38,15 +38,6 @@ struct pebs_record_nhm { }; /* - * Bits in the debugctlmsr controlling branch tracing. - */ -#define X86_DEBUGCTL_TR (1 << 6) -#define X86_DEBUGCTL_BTS (1 << 7) -#define X86_DEBUGCTL_BTINT (1 << 8) -#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) -#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) - -/* * A debug store configuration. * * We only support architectures that use 64bit fields. @@ -193,15 +184,15 @@ static void intel_pmu_enable_bts(u64 config) debugctlmsr = get_debugctlmsr(); - debugctlmsr |= X86_DEBUGCTL_TR; - debugctlmsr |= X86_DEBUGCTL_BTS; - debugctlmsr |= X86_DEBUGCTL_BTINT; + debugctlmsr |= DEBUGCTLMSR_TR; + debugctlmsr |= DEBUGCTLMSR_BTS; + debugctlmsr |= DEBUGCTLMSR_BTINT; if (!(config & ARCH_PERFMON_EVENTSEL_OS)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; + debugctlmsr |= DEBUGCTLMSR_BTS_OFF_OS; if (!(config & ARCH_PERFMON_EVENTSEL_USR)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; + debugctlmsr |= DEBUGCTLMSR_BTS_OFF_USR; update_debugctlmsr(debugctlmsr); } @@ -217,8 +208,8 @@ static void intel_pmu_disable_bts(void) debugctlmsr = get_debugctlmsr(); debugctlmsr &= - ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | - X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); + ~(DEBUGCTLMSR_TR | DEBUGCTLMSR_BTS | DEBUGCTLMSR_BTINT | + DEBUGCTLMSR_BTS_OFF_OS | DEBUGCTLMSR_BTS_OFF_USR); update_debugctlmsr(debugctlmsr); } diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index df4c98e..d202c1b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -12,15 +12,12 @@ enum { * otherwise it becomes near impossible to get a reliable stack. */ -#define X86_DEBUGCTL_LBR (1 << 0) -#define X86_DEBUGCTL_FREEZE_LBRS_ON_PMI (1 << 11) - static void __intel_pmu_lbr_enable(void) { u64 debugctl; rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); - debugctl |= (X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI); + debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); } @@ -29,7 +26,7 @@ static void __intel_pmu_lbr_disable(void) u64 debugctl; rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); - debugctl &= ~(X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI); + debugctl &= ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); } -- cgit v0.10.2 From faa4602e47690fb11221e00f9b9697c8dc0d4b19 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 25 Mar 2010 14:51:50 +0100 Subject: x86, perf, bts, mm: Delete the never used BTS-ptrace code Support for the PMU's BTS features has been upstreamed in v2.6.32, but we still have the old and disabled ptrace-BTS, as Linus noticed it not so long ago. It's buggy: TIF_DEBUGCTLMSR is trampling all over that MSR without regard for other uses (perf) and doesn't provide the flexibility needed for perf either. Its users are ptrace-block-step and ptrace-bts, since ptrace-bts was never used and ptrace-block-step can be implemented using a much simpler approach. So axe all 3000 lines of it. That includes the *locked_memory*() APIs in mm/mlock.c as well. Reported-by: Linus Torvalds Signed-off-by: Peter Zijlstra Cc: Roland McGrath Cc: Oleg Nesterov Cc: Markus Metzger Cc: Steven Rostedt Cc: Andrew Morton LKML-Reference: <20100325135413.938004390@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index a198293..918fbb1 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -502,23 +502,3 @@ config CPU_SUP_UMC_32 CPU might render the kernel unbootable. If unsure, say N. - -config X86_DS - def_bool X86_PTRACE_BTS - depends on X86_DEBUGCTLMSR - select HAVE_HW_BRANCH_TRACER - -config X86_PTRACE_BTS - bool "Branch Trace Store" - default y - depends on X86_DEBUGCTLMSR - depends on BROKEN - ---help--- - This adds a ptrace interface to the hardware's branch trace store. - - Debuggers may use it to collect an execution trace of the debugged - application in order to answer the question 'how did I get here?'. - Debuggers may trace user mode as well as kernel mode. - - Say Y unless there is no application development on this machine - and you want to save a small amount of code size. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index bc01e3e..bd58c8a 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -174,15 +174,6 @@ config IOMMU_LEAK Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. -config X86_DS_SELFTEST - bool "DS selftest" - default y - depends on DEBUG_KERNEL - depends on X86_DS - ---help--- - Perform Debug Store selftests at boot time. - If in doubt, say "N". - config HAVE_MMIOTRACE_SUPPORT def_bool y diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h deleted file mode 100644 index 70dac19..0000000 --- a/arch/x86/include/asm/ds.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Debug Store (DS) support - * - * This provides a low-level interface to the hardware's Debug Store - * feature that is used for branch trace store (BTS) and - * precise-event based sampling (PEBS). - * - * It manages: - * - DS and BTS hardware configuration - * - buffer overflow handling (to be done) - * - buffer access - * - * It does not do: - * - security checking (is the caller allowed to trace the task) - * - buffer allocation (memory accounting) - * - * - * Copyright (C) 2007-2009 Intel Corporation. - * Markus Metzger , 2007-2009 - */ - -#ifndef _ASM_X86_DS_H -#define _ASM_X86_DS_H - - -#include -#include -#include - - -#ifdef CONFIG_X86_DS - -struct task_struct; -struct ds_context; -struct ds_tracer; -struct bts_tracer; -struct pebs_tracer; - -typedef void (*bts_ovfl_callback_t)(struct bts_tracer *); -typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *); - - -/* - * A list of features plus corresponding macros to talk about them in - * the ds_request function's flags parameter. - * - * We use the enum to index an array of corresponding control bits; - * we use the macro to index a flags bit-vector. - */ -enum ds_feature { - dsf_bts = 0, - dsf_bts_kernel, -#define BTS_KERNEL (1 << dsf_bts_kernel) - /* trace kernel-mode branches */ - - dsf_bts_user, -#define BTS_USER (1 << dsf_bts_user) - /* trace user-mode branches */ - - dsf_bts_overflow, - dsf_bts_max, - dsf_pebs = dsf_bts_max, - - dsf_pebs_max, - dsf_ctl_max = dsf_pebs_max, - dsf_bts_timestamps = dsf_ctl_max, -#define BTS_TIMESTAMPS (1 << dsf_bts_timestamps) - /* add timestamps into BTS trace */ - -#define BTS_USER_FLAGS (BTS_KERNEL | BTS_USER | BTS_TIMESTAMPS) -}; - - -/* - * Request BTS or PEBS - * - * Due to alignement constraints, the actual buffer may be slightly - * smaller than the requested or provided buffer. - * - * Returns a pointer to a tracer structure on success, or - * ERR_PTR(errcode) on failure. - * - * The interrupt threshold is independent from the overflow callback - * to allow users to use their own overflow interrupt handling mechanism. - * - * The function might sleep. - * - * task: the task to request recording for - * cpu: the cpu to request recording for - * base: the base pointer for the (non-pageable) buffer; - * size: the size of the provided buffer in bytes - * ovfl: pointer to a function to be called on buffer overflow; - * NULL if cyclic buffer requested - * th: the interrupt threshold in records from the end of the buffer; - * -1 if no interrupt threshold is requested. - * flags: a bit-mask of the above flags - */ -extern struct bts_tracer *ds_request_bts_task(struct task_struct *task, - void *base, size_t size, - bts_ovfl_callback_t ovfl, - size_t th, unsigned int flags); -extern struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, - bts_ovfl_callback_t ovfl, - size_t th, unsigned int flags); -extern struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, - void *base, size_t size, - pebs_ovfl_callback_t ovfl, - size_t th, unsigned int flags); -extern struct pebs_tracer *ds_request_pebs_cpu(int cpu, - void *base, size_t size, - pebs_ovfl_callback_t ovfl, - size_t th, unsigned int flags); - -/* - * Release BTS or PEBS resources - * Suspend and resume BTS or PEBS tracing - * - * Must be called with irq's enabled. - * - * tracer: the tracer handle returned from ds_request_~() - */ -extern void ds_release_bts(struct bts_tracer *tracer); -extern void ds_suspend_bts(struct bts_tracer *tracer); -extern void ds_resume_bts(struct bts_tracer *tracer); -extern void ds_release_pebs(struct pebs_tracer *tracer); -extern void ds_suspend_pebs(struct pebs_tracer *tracer); -extern void ds_resume_pebs(struct pebs_tracer *tracer); - -/* - * Release BTS or PEBS resources - * Suspend and resume BTS or PEBS tracing - * - * Cpu tracers must call this on the traced cpu. - * Task tracers must call ds_release_~_noirq() for themselves. - * - * May be called with irq's disabled. - * - * Returns 0 if successful; - * -EPERM if the cpu tracer does not trace the current cpu. - * -EPERM if the task tracer does not trace itself. - * - * tracer: the tracer handle returned from ds_request_~() - */ -extern int ds_release_bts_noirq(struct bts_tracer *tracer); -extern int ds_suspend_bts_noirq(struct bts_tracer *tracer); -extern int ds_resume_bts_noirq(struct bts_tracer *tracer); -extern int ds_release_pebs_noirq(struct pebs_tracer *tracer); -extern int ds_suspend_pebs_noirq(struct pebs_tracer *tracer); -extern int ds_resume_pebs_noirq(struct pebs_tracer *tracer); - - -/* - * The raw DS buffer state as it is used for BTS and PEBS recording. - * - * This is the low-level, arch-dependent interface for working - * directly on the raw trace data. - */ -struct ds_trace { - /* the number of bts/pebs records */ - size_t n; - /* the size of a bts/pebs record in bytes */ - size_t size; - /* pointers into the raw buffer: - - to the first entry */ - void *begin; - /* - one beyond the last entry */ - void *end; - /* - one beyond the newest entry */ - void *top; - /* - the interrupt threshold */ - void *ith; - /* flags given on ds_request() */ - unsigned int flags; -}; - -/* - * An arch-independent view on branch trace data. - */ -enum bts_qualifier { - bts_invalid, -#define BTS_INVALID bts_invalid - - bts_branch, -#define BTS_BRANCH bts_branch - - bts_task_arrives, -#define BTS_TASK_ARRIVES bts_task_arrives - - bts_task_departs, -#define BTS_TASK_DEPARTS bts_task_departs - - bts_qual_bit_size = 4, - bts_qual_max = (1 << bts_qual_bit_size), -}; - -struct bts_struct { - __u64 qualifier; - union { - /* BTS_BRANCH */ - struct { - __u64 from; - __u64 to; - } lbr; - /* BTS_TASK_ARRIVES or BTS_TASK_DEPARTS */ - struct { - __u64 clock; - pid_t pid; - } event; - } variant; -}; - - -/* - * The BTS state. - * - * This gives access to the raw DS state and adds functions to provide - * an arch-independent view of the BTS data. - */ -struct bts_trace { - struct ds_trace ds; - - int (*read)(struct bts_tracer *tracer, const void *at, - struct bts_struct *out); - int (*write)(struct bts_tracer *tracer, const struct bts_struct *in); -}; - - -/* - * The PEBS state. - * - * This gives access to the raw DS state and the PEBS-specific counter - * reset value. - */ -struct pebs_trace { - struct ds_trace ds; - - /* the number of valid counters in the below array */ - unsigned int counters; - -#define MAX_PEBS_COUNTERS 4 - /* the counter reset value */ - unsigned long long counter_reset[MAX_PEBS_COUNTERS]; -}; - - -/* - * Read the BTS or PEBS trace. - * - * Returns a view on the trace collected for the parameter tracer. - * - * The view remains valid as long as the traced task is not running or - * the tracer is suspended. - * Writes into the trace buffer are not reflected. - * - * tracer: the tracer handle returned from ds_request_~() - */ -extern const struct bts_trace *ds_read_bts(struct bts_tracer *tracer); -extern const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer); - - -/* - * Reset the write pointer of the BTS/PEBS buffer. - * - * Returns 0 on success; -Eerrno on error - * - * tracer: the tracer handle returned from ds_request_~() - */ -extern int ds_reset_bts(struct bts_tracer *tracer); -extern int ds_reset_pebs(struct pebs_tracer *tracer); - -/* - * Set the PEBS counter reset value. - * - * Returns 0 on success; -Eerrno on error - * - * tracer: the tracer handle returned from ds_request_pebs() - * counter: the index of the counter - * value: the new counter reset value - */ -extern int ds_set_pebs_reset(struct pebs_tracer *tracer, - unsigned int counter, u64 value); - -/* - * Initialization - */ -struct cpuinfo_x86; -extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *); - -/* - * Context switch work - */ -extern void ds_switch_to(struct task_struct *prev, struct task_struct *next); - -#else /* CONFIG_X86_DS */ - -struct cpuinfo_x86; -static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {} -static inline void ds_switch_to(struct task_struct *prev, - struct task_struct *next) {} - -#endif /* CONFIG_X86_DS */ -#endif /* _ASM_X86_DS_H */ diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index b753ea5..5bec21a 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -21,7 +21,6 @@ struct mm_struct; #include #include #include -#include #include #include @@ -29,6 +28,7 @@ struct mm_struct; #include #include #include +#include #define HBP_NUM 4 /* @@ -473,10 +473,6 @@ struct thread_struct { unsigned long iopl; /* Max allowed port in the bitmap, in bytes: */ unsigned io_bitmap_max; -/* MSR_IA32_DEBUGCTLMSR value to switch in if TIF_DEBUGCTLMSR is set. */ - unsigned long debugctlmsr; - /* Debug Store context; see asm/ds.h */ - struct ds_context *ds_ctx; }; static inline unsigned long native_get_debugreg(int regno) @@ -814,21 +810,6 @@ static inline unsigned long get_debugctlmsr(void) return debugctlmsr; } -static inline unsigned long get_debugctlmsr_on_cpu(int cpu) -{ - u64 debugctlmsr = 0; - u32 val1, val2; - -#ifndef CONFIG_X86_DEBUGCTLMSR - if (boot_cpu_data.x86 < 6) - return 0; -#endif - rdmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, &val1, &val2); - debugctlmsr = val1 | ((u64)val2 << 32); - - return debugctlmsr; -} - static inline void update_debugctlmsr(unsigned long debugctlmsr) { #ifndef CONFIG_X86_DEBUGCTLMSR @@ -838,18 +819,6 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr) wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); } -static inline void update_debugctlmsr_on_cpu(int cpu, - unsigned long debugctlmsr) -{ -#ifndef CONFIG_X86_DEBUGCTLMSR - if (boot_cpu_data.x86 < 6) - return; -#endif - wrmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, - (u32)((u64)debugctlmsr), - (u32)((u64)debugctlmsr >> 32)); -} - /* * from system description table in BIOS. Mostly for MCA use, but * others may find it useful: diff --git a/arch/x86/include/asm/ptrace-abi.h b/arch/x86/include/asm/ptrace-abi.h index 8672303..52b098a 100644 --- a/arch/x86/include/asm/ptrace-abi.h +++ b/arch/x86/include/asm/ptrace-abi.h @@ -82,61 +82,6 @@ #ifndef __ASSEMBLY__ #include - -/* configuration/status structure used in PTRACE_BTS_CONFIG and - PTRACE_BTS_STATUS commands. -*/ -struct ptrace_bts_config { - /* requested or actual size of BTS buffer in bytes */ - __u32 size; - /* bitmask of below flags */ - __u32 flags; - /* buffer overflow signal */ - __u32 signal; - /* actual size of bts_struct in bytes */ - __u32 bts_size; -}; -#endif /* __ASSEMBLY__ */ - -#define PTRACE_BTS_O_TRACE 0x1 /* branch trace */ -#define PTRACE_BTS_O_SCHED 0x2 /* scheduling events w/ jiffies */ -#define PTRACE_BTS_O_SIGNAL 0x4 /* send SIG on buffer overflow - instead of wrapping around */ -#define PTRACE_BTS_O_ALLOC 0x8 /* (re)allocate buffer */ - -#define PTRACE_BTS_CONFIG 40 -/* Configure branch trace recording. - ADDR points to a struct ptrace_bts_config. - DATA gives the size of that buffer. - A new buffer is allocated, if requested in the flags. - An overflow signal may only be requested for new buffers. - Returns the number of bytes read. -*/ -#define PTRACE_BTS_STATUS 41 -/* Return the current configuration in a struct ptrace_bts_config - pointed to by ADDR; DATA gives the size of that buffer. - Returns the number of bytes written. -*/ -#define PTRACE_BTS_SIZE 42 -/* Return the number of available BTS records for draining. - DATA and ADDR are ignored. -*/ -#define PTRACE_BTS_GET 43 -/* Get a single BTS record. - DATA defines the index into the BTS array, where 0 is the newest - entry, and higher indices refer to older entries. - ADDR is pointing to struct bts_struct (see asm/ds.h). -*/ -#define PTRACE_BTS_CLEAR 44 -/* Clear the BTS buffer. - DATA and ADDR are ignored. -*/ -#define PTRACE_BTS_DRAIN 45 -/* Read all available BTS records and clear the buffer. - ADDR points to an array of struct bts_struct. - DATA gives the size of that buffer. - BTS records are read from oldest to newest. - Returns number of BTS records drained. -*/ +#endif #endif /* _ASM_X86_PTRACE_ABI_H */ diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index 69a686a..78cd1ea 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h @@ -289,12 +289,6 @@ extern int do_get_thread_area(struct task_struct *p, int idx, extern int do_set_thread_area(struct task_struct *p, int idx, struct user_desc __user *info, int can_allocate); -#ifdef CONFIG_X86_PTRACE_BTS -extern void ptrace_bts_untrace(struct task_struct *tsk); - -#define arch_ptrace_untrace(tsk) ptrace_bts_untrace(tsk) -#endif /* CONFIG_X86_PTRACE_BTS */ - #endif /* __KERNEL__ */ #endif /* !__ASSEMBLY__ */ diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index e0d2890..dc85e12 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -92,8 +92,6 @@ struct thread_info { #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ #define TIF_FREEZE 23 /* is freezing for suspend */ #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ -#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */ -#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */ #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ @@ -115,8 +113,6 @@ struct thread_info { #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FREEZE (1 << TIF_FREEZE) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) -#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR) -#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR) #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) @@ -147,7 +143,7 @@ struct thread_info { /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ - (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC) + (_TIF_IO_BITMAP|_TIF_NOTSC) #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 4c58352..e77b220 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -47,8 +47,6 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-y += process.o obj-y += i387.o xsave.o obj-y += ptrace.o -obj-$(CONFIG_X86_DS) += ds.o -obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o obj-$(CONFIG_X86_32) += tls.o obj-$(CONFIG_IA32_EMULATION) += tls.o obj-y += step.o diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 7e1cca1..d72377c 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -367,7 +366,6 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c) set_cpu_cap(c, X86_FEATURE_BTS); if (!(l1 & (1<<12))) set_cpu_cap(c, X86_FEATURE_PEBS); - ds_init_intel(c); } if (c->x86 == 6 && c->x86_model == 29 && cpu_has_clflush) diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c deleted file mode 100644 index 1c47390..0000000 --- a/arch/x86/kernel/ds.c +++ /dev/null @@ -1,1437 +0,0 @@ -/* - * Debug Store support - * - * This provides a low-level interface to the hardware's Debug Store - * feature that is used for branch trace store (BTS) and - * precise-event based sampling (PEBS). - * - * It manages: - * - DS and BTS hardware configuration - * - buffer overflow handling (to be done) - * - buffer access - * - * It does not do: - * - security checking (is the caller allowed to trace the task) - * - buffer allocation (memory accounting) - * - * - * Copyright (C) 2007-2009 Intel Corporation. - * Markus Metzger , 2007-2009 - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ds_selftest.h" - -/* - * The configuration for a particular DS hardware implementation: - */ -struct ds_configuration { - /* The name of the configuration: */ - const char *name; - - /* The size of pointer-typed fields in DS, BTS, and PEBS: */ - unsigned char sizeof_ptr_field; - - /* The size of a BTS/PEBS record in bytes: */ - unsigned char sizeof_rec[2]; - - /* The number of pebs counter reset values in the DS structure. */ - unsigned char nr_counter_reset; - - /* Control bit-masks indexed by enum ds_feature: */ - unsigned long ctl[dsf_ctl_max]; -}; -static struct ds_configuration ds_cfg __read_mostly; - - -/* Maximal size of a DS configuration: */ -#define MAX_SIZEOF_DS 0x80 - -/* Maximal size of a BTS record: */ -#define MAX_SIZEOF_BTS (3 * 8) - -/* BTS and PEBS buffer alignment: */ -#define DS_ALIGNMENT (1 << 3) - -/* Number of buffer pointers in DS: */ -#define NUM_DS_PTR_FIELDS 8 - -/* Size of a pebs reset value in DS: */ -#define PEBS_RESET_FIELD_SIZE 8 - -/* Mask of control bits in the DS MSR register: */ -#define BTS_CONTROL \ - ( ds_cfg.ctl[dsf_bts] | \ - ds_cfg.ctl[dsf_bts_kernel] | \ - ds_cfg.ctl[dsf_bts_user] | \ - ds_cfg.ctl[dsf_bts_overflow] ) - -/* - * A BTS or PEBS tracer. - * - * This holds the configuration of the tracer and serves as a handle - * to identify tracers. - */ -struct ds_tracer { - /* The DS context (partially) owned by this tracer. */ - struct ds_context *context; - /* The buffer provided on ds_request() and its size in bytes. */ - void *buffer; - size_t size; -}; - -struct bts_tracer { - /* The common DS part: */ - struct ds_tracer ds; - - /* The trace including the DS configuration: */ - struct bts_trace trace; - - /* Buffer overflow notification function: */ - bts_ovfl_callback_t ovfl; - - /* Active flags affecting trace collection. */ - unsigned int flags; -}; - -struct pebs_tracer { - /* The common DS part: */ - struct ds_tracer ds; - - /* The trace including the DS configuration: */ - struct pebs_trace trace; - - /* Buffer overflow notification function: */ - pebs_ovfl_callback_t ovfl; -}; - -/* - * Debug Store (DS) save area configuration (see Intel64 and IA32 - * Architectures Software Developer's Manual, section 18.5) - * - * The DS configuration consists of the following fields; different - * architetures vary in the size of those fields. - * - * - double-word aligned base linear address of the BTS buffer - * - write pointer into the BTS buffer - * - end linear address of the BTS buffer (one byte beyond the end of - * the buffer) - * - interrupt pointer into BTS buffer - * (interrupt occurs when write pointer passes interrupt pointer) - * - double-word aligned base linear address of the PEBS buffer - * - write pointer into the PEBS buffer - * - end linear address of the PEBS buffer (one byte beyond the end of - * the buffer) - * - interrupt pointer into PEBS buffer - * (interrupt occurs when write pointer passes interrupt pointer) - * - value to which counter is reset following counter overflow - * - * Later architectures use 64bit pointers throughout, whereas earlier - * architectures use 32bit pointers in 32bit mode. - * - * - * We compute the base address for the first 8 fields based on: - * - the field size stored in the DS configuration - * - the relative field position - * - an offset giving the start of the respective region - * - * This offset is further used to index various arrays holding - * information for BTS and PEBS at the respective index. - * - * On later 32bit processors, we only access the lower 32bit of the - * 64bit pointer fields. The upper halves will be zeroed out. - */ - -enum ds_field { - ds_buffer_base = 0, - ds_index, - ds_absolute_maximum, - ds_interrupt_threshold, -}; - -enum ds_qualifier { - ds_bts = 0, - ds_pebs -}; - -static inline unsigned long -ds_get(const unsigned char *base, enum ds_qualifier qual, enum ds_field field) -{ - base += (ds_cfg.sizeof_ptr_field * (field + (4 * qual))); - return *(unsigned long *)base; -} - -static inline void -ds_set(unsigned char *base, enum ds_qualifier qual, enum ds_field field, - unsigned long value) -{ - base += (ds_cfg.sizeof_ptr_field * (field + (4 * qual))); - (*(unsigned long *)base) = value; -} - - -/* - * Locking is done only for allocating BTS or PEBS resources. - */ -static DEFINE_SPINLOCK(ds_lock); - -/* - * We either support (system-wide) per-cpu or per-thread allocation. - * We distinguish the two based on the task_struct pointer, where a - * NULL pointer indicates per-cpu allocation for the current cpu. - * - * Allocations are use-counted. As soon as resources are allocated, - * further allocations must be of the same type (per-cpu or - * per-thread). We model this by counting allocations (i.e. the number - * of tracers of a certain type) for one type negatively: - * =0 no tracers - * >0 number of per-thread tracers - * <0 number of per-cpu tracers - * - * Tracers essentially gives the number of ds contexts for a certain - * type of allocation. - */ -static atomic_t tracers = ATOMIC_INIT(0); - -static inline int get_tracer(struct task_struct *task) -{ - int error; - - spin_lock_irq(&ds_lock); - - if (task) { - error = -EPERM; - if (atomic_read(&tracers) < 0) - goto out; - atomic_inc(&tracers); - } else { - error = -EPERM; - if (atomic_read(&tracers) > 0) - goto out; - atomic_dec(&tracers); - } - - error = 0; -out: - spin_unlock_irq(&ds_lock); - return error; -} - -static inline void put_tracer(struct task_struct *task) -{ - if (task) - atomic_dec(&tracers); - else - atomic_inc(&tracers); -} - -/* - * The DS context is either attached to a thread or to a cpu: - * - in the former case, the thread_struct contains a pointer to the - * attached context. - * - in the latter case, we use a static array of per-cpu context - * pointers. - * - * Contexts are use-counted. They are allocated on first access and - * deallocated when the last user puts the context. - */ -struct ds_context { - /* The DS configuration; goes into MSR_IA32_DS_AREA: */ - unsigned char ds[MAX_SIZEOF_DS]; - - /* The owner of the BTS and PEBS configuration, respectively: */ - struct bts_tracer *bts_master; - struct pebs_tracer *pebs_master; - - /* Use count: */ - unsigned long count; - - /* Pointer to the context pointer field: */ - struct ds_context **this; - - /* The traced task; NULL for cpu tracing: */ - struct task_struct *task; - - /* The traced cpu; only valid if task is NULL: */ - int cpu; -}; - -static DEFINE_PER_CPU(struct ds_context *, cpu_ds_context); - - -static struct ds_context *ds_get_context(struct task_struct *task, int cpu) -{ - struct ds_context **p_context = - (task ? &task->thread.ds_ctx : &per_cpu(cpu_ds_context, cpu)); - struct ds_context *context = NULL; - struct ds_context *new_context = NULL; - - /* Chances are small that we already have a context. */ - new_context = kzalloc(sizeof(*new_context), GFP_KERNEL); - if (!new_context) - return NULL; - - spin_lock_irq(&ds_lock); - - context = *p_context; - if (likely(!context)) { - context = new_context; - - context->this = p_context; - context->task = task; - context->cpu = cpu; - context->count = 0; - - *p_context = context; - } - - context->count++; - - spin_unlock_irq(&ds_lock); - - if (context != new_context) - kfree(new_context); - - return context; -} - -static void ds_put_context(struct ds_context *context) -{ - struct task_struct *task; - unsigned long irq; - - if (!context) - return; - - spin_lock_irqsave(&ds_lock, irq); - - if (--context->count) { - spin_unlock_irqrestore(&ds_lock, irq); - return; - } - - *(context->this) = NULL; - - task = context->task; - - if (task) - clear_tsk_thread_flag(task, TIF_DS_AREA_MSR); - - /* - * We leave the (now dangling) pointer to the DS configuration in - * the DS_AREA msr. This is as good or as bad as replacing it with - * NULL - the hardware would crash if we enabled tracing. - * - * This saves us some problems with having to write an msr on a - * different cpu while preventing others from doing the same for the - * next context for that same cpu. - */ - - spin_unlock_irqrestore(&ds_lock, irq); - - /* The context might still be in use for context switching. */ - if (task && (task != current)) - wait_task_context_switch(task); - - kfree(context); -} - -static void ds_install_ds_area(struct ds_context *context) -{ - unsigned long ds; - - ds = (unsigned long)context->ds; - - /* - * There is a race between the bts master and the pebs master. - * - * The thread/cpu access is synchronized via get/put_cpu() for - * task tracing and via wrmsr_on_cpu for cpu tracing. - * - * If bts and pebs are collected for the same task or same cpu, - * the same confiuration is written twice. - */ - if (context->task) { - get_cpu(); - if (context->task == current) - wrmsrl(MSR_IA32_DS_AREA, ds); - set_tsk_thread_flag(context->task, TIF_DS_AREA_MSR); - put_cpu(); - } else - wrmsr_on_cpu(context->cpu, MSR_IA32_DS_AREA, - (u32)((u64)ds), (u32)((u64)ds >> 32)); -} - -/* - * Call the tracer's callback on a buffer overflow. - * - * context: the ds context - * qual: the buffer type - */ -static void ds_overflow(struct ds_context *context, enum ds_qualifier qual) -{ - switch (qual) { - case ds_bts: - if (context->bts_master && - context->bts_master->ovfl) - context->bts_master->ovfl(context->bts_master); - break; - case ds_pebs: - if (context->pebs_master && - context->pebs_master->ovfl) - context->pebs_master->ovfl(context->pebs_master); - break; - } -} - - -/* - * Write raw data into the BTS or PEBS buffer. - * - * The remainder of any partially written record is zeroed out. - * - * context: the DS context - * qual: the buffer type - * record: the data to write - * size: the size of the data - */ -static int ds_write(struct ds_context *context, enum ds_qualifier qual, - const void *record, size_t size) -{ - int bytes_written = 0; - - if (!record) - return -EINVAL; - - while (size) { - unsigned long base, index, end, write_end, int_th; - unsigned long write_size, adj_write_size; - - /* - * Write as much as possible without producing an - * overflow interrupt. - * - * Interrupt_threshold must either be - * - bigger than absolute_maximum or - * - point to a record between buffer_base and absolute_maximum - * - * Index points to a valid record. - */ - base = ds_get(context->ds, qual, ds_buffer_base); - index = ds_get(context->ds, qual, ds_index); - end = ds_get(context->ds, qual, ds_absolute_maximum); - int_th = ds_get(context->ds, qual, ds_interrupt_threshold); - - write_end = min(end, int_th); - - /* - * If we are already beyond the interrupt threshold, - * we fill the entire buffer. - */ - if (write_end <= index) - write_end = end; - - if (write_end <= index) - break; - - write_size = min((unsigned long) size, write_end - index); - memcpy((void *)index, record, write_size); - - record = (const char *)record + write_size; - size -= write_size; - bytes_written += write_size; - - adj_write_size = write_size / ds_cfg.sizeof_rec[qual]; - adj_write_size *= ds_cfg.sizeof_rec[qual]; - - /* Zero out trailing bytes. */ - memset((char *)index + write_size, 0, - adj_write_size - write_size); - index += adj_write_size; - - if (index >= end) - index = base; - ds_set(context->ds, qual, ds_index, index); - - if (index >= int_th) - ds_overflow(context, qual); - } - - return bytes_written; -} - - -/* - * Branch Trace Store (BTS) uses the following format. Different - * architectures vary in the size of those fields. - * - source linear address - * - destination linear address - * - flags - * - * Later architectures use 64bit pointers throughout, whereas earlier - * architectures use 32bit pointers in 32bit mode. - * - * We compute the base address for the fields based on: - * - the field size stored in the DS configuration - * - the relative field position - * - * In order to store additional information in the BTS buffer, we use - * a special source address to indicate that the record requires - * special interpretation. - * - * Netburst indicated via a bit in the flags field whether the branch - * was predicted; this is ignored. - * - * We use two levels of abstraction: - * - the raw data level defined here - * - an arch-independent level defined in ds.h - */ - -enum bts_field { - bts_from, - bts_to, - bts_flags, - - bts_qual = bts_from, - bts_clock = bts_to, - bts_pid = bts_flags, - - bts_qual_mask = (bts_qual_max - 1), - bts_escape = ((unsigned long)-1 & ~bts_qual_mask) -}; - -static inline unsigned long bts_get(const char *base, unsigned long field) -{ - base += (ds_cfg.sizeof_ptr_field * field); - return *(unsigned long *)base; -} - -static inline void bts_set(char *base, unsigned long field, unsigned long val) -{ - base += (ds_cfg.sizeof_ptr_field * field); - (*(unsigned long *)base) = val; -} - - -/* - * The raw BTS data is architecture dependent. - * - * For higher-level users, we give an arch-independent view. - * - ds.h defines struct bts_struct - * - bts_read translates one raw bts record into a bts_struct - * - bts_write translates one bts_struct into the raw format and - * writes it into the top of the parameter tracer's buffer. - * - * return: bytes read/written on success; -Eerrno, otherwise - */ -static int -bts_read(struct bts_tracer *tracer, const void *at, struct bts_struct *out) -{ - if (!tracer) - return -EINVAL; - - if (at < tracer->trace.ds.begin) - return -EINVAL; - - if (tracer->trace.ds.end < (at + tracer->trace.ds.size)) - return -EINVAL; - - memset(out, 0, sizeof(*out)); - if ((bts_get(at, bts_qual) & ~bts_qual_mask) == bts_escape) { - out->qualifier = (bts_get(at, bts_qual) & bts_qual_mask); - out->variant.event.clock = bts_get(at, bts_clock); - out->variant.event.pid = bts_get(at, bts_pid); - } else { - out->qualifier = bts_branch; - out->variant.lbr.from = bts_get(at, bts_from); - out->variant.lbr.to = bts_get(at, bts_to); - - if (!out->variant.lbr.from && !out->variant.lbr.to) - out->qualifier = bts_invalid; - } - - return ds_cfg.sizeof_rec[ds_bts]; -} - -static int bts_write(struct bts_tracer *tracer, const struct bts_struct *in) -{ - unsigned char raw[MAX_SIZEOF_BTS]; - - if (!tracer) - return -EINVAL; - - if (MAX_SIZEOF_BTS < ds_cfg.sizeof_rec[ds_bts]) - return -EOVERFLOW; - - switch (in->qualifier) { - case bts_invalid: - bts_set(raw, bts_from, 0); - bts_set(raw, bts_to, 0); - bts_set(raw, bts_flags, 0); - break; - case bts_branch: - bts_set(raw, bts_from, in->variant.lbr.from); - bts_set(raw, bts_to, in->variant.lbr.to); - bts_set(raw, bts_flags, 0); - break; - case bts_task_arrives: - case bts_task_departs: - bts_set(raw, bts_qual, (bts_escape | in->qualifier)); - bts_set(raw, bts_clock, in->variant.event.clock); - bts_set(raw, bts_pid, in->variant.event.pid); - break; - default: - return -EINVAL; - } - - return ds_write(tracer->ds.context, ds_bts, raw, - ds_cfg.sizeof_rec[ds_bts]); -} - - -static void ds_write_config(struct ds_context *context, - struct ds_trace *cfg, enum ds_qualifier qual) -{ - unsigned char *ds = context->ds; - - ds_set(ds, qual, ds_buffer_base, (unsigned long)cfg->begin); - ds_set(ds, qual, ds_index, (unsigned long)cfg->top); - ds_set(ds, qual, ds_absolute_maximum, (unsigned long)cfg->end); - ds_set(ds, qual, ds_interrupt_threshold, (unsigned long)cfg->ith); -} - -static void ds_read_config(struct ds_context *context, - struct ds_trace *cfg, enum ds_qualifier qual) -{ - unsigned char *ds = context->ds; - - cfg->begin = (void *)ds_get(ds, qual, ds_buffer_base); - cfg->top = (void *)ds_get(ds, qual, ds_index); - cfg->end = (void *)ds_get(ds, qual, ds_absolute_maximum); - cfg->ith = (void *)ds_get(ds, qual, ds_interrupt_threshold); -} - -static void ds_init_ds_trace(struct ds_trace *trace, enum ds_qualifier qual, - void *base, size_t size, size_t ith, - unsigned int flags) { - unsigned long buffer, adj; - - /* - * Adjust the buffer address and size to meet alignment - * constraints: - * - buffer is double-word aligned - * - size is multiple of record size - * - * We checked the size at the very beginning; we have enough - * space to do the adjustment. - */ - buffer = (unsigned long)base; - - adj = ALIGN(buffer, DS_ALIGNMENT) - buffer; - buffer += adj; - size -= adj; - - trace->n = size / ds_cfg.sizeof_rec[qual]; - trace->size = ds_cfg.sizeof_rec[qual]; - - size = (trace->n * trace->size); - - trace->begin = (void *)buffer; - trace->top = trace->begin; - trace->end = (void *)(buffer + size); - /* - * The value for 'no threshold' is -1, which will set the - * threshold outside of the buffer, just like we want it. - */ - ith *= ds_cfg.sizeof_rec[qual]; - trace->ith = (void *)(buffer + size - ith); - - trace->flags = flags; -} - - -static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace, - enum ds_qualifier qual, struct task_struct *task, - int cpu, void *base, size_t size, size_t th) -{ - struct ds_context *context; - int error; - size_t req_size; - - error = -EOPNOTSUPP; - if (!ds_cfg.sizeof_rec[qual]) - goto out; - - error = -EINVAL; - if (!base) - goto out; - - req_size = ds_cfg.sizeof_rec[qual]; - /* We might need space for alignment adjustments. */ - if (!IS_ALIGNED((unsigned long)base, DS_ALIGNMENT)) - req_size += DS_ALIGNMENT; - - error = -EINVAL; - if (size < req_size) - goto out; - - if (th != (size_t)-1) { - th *= ds_cfg.sizeof_rec[qual]; - - error = -EINVAL; - if (size <= th) - goto out; - } - - tracer->buffer = base; - tracer->size = size; - - error = -ENOMEM; - context = ds_get_context(task, cpu); - if (!context) - goto out; - tracer->context = context; - - /* - * Defer any tracer-specific initialization work for the context until - * context ownership has been clarified. - */ - - error = 0; - out: - return error; -} - -static struct bts_tracer *ds_request_bts(struct task_struct *task, int cpu, - void *base, size_t size, - bts_ovfl_callback_t ovfl, size_t th, - unsigned int flags) -{ - struct bts_tracer *tracer; - int error; - - /* Buffer overflow notification is not yet implemented. */ - error = -EOPNOTSUPP; - if (ovfl) - goto out; - - error = get_tracer(task); - if (error < 0) - goto out; - - error = -ENOMEM; - tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); - if (!tracer) - goto out_put_tracer; - tracer->ovfl = ovfl; - - /* Do some more error checking and acquire a tracing context. */ - error = ds_request(&tracer->ds, &tracer->trace.ds, - ds_bts, task, cpu, base, size, th); - if (error < 0) - goto out_tracer; - - /* Claim the bts part of the tracing context we acquired above. */ - spin_lock_irq(&ds_lock); - - error = -EPERM; - if (tracer->ds.context->bts_master) - goto out_unlock; - tracer->ds.context->bts_master = tracer; - - spin_unlock_irq(&ds_lock); - - /* - * Now that we own the bts part of the context, let's complete the - * initialization for that part. - */ - ds_init_ds_trace(&tracer->trace.ds, ds_bts, base, size, th, flags); - ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts); - ds_install_ds_area(tracer->ds.context); - - tracer->trace.read = bts_read; - tracer->trace.write = bts_write; - - /* Start tracing. */ - ds_resume_bts(tracer); - - return tracer; - - out_unlock: - spin_unlock_irq(&ds_lock); - ds_put_context(tracer->ds.context); - out_tracer: - kfree(tracer); - out_put_tracer: - put_tracer(task); - out: - return ERR_PTR(error); -} - -struct bts_tracer *ds_request_bts_task(struct task_struct *task, - void *base, size_t size, - bts_ovfl_callback_t ovfl, - size_t th, unsigned int flags) -{ - return ds_request_bts(task, 0, base, size, ovfl, th, flags); -} - -struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size, - bts_ovfl_callback_t ovfl, - size_t th, unsigned int flags) -{ - return ds_request_bts(NULL, cpu, base, size, ovfl, th, flags); -} - -static struct pebs_tracer *ds_request_pebs(struct task_struct *task, int cpu, - void *base, size_t size, - pebs_ovfl_callback_t ovfl, size_t th, - unsigned int flags) -{ - struct pebs_tracer *tracer; - int error; - - /* Buffer overflow notification is not yet implemented. */ - error = -EOPNOTSUPP; - if (ovfl) - goto out; - - error = get_tracer(task); - if (error < 0) - goto out; - - error = -ENOMEM; - tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); - if (!tracer) - goto out_put_tracer; - tracer->ovfl = ovfl; - - /* Do some more error checking and acquire a tracing context. */ - error = ds_request(&tracer->ds, &tracer->trace.ds, - ds_pebs, task, cpu, base, size, th); - if (error < 0) - goto out_tracer; - - /* Claim the pebs part of the tracing context we acquired above. */ - spin_lock_irq(&ds_lock); - - error = -EPERM; - if (tracer->ds.context->pebs_master) - goto out_unlock; - tracer->ds.context->pebs_master = tracer; - - spin_unlock_irq(&ds_lock); - - /* - * Now that we own the pebs part of the context, let's complete the - * initialization for that part. - */ - ds_init_ds_trace(&tracer->trace.ds, ds_pebs, base, size, th, flags); - ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); - ds_install_ds_area(tracer->ds.context); - - /* Start tracing. */ - ds_resume_pebs(tracer); - - return tracer; - - out_unlock: - spin_unlock_irq(&ds_lock); - ds_put_context(tracer->ds.context); - out_tracer: - kfree(tracer); - out_put_tracer: - put_tracer(task); - out: - return ERR_PTR(error); -} - -struct pebs_tracer *ds_request_pebs_task(struct task_struct *task, - void *base, size_t size, - pebs_ovfl_callback_t ovfl, - size_t th, unsigned int flags) -{ - return ds_request_pebs(task, 0, base, size, ovfl, th, flags); -} - -struct pebs_tracer *ds_request_pebs_cpu(int cpu, void *base, size_t size, - pebs_ovfl_callback_t ovfl, - size_t th, unsigned int flags) -{ - return ds_request_pebs(NULL, cpu, base, size, ovfl, th, flags); -} - -static void ds_free_bts(struct bts_tracer *tracer) -{ - struct task_struct *task; - - task = tracer->ds.context->task; - - WARN_ON_ONCE(tracer->ds.context->bts_master != tracer); - tracer->ds.context->bts_master = NULL; - - /* Make sure tracing stopped and the tracer is not in use. */ - if (task && (task != current)) - wait_task_context_switch(task); - - ds_put_context(tracer->ds.context); - put_tracer(task); - - kfree(tracer); -} - -void ds_release_bts(struct bts_tracer *tracer) -{ - might_sleep(); - - if (!tracer) - return; - - ds_suspend_bts(tracer); - ds_free_bts(tracer); -} - -int ds_release_bts_noirq(struct bts_tracer *tracer) -{ - struct task_struct *task; - unsigned long irq; - int error; - - if (!tracer) - return 0; - - task = tracer->ds.context->task; - - local_irq_save(irq); - - error = -EPERM; - if (!task && - (tracer->ds.context->cpu != smp_processor_id())) - goto out; - - error = -EPERM; - if (task && (task != current)) - goto out; - - ds_suspend_bts_noirq(tracer); - ds_free_bts(tracer); - - error = 0; - out: - local_irq_restore(irq); - return error; -} - -static void update_task_debugctlmsr(struct task_struct *task, - unsigned long debugctlmsr) -{ - task->thread.debugctlmsr = debugctlmsr; - - get_cpu(); - if (task == current) - update_debugctlmsr(debugctlmsr); - put_cpu(); -} - -void ds_suspend_bts(struct bts_tracer *tracer) -{ - struct task_struct *task; - unsigned long debugctlmsr; - int cpu; - - if (!tracer) - return; - - tracer->flags = 0; - - task = tracer->ds.context->task; - cpu = tracer->ds.context->cpu; - - WARN_ON(!task && irqs_disabled()); - - debugctlmsr = (task ? - task->thread.debugctlmsr : - get_debugctlmsr_on_cpu(cpu)); - debugctlmsr &= ~BTS_CONTROL; - - if (task) - update_task_debugctlmsr(task, debugctlmsr); - else - update_debugctlmsr_on_cpu(cpu, debugctlmsr); -} - -int ds_suspend_bts_noirq(struct bts_tracer *tracer) -{ - struct task_struct *task; - unsigned long debugctlmsr, irq; - int cpu, error = 0; - - if (!tracer) - return 0; - - tracer->flags = 0; - - task = tracer->ds.context->task; - cpu = tracer->ds.context->cpu; - - local_irq_save(irq); - - error = -EPERM; - if (!task && (cpu != smp_processor_id())) - goto out; - - debugctlmsr = (task ? - task->thread.debugctlmsr : - get_debugctlmsr()); - debugctlmsr &= ~BTS_CONTROL; - - if (task) - update_task_debugctlmsr(task, debugctlmsr); - else - update_debugctlmsr(debugctlmsr); - - error = 0; - out: - local_irq_restore(irq); - return error; -} - -static unsigned long ds_bts_control(struct bts_tracer *tracer) -{ - unsigned long control; - - control = ds_cfg.ctl[dsf_bts]; - if (!(tracer->trace.ds.flags & BTS_KERNEL)) - control |= ds_cfg.ctl[dsf_bts_kernel]; - if (!(tracer->trace.ds.flags & BTS_USER)) - control |= ds_cfg.ctl[dsf_bts_user]; - - return control; -} - -void ds_resume_bts(struct bts_tracer *tracer) -{ - struct task_struct *task; - unsigned long debugctlmsr; - int cpu; - - if (!tracer) - return; - - tracer->flags = tracer->trace.ds.flags; - - task = tracer->ds.context->task; - cpu = tracer->ds.context->cpu; - - WARN_ON(!task && irqs_disabled()); - - debugctlmsr = (task ? - task->thread.debugctlmsr : - get_debugctlmsr_on_cpu(cpu)); - debugctlmsr |= ds_bts_control(tracer); - - if (task) - update_task_debugctlmsr(task, debugctlmsr); - else - update_debugctlmsr_on_cpu(cpu, debugctlmsr); -} - -int ds_resume_bts_noirq(struct bts_tracer *tracer) -{ - struct task_struct *task; - unsigned long debugctlmsr, irq; - int cpu, error = 0; - - if (!tracer) - return 0; - - tracer->flags = tracer->trace.ds.flags; - - task = tracer->ds.context->task; - cpu = tracer->ds.context->cpu; - - local_irq_save(irq); - - error = -EPERM; - if (!task && (cpu != smp_processor_id())) - goto out; - - debugctlmsr = (task ? - task->thread.debugctlmsr : - get_debugctlmsr()); - debugctlmsr |= ds_bts_control(tracer); - - if (task) - update_task_debugctlmsr(task, debugctlmsr); - else - update_debugctlmsr(debugctlmsr); - - error = 0; - out: - local_irq_restore(irq); - return error; -} - -static void ds_free_pebs(struct pebs_tracer *tracer) -{ - struct task_struct *task; - - task = tracer->ds.context->task; - - WARN_ON_ONCE(tracer->ds.context->pebs_master != tracer); - tracer->ds.context->pebs_master = NULL; - - ds_put_context(tracer->ds.context); - put_tracer(task); - - kfree(tracer); -} - -void ds_release_pebs(struct pebs_tracer *tracer) -{ - might_sleep(); - - if (!tracer) - return; - - ds_suspend_pebs(tracer); - ds_free_pebs(tracer); -} - -int ds_release_pebs_noirq(struct pebs_tracer *tracer) -{ - struct task_struct *task; - unsigned long irq; - int error; - - if (!tracer) - return 0; - - task = tracer->ds.context->task; - - local_irq_save(irq); - - error = -EPERM; - if (!task && - (tracer->ds.context->cpu != smp_processor_id())) - goto out; - - error = -EPERM; - if (task && (task != current)) - goto out; - - ds_suspend_pebs_noirq(tracer); - ds_free_pebs(tracer); - - error = 0; - out: - local_irq_restore(irq); - return error; -} - -void ds_suspend_pebs(struct pebs_tracer *tracer) -{ - -} - -int ds_suspend_pebs_noirq(struct pebs_tracer *tracer) -{ - return 0; -} - -void ds_resume_pebs(struct pebs_tracer *tracer) -{ - -} - -int ds_resume_pebs_noirq(struct pebs_tracer *tracer) -{ - return 0; -} - -const struct bts_trace *ds_read_bts(struct bts_tracer *tracer) -{ - if (!tracer) - return NULL; - - ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_bts); - return &tracer->trace; -} - -const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer) -{ - if (!tracer) - return NULL; - - ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs); - - tracer->trace.counters = ds_cfg.nr_counter_reset; - memcpy(tracer->trace.counter_reset, - tracer->ds.context->ds + - (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field), - ds_cfg.nr_counter_reset * PEBS_RESET_FIELD_SIZE); - - return &tracer->trace; -} - -int ds_reset_bts(struct bts_tracer *tracer) -{ - if (!tracer) - return -EINVAL; - - tracer->trace.ds.top = tracer->trace.ds.begin; - - ds_set(tracer->ds.context->ds, ds_bts, ds_index, - (unsigned long)tracer->trace.ds.top); - - return 0; -} - -int ds_reset_pebs(struct pebs_tracer *tracer) -{ - if (!tracer) - return -EINVAL; - - tracer->trace.ds.top = tracer->trace.ds.begin; - - ds_set(tracer->ds.context->ds, ds_pebs, ds_index, - (unsigned long)tracer->trace.ds.top); - - return 0; -} - -int ds_set_pebs_reset(struct pebs_tracer *tracer, - unsigned int counter, u64 value) -{ - if (!tracer) - return -EINVAL; - - if (ds_cfg.nr_counter_reset < counter) - return -EINVAL; - - *(u64 *)(tracer->ds.context->ds + - (NUM_DS_PTR_FIELDS * ds_cfg.sizeof_ptr_field) + - (counter * PEBS_RESET_FIELD_SIZE)) = value; - - return 0; -} - -static const struct ds_configuration ds_cfg_netburst = { - .name = "Netburst", - .ctl[dsf_bts] = (1 << 2) | (1 << 3), - .ctl[dsf_bts_kernel] = (1 << 5), - .ctl[dsf_bts_user] = (1 << 6), - .nr_counter_reset = 1, -}; -static const struct ds_configuration ds_cfg_pentium_m = { - .name = "Pentium M", - .ctl[dsf_bts] = (1 << 6) | (1 << 7), - .nr_counter_reset = 1, -}; -static const struct ds_configuration ds_cfg_core2_atom = { - .name = "Core 2/Atom", - .ctl[dsf_bts] = (1 << 6) | (1 << 7), - .ctl[dsf_bts_kernel] = (1 << 9), - .ctl[dsf_bts_user] = (1 << 10), - .nr_counter_reset = 1, -}; -static const struct ds_configuration ds_cfg_core_i7 = { - .name = "Core i7", - .ctl[dsf_bts] = (1 << 6) | (1 << 7), - .ctl[dsf_bts_kernel] = (1 << 9), - .ctl[dsf_bts_user] = (1 << 10), - .nr_counter_reset = 4, -}; - -static void -ds_configure(const struct ds_configuration *cfg, - struct cpuinfo_x86 *cpu) -{ - unsigned long nr_pebs_fields = 0; - - printk(KERN_INFO "[ds] using %s configuration\n", cfg->name); - -#ifdef __i386__ - nr_pebs_fields = 10; -#else - nr_pebs_fields = 18; -#endif - - /* - * Starting with version 2, architectural performance - * monitoring supports a format specifier. - */ - if ((cpuid_eax(0xa) & 0xff) > 1) { - unsigned long perf_capabilities, format; - - rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_capabilities); - - format = (perf_capabilities >> 8) & 0xf; - - switch (format) { - case 0: - nr_pebs_fields = 18; - break; - case 1: - nr_pebs_fields = 22; - break; - default: - printk(KERN_INFO - "[ds] unknown PEBS format: %lu\n", format); - nr_pebs_fields = 0; - break; - } - } - - memset(&ds_cfg, 0, sizeof(ds_cfg)); - ds_cfg = *cfg; - - ds_cfg.sizeof_ptr_field = - (cpu_has(cpu, X86_FEATURE_DTES64) ? 8 : 4); - - ds_cfg.sizeof_rec[ds_bts] = ds_cfg.sizeof_ptr_field * 3; - ds_cfg.sizeof_rec[ds_pebs] = ds_cfg.sizeof_ptr_field * nr_pebs_fields; - - if (!cpu_has(cpu, X86_FEATURE_BTS)) { - ds_cfg.sizeof_rec[ds_bts] = 0; - printk(KERN_INFO "[ds] bts not available\n"); - } - if (!cpu_has(cpu, X86_FEATURE_PEBS)) { - ds_cfg.sizeof_rec[ds_pebs] = 0; - printk(KERN_INFO "[ds] pebs not available\n"); - } - - printk(KERN_INFO "[ds] sizes: address: %u bit, ", - 8 * ds_cfg.sizeof_ptr_field); - printk("bts/pebs record: %u/%u bytes\n", - ds_cfg.sizeof_rec[ds_bts], ds_cfg.sizeof_rec[ds_pebs]); - - WARN_ON_ONCE(MAX_PEBS_COUNTERS < ds_cfg.nr_counter_reset); -} - -void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) -{ - /* Only configure the first cpu. Others are identical. */ - if (ds_cfg.name) - return; - - switch (c->x86) { - case 0x6: - switch (c->x86_model) { - case 0x9: - case 0xd: /* Pentium M */ - ds_configure(&ds_cfg_pentium_m, c); - break; - case 0xf: - case 0x17: /* Core2 */ - case 0x1c: /* Atom */ - ds_configure(&ds_cfg_core2_atom, c); - break; - case 0x1a: /* Core i7 */ - ds_configure(&ds_cfg_core_i7, c); - break; - default: - /* Sorry, don't know about them. */ - break; - } - break; - case 0xf: - switch (c->x86_model) { - case 0x0: - case 0x1: - case 0x2: /* Netburst */ - ds_configure(&ds_cfg_netburst, c); - break; - default: - /* Sorry, don't know about them. */ - break; - } - break; - default: - /* Sorry, don't know about them. */ - break; - } -} - -static inline void ds_take_timestamp(struct ds_context *context, - enum bts_qualifier qualifier, - struct task_struct *task) -{ - struct bts_tracer *tracer = context->bts_master; - struct bts_struct ts; - - /* Prevent compilers from reading the tracer pointer twice. */ - barrier(); - - if (!tracer || !(tracer->flags & BTS_TIMESTAMPS)) - return; - - memset(&ts, 0, sizeof(ts)); - ts.qualifier = qualifier; - ts.variant.event.clock = trace_clock_global(); - ts.variant.event.pid = task->pid; - - bts_write(tracer, &ts); -} - -/* - * Change the DS configuration from tracing prev to tracing next. - */ -void ds_switch_to(struct task_struct *prev, struct task_struct *next) -{ - struct ds_context *prev_ctx = prev->thread.ds_ctx; - struct ds_context *next_ctx = next->thread.ds_ctx; - unsigned long debugctlmsr = next->thread.debugctlmsr; - - /* Make sure all data is read before we start. */ - barrier(); - - if (prev_ctx) { - update_debugctlmsr(0); - - ds_take_timestamp(prev_ctx, bts_task_departs, prev); - } - - if (next_ctx) { - ds_take_timestamp(next_ctx, bts_task_arrives, next); - - wrmsrl(MSR_IA32_DS_AREA, (unsigned long)next_ctx->ds); - } - - update_debugctlmsr(debugctlmsr); -} - -static __init int ds_selftest(void) -{ - if (ds_cfg.sizeof_rec[ds_bts]) { - int error; - - error = ds_selftest_bts(); - if (error) { - WARN(1, "[ds] selftest failed. disabling bts.\n"); - ds_cfg.sizeof_rec[ds_bts] = 0; - } - } - - if (ds_cfg.sizeof_rec[ds_pebs]) { - int error; - - error = ds_selftest_pebs(); - if (error) { - WARN(1, "[ds] selftest failed. disabling pebs.\n"); - ds_cfg.sizeof_rec[ds_pebs] = 0; - } - } - - return 0; -} -device_initcall(ds_selftest); diff --git a/arch/x86/kernel/ds_selftest.c b/arch/x86/kernel/ds_selftest.c deleted file mode 100644 index 6bc7c19..0000000 --- a/arch/x86/kernel/ds_selftest.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Debug Store support - selftest - * - * - * Copyright (C) 2009 Intel Corporation. - * Markus Metzger , 2009 - */ - -#include "ds_selftest.h" - -#include -#include -#include -#include - -#include - - -#define BUFFER_SIZE 521 /* Intentionally chose an odd size. */ -#define SMALL_BUFFER_SIZE 24 /* A single bts entry. */ - -struct ds_selftest_bts_conf { - struct bts_tracer *tracer; - int error; - int (*suspend)(struct bts_tracer *); - int (*resume)(struct bts_tracer *); -}; - -static int ds_selftest_bts_consistency(const struct bts_trace *trace) -{ - int error = 0; - - if (!trace) { - printk(KERN_CONT "failed to access trace..."); - /* Bail out. Other tests are pointless. */ - return -1; - } - - if (!trace->read) { - printk(KERN_CONT "bts read not available..."); - error = -1; - } - - /* Do some sanity checks on the trace configuration. */ - if (!trace->ds.n) { - printk(KERN_CONT "empty bts buffer..."); - error = -1; - } - if (!trace->ds.size) { - printk(KERN_CONT "bad bts trace setup..."); - error = -1; - } - if (trace->ds.end != - (char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) { - printk(KERN_CONT "bad bts buffer setup..."); - error = -1; - } - /* - * We allow top in [begin; end], since its not clear when the - * overflow adjustment happens: after the increment or before the - * write. - */ - if ((trace->ds.top < trace->ds.begin) || - (trace->ds.end < trace->ds.top)) { - printk(KERN_CONT "bts top out of bounds..."); - error = -1; - } - - return error; -} - -static int ds_selftest_bts_read(struct bts_tracer *tracer, - const struct bts_trace *trace, - const void *from, const void *to) -{ - const unsigned char *at; - - /* - * Check a few things which do not belong to this test. - * They should be covered by other tests. - */ - if (!trace) - return -1; - - if (!trace->read) - return -1; - - if (to < from) - return -1; - - if (from < trace->ds.begin) - return -1; - - if (trace->ds.end < to) - return -1; - - if (!trace->ds.size) - return -1; - - /* Now to the test itself. */ - for (at = from; (void *)at < to; at += trace->ds.size) { - struct bts_struct bts; - unsigned long index; - int error; - - if (((void *)at - trace->ds.begin) % trace->ds.size) { - printk(KERN_CONT - "read from non-integer index..."); - return -1; - } - index = ((void *)at - trace->ds.begin) / trace->ds.size; - - memset(&bts, 0, sizeof(bts)); - error = trace->read(tracer, at, &bts); - if (error < 0) { - printk(KERN_CONT - "error reading bts trace at [%lu] (0x%p)...", - index, at); - return error; - } - - switch (bts.qualifier) { - case BTS_BRANCH: - break; - default: - printk(KERN_CONT - "unexpected bts entry %llu at [%lu] (0x%p)...", - bts.qualifier, index, at); - return -1; - } - } - - return 0; -} - -static void ds_selftest_bts_cpu(void *arg) -{ - struct ds_selftest_bts_conf *conf = arg; - const struct bts_trace *trace; - void *top; - - if (IS_ERR(conf->tracer)) { - conf->error = PTR_ERR(conf->tracer); - conf->tracer = NULL; - - printk(KERN_CONT - "initialization failed (err: %d)...", conf->error); - return; - } - - /* We should meanwhile have enough trace. */ - conf->error = conf->suspend(conf->tracer); - if (conf->error < 0) - return; - - /* Let's see if we can access the trace. */ - trace = ds_read_bts(conf->tracer); - - conf->error = ds_selftest_bts_consistency(trace); - if (conf->error < 0) - return; - - /* If everything went well, we should have a few trace entries. */ - if (trace->ds.top == trace->ds.begin) { - /* - * It is possible but highly unlikely that we got a - * buffer overflow and end up at exactly the same - * position we started from. - * Let's issue a warning, but continue. - */ - printk(KERN_CONT "no trace/overflow..."); - } - - /* Let's try to read the trace we collected. */ - conf->error = - ds_selftest_bts_read(conf->tracer, trace, - trace->ds.begin, trace->ds.top); - if (conf->error < 0) - return; - - /* - * Let's read the trace again. - * Since we suspended tracing, we should get the same result. - */ - top = trace->ds.top; - - trace = ds_read_bts(conf->tracer); - conf->error = ds_selftest_bts_consistency(trace); - if (conf->error < 0) - return; - - if (top != trace->ds.top) { - printk(KERN_CONT "suspend not working..."); - conf->error = -1; - return; - } - - /* Let's collect some more trace - see if resume is working. */ - conf->error = conf->resume(conf->tracer); - if (conf->error < 0) - return; - - conf->error = conf->suspend(conf->tracer); - if (conf->error < 0) - return; - - trace = ds_read_bts(conf->tracer); - - conf->error = ds_selftest_bts_consistency(trace); - if (conf->error < 0) - return; - - if (trace->ds.top == top) { - /* - * It is possible but highly unlikely that we got a - * buffer overflow and end up at exactly the same - * position we started from. - * Let's issue a warning and check the full trace. - */ - printk(KERN_CONT - "no resume progress/overflow..."); - - conf->error = - ds_selftest_bts_read(conf->tracer, trace, - trace->ds.begin, trace->ds.end); - } else if (trace->ds.top < top) { - /* - * We had a buffer overflow - the entire buffer should - * contain trace records. - */ - conf->error = - ds_selftest_bts_read(conf->tracer, trace, - trace->ds.begin, trace->ds.end); - } else { - /* - * It is quite likely that the buffer did not overflow. - * Let's just check the delta trace. - */ - conf->error = - ds_selftest_bts_read(conf->tracer, trace, top, - trace->ds.top); - } - if (conf->error < 0) - return; - - conf->error = 0; -} - -static int ds_suspend_bts_wrap(struct bts_tracer *tracer) -{ - ds_suspend_bts(tracer); - return 0; -} - -static int ds_resume_bts_wrap(struct bts_tracer *tracer) -{ - ds_resume_bts(tracer); - return 0; -} - -static void ds_release_bts_noirq_wrap(void *tracer) -{ - (void)ds_release_bts_noirq(tracer); -} - -static int ds_selftest_bts_bad_release_noirq(int cpu, - struct bts_tracer *tracer) -{ - int error = -EPERM; - - /* Try to release the tracer on the wrong cpu. */ - get_cpu(); - if (cpu != smp_processor_id()) { - error = ds_release_bts_noirq(tracer); - if (error != -EPERM) - printk(KERN_CONT "release on wrong cpu..."); - } - put_cpu(); - - return error ? 0 : -1; -} - -static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer) -{ - struct bts_tracer *tracer; - int error; - - /* Try to request cpu tracing while task tracing is active. */ - tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL, - (size_t)-1, BTS_KERNEL); - error = PTR_ERR(tracer); - if (!IS_ERR(tracer)) { - ds_release_bts(tracer); - error = 0; - } - - if (error != -EPERM) - printk(KERN_CONT "cpu/task tracing overlap..."); - - return error ? 0 : -1; -} - -static int ds_selftest_bts_bad_request_task(void *buffer) -{ - struct bts_tracer *tracer; - int error; - - /* Try to request cpu tracing while task tracing is active. */ - tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL, - (size_t)-1, BTS_KERNEL); - error = PTR_ERR(tracer); - if (!IS_ERR(tracer)) { - error = 0; - ds_release_bts(tracer); - } - - if (error != -EPERM) - printk(KERN_CONT "task/cpu tracing overlap..."); - - return error ? 0 : -1; -} - -int ds_selftest_bts(void) -{ - struct ds_selftest_bts_conf conf; - unsigned char buffer[BUFFER_SIZE], *small_buffer; - unsigned long irq; - int cpu; - - printk(KERN_INFO "[ds] bts selftest..."); - conf.error = 0; - - small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8; - - get_online_cpus(); - for_each_online_cpu(cpu) { - conf.suspend = ds_suspend_bts_wrap; - conf.resume = ds_resume_bts_wrap; - conf.tracer = - ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, - NULL, (size_t)-1, BTS_KERNEL); - ds_selftest_bts_cpu(&conf); - if (conf.error >= 0) - conf.error = ds_selftest_bts_bad_request_task(buffer); - ds_release_bts(conf.tracer); - if (conf.error < 0) - goto out; - - conf.suspend = ds_suspend_bts_noirq; - conf.resume = ds_resume_bts_noirq; - conf.tracer = - ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, - NULL, (size_t)-1, BTS_KERNEL); - smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1); - if (conf.error >= 0) { - conf.error = - ds_selftest_bts_bad_release_noirq(cpu, - conf.tracer); - /* We must not release the tracer twice. */ - if (conf.error < 0) - conf.tracer = NULL; - } - if (conf.error >= 0) - conf.error = ds_selftest_bts_bad_request_task(buffer); - smp_call_function_single(cpu, ds_release_bts_noirq_wrap, - conf.tracer, 1); - if (conf.error < 0) - goto out; - } - - conf.suspend = ds_suspend_bts_wrap; - conf.resume = ds_resume_bts_wrap; - conf.tracer = - ds_request_bts_task(current, buffer, BUFFER_SIZE, - NULL, (size_t)-1, BTS_KERNEL); - ds_selftest_bts_cpu(&conf); - if (conf.error >= 0) - conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); - ds_release_bts(conf.tracer); - if (conf.error < 0) - goto out; - - conf.suspend = ds_suspend_bts_noirq; - conf.resume = ds_resume_bts_noirq; - conf.tracer = - ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE, - NULL, (size_t)-1, BTS_KERNEL); - local_irq_save(irq); - ds_selftest_bts_cpu(&conf); - if (conf.error >= 0) - conf.error = ds_selftest_bts_bad_request_cpu(0, buffer); - ds_release_bts_noirq(conf.tracer); - local_irq_restore(irq); - if (conf.error < 0) - goto out; - - conf.error = 0; - out: - put_online_cpus(); - printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed")); - - return conf.error; -} - -int ds_selftest_pebs(void) -{ - return 0; -} diff --git a/arch/x86/kernel/ds_selftest.h b/arch/x86/kernel/ds_selftest.h deleted file mode 100644 index 2ba8745..0000000 --- a/arch/x86/kernel/ds_selftest.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Debug Store support - selftest - * - * - * Copyright (C) 2009 Intel Corporation. - * Markus Metzger , 2009 - */ - -#ifdef CONFIG_X86_DS_SELFTEST -extern int ds_selftest_bts(void); -extern int ds_selftest_pebs(void); -#else -static inline int ds_selftest_bts(void) { return 0; } -static inline int ds_selftest_pebs(void) { return 0; } -#endif diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 6d81755..c89a386 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -224,11 +224,6 @@ unsigned __kprobes long oops_begin(void) int cpu; unsigned long flags; - /* notify the hw-branch tracer so it may disable tracing and - add the last trace to the trace buffer - - the earlier this happens, the more useful the trace. */ - trace_hw_branch_oops(); - oops_enter(); /* racy, but better than risking deadlock. */ diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index b43bbae..7a880ad 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -422,14 +422,12 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, static void __kprobes clear_btf(void) { - if (test_thread_flag(TIF_DEBUGCTLMSR)) - update_debugctlmsr(0); + /* XXX */ } static void __kprobes restore_btf(void) { - if (test_thread_flag(TIF_DEBUGCTLMSR)) - update_debugctlmsr(current->thread.debugctlmsr); + /* XXX */ } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index ad95406..1a60beb 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -20,7 +20,6 @@ #include #include #include -#include #include unsigned long idle_halt; @@ -50,8 +49,6 @@ void free_thread_xstate(struct task_struct *tsk) kmem_cache_free(task_xstate_cachep, tsk->thread.xstate); tsk->thread.xstate = NULL; } - - WARN(tsk->thread.ds_ctx, "leaking DS context\n"); } void free_thread_info(struct thread_info *ti) @@ -198,12 +195,6 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, prev = &prev_p->thread; next = &next_p->thread; - if (test_tsk_thread_flag(next_p, TIF_DS_AREA_MSR) || - test_tsk_thread_flag(prev_p, TIF_DS_AREA_MSR)) - ds_switch_to(prev_p, next_p); - else if (next->debugctlmsr != prev->debugctlmsr) - update_debugctlmsr(next->debugctlmsr); - if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ test_tsk_thread_flag(next_p, TIF_NOTSC)) { /* prev and next are different */ diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index f6c6266..75090c5 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -55,7 +55,6 @@ #include #include #include -#include #include asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); @@ -238,13 +237,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, kfree(p->thread.io_bitmap_ptr); p->thread.io_bitmap_max = 0; } - - clear_tsk_thread_flag(p, TIF_DS_AREA_MSR); - p->thread.ds_ctx = NULL; - - clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); - p->thread.debugctlmsr = 0; - return err; } diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index dc9690b..cc4258f 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -49,7 +49,6 @@ #include #include #include -#include #include asmlinkage extern void ret_from_fork(void); @@ -313,13 +312,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, if (err) goto out; } - - clear_tsk_thread_flag(p, TIF_DS_AREA_MSR); - p->thread.ds_ctx = NULL; - - clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR); - p->thread.debugctlmsr = 0; - err = 0; out: if (err && p->thread.io_bitmap_ptr) { diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index a503b1f..f2fd3b8 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -2,9 +2,6 @@ /* * Pentium III FXSR, SSE support * Gareth Hughes , May 2000 - * - * BTS tracing - * Markus Metzger , Dec 2007 */ #include @@ -21,7 +18,6 @@ #include #include #include -#include #include #include @@ -35,7 +31,6 @@ #include #include #include -#include #include #include "tls.h" @@ -788,342 +783,6 @@ static int ioperm_get(struct task_struct *target, 0, IO_BITMAP_BYTES); } -#ifdef CONFIG_X86_PTRACE_BTS -/* - * A branch trace store context. - * - * Contexts may only be installed by ptrace_bts_config() and only for - * ptraced tasks. - * - * Contexts are destroyed when the tracee is detached from the tracer. - * The actual destruction work requires interrupts enabled, so the - * work is deferred and will be scheduled during __ptrace_unlink(). - * - * Contexts hold an additional task_struct reference on the traced - * task, as well as a reference on the tracer's mm. - * - * Ptrace already holds a task_struct for the duration of ptrace operations, - * but since destruction is deferred, it may be executed after both - * tracer and tracee exited. - */ -struct bts_context { - /* The branch trace handle. */ - struct bts_tracer *tracer; - - /* The buffer used to store the branch trace and its size. */ - void *buffer; - unsigned int size; - - /* The mm that paid for the above buffer. */ - struct mm_struct *mm; - - /* The task this context belongs to. */ - struct task_struct *task; - - /* The signal to send on a bts buffer overflow. */ - unsigned int bts_ovfl_signal; - - /* The work struct to destroy a context. */ - struct work_struct work; -}; - -static int alloc_bts_buffer(struct bts_context *context, unsigned int size) -{ - void *buffer = NULL; - int err = -ENOMEM; - - err = account_locked_memory(current->mm, current->signal->rlim, size); - if (err < 0) - return err; - - buffer = kzalloc(size, GFP_KERNEL); - if (!buffer) - goto out_refund; - - context->buffer = buffer; - context->size = size; - context->mm = get_task_mm(current); - - return 0; - - out_refund: - refund_locked_memory(current->mm, size); - return err; -} - -static inline void free_bts_buffer(struct bts_context *context) -{ - if (!context->buffer) - return; - - kfree(context->buffer); - context->buffer = NULL; - - refund_locked_memory(context->mm, context->size); - context->size = 0; - - mmput(context->mm); - context->mm = NULL; -} - -static void free_bts_context_work(struct work_struct *w) -{ - struct bts_context *context; - - context = container_of(w, struct bts_context, work); - - ds_release_bts(context->tracer); - put_task_struct(context->task); - free_bts_buffer(context); - kfree(context); -} - -static inline void free_bts_context(struct bts_context *context) -{ - INIT_WORK(&context->work, free_bts_context_work); - schedule_work(&context->work); -} - -static inline struct bts_context *alloc_bts_context(struct task_struct *task) -{ - struct bts_context *context = kzalloc(sizeof(*context), GFP_KERNEL); - if (context) { - context->task = task; - task->bts = context; - - get_task_struct(task); - } - - return context; -} - -static int ptrace_bts_read_record(struct task_struct *child, size_t index, - struct bts_struct __user *out) -{ - struct bts_context *context; - const struct bts_trace *trace; - struct bts_struct bts; - const unsigned char *at; - int error; - - context = child->bts; - if (!context) - return -ESRCH; - - trace = ds_read_bts(context->tracer); - if (!trace) - return -ESRCH; - - at = trace->ds.top - ((index + 1) * trace->ds.size); - if ((void *)at < trace->ds.begin) - at += (trace->ds.n * trace->ds.size); - - if (!trace->read) - return -EOPNOTSUPP; - - error = trace->read(context->tracer, at, &bts); - if (error < 0) - return error; - - if (copy_to_user(out, &bts, sizeof(bts))) - return -EFAULT; - - return sizeof(bts); -} - -static int ptrace_bts_drain(struct task_struct *child, - long size, - struct bts_struct __user *out) -{ - struct bts_context *context; - const struct bts_trace *trace; - const unsigned char *at; - int error, drained = 0; - - context = child->bts; - if (!context) - return -ESRCH; - - trace = ds_read_bts(context->tracer); - if (!trace) - return -ESRCH; - - if (!trace->read) - return -EOPNOTSUPP; - - if (size < (trace->ds.top - trace->ds.begin)) - return -EIO; - - for (at = trace->ds.begin; (void *)at < trace->ds.top; - out++, drained++, at += trace->ds.size) { - struct bts_struct bts; - - error = trace->read(context->tracer, at, &bts); - if (error < 0) - return error; - - if (copy_to_user(out, &bts, sizeof(bts))) - return -EFAULT; - } - - memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); - - error = ds_reset_bts(context->tracer); - if (error < 0) - return error; - - return drained; -} - -static int ptrace_bts_config(struct task_struct *child, - long cfg_size, - const struct ptrace_bts_config __user *ucfg) -{ - struct bts_context *context; - struct ptrace_bts_config cfg; - unsigned int flags = 0; - - if (cfg_size < sizeof(cfg)) - return -EIO; - - if (copy_from_user(&cfg, ucfg, sizeof(cfg))) - return -EFAULT; - - context = child->bts; - if (!context) - context = alloc_bts_context(child); - if (!context) - return -ENOMEM; - - if (cfg.flags & PTRACE_BTS_O_SIGNAL) { - if (!cfg.signal) - return -EINVAL; - - return -EOPNOTSUPP; - context->bts_ovfl_signal = cfg.signal; - } - - ds_release_bts(context->tracer); - context->tracer = NULL; - - if ((cfg.flags & PTRACE_BTS_O_ALLOC) && (cfg.size != context->size)) { - int err; - - free_bts_buffer(context); - if (!cfg.size) - return 0; - - err = alloc_bts_buffer(context, cfg.size); - if (err < 0) - return err; - } - - if (cfg.flags & PTRACE_BTS_O_TRACE) - flags |= BTS_USER; - - if (cfg.flags & PTRACE_BTS_O_SCHED) - flags |= BTS_TIMESTAMPS; - - context->tracer = - ds_request_bts_task(child, context->buffer, context->size, - NULL, (size_t)-1, flags); - if (unlikely(IS_ERR(context->tracer))) { - int error = PTR_ERR(context->tracer); - - free_bts_buffer(context); - context->tracer = NULL; - return error; - } - - return sizeof(cfg); -} - -static int ptrace_bts_status(struct task_struct *child, - long cfg_size, - struct ptrace_bts_config __user *ucfg) -{ - struct bts_context *context; - const struct bts_trace *trace; - struct ptrace_bts_config cfg; - - context = child->bts; - if (!context) - return -ESRCH; - - if (cfg_size < sizeof(cfg)) - return -EIO; - - trace = ds_read_bts(context->tracer); - if (!trace) - return -ESRCH; - - memset(&cfg, 0, sizeof(cfg)); - cfg.size = trace->ds.end - trace->ds.begin; - cfg.signal = context->bts_ovfl_signal; - cfg.bts_size = sizeof(struct bts_struct); - - if (cfg.signal) - cfg.flags |= PTRACE_BTS_O_SIGNAL; - - if (trace->ds.flags & BTS_USER) - cfg.flags |= PTRACE_BTS_O_TRACE; - - if (trace->ds.flags & BTS_TIMESTAMPS) - cfg.flags |= PTRACE_BTS_O_SCHED; - - if (copy_to_user(ucfg, &cfg, sizeof(cfg))) - return -EFAULT; - - return sizeof(cfg); -} - -static int ptrace_bts_clear(struct task_struct *child) -{ - struct bts_context *context; - const struct bts_trace *trace; - - context = child->bts; - if (!context) - return -ESRCH; - - trace = ds_read_bts(context->tracer); - if (!trace) - return -ESRCH; - - memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size); - - return ds_reset_bts(context->tracer); -} - -static int ptrace_bts_size(struct task_struct *child) -{ - struct bts_context *context; - const struct bts_trace *trace; - - context = child->bts; - if (!context) - return -ESRCH; - - trace = ds_read_bts(context->tracer); - if (!trace) - return -ESRCH; - - return (trace->ds.top - trace->ds.begin) / trace->ds.size; -} - -/* - * Called from __ptrace_unlink() after the child has been moved back - * to its original parent. - */ -void ptrace_bts_untrace(struct task_struct *child) -{ - if (unlikely(child->bts)) { - free_bts_context(child->bts); - child->bts = NULL; - } -} -#endif /* CONFIG_X86_PTRACE_BTS */ - /* * Called by kernel/ptrace.c when detaching.. * @@ -1251,39 +910,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; #endif - /* - * These bits need more cooking - not enabled yet: - */ -#ifdef CONFIG_X86_PTRACE_BTS - case PTRACE_BTS_CONFIG: - ret = ptrace_bts_config - (child, data, (struct ptrace_bts_config __user *)addr); - break; - - case PTRACE_BTS_STATUS: - ret = ptrace_bts_status - (child, data, (struct ptrace_bts_config __user *)addr); - break; - - case PTRACE_BTS_SIZE: - ret = ptrace_bts_size(child); - break; - - case PTRACE_BTS_GET: - ret = ptrace_bts_read_record - (child, data, (struct bts_struct __user *) addr); - break; - - case PTRACE_BTS_CLEAR: - ret = ptrace_bts_clear(child); - break; - - case PTRACE_BTS_DRAIN: - ret = ptrace_bts_drain - (child, data, (struct bts_struct __user *) addr); - break; -#endif /* CONFIG_X86_PTRACE_BTS */ - default: ret = ptrace_request(child, request, addr, data); break; @@ -1543,14 +1169,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, case PTRACE_GET_THREAD_AREA: case PTRACE_SET_THREAD_AREA: -#ifdef CONFIG_X86_PTRACE_BTS - case PTRACE_BTS_CONFIG: - case PTRACE_BTS_STATUS: - case PTRACE_BTS_SIZE: - case PTRACE_BTS_GET: - case PTRACE_BTS_CLEAR: - case PTRACE_BTS_DRAIN: -#endif /* CONFIG_X86_PTRACE_BTS */ return arch_ptrace(child, request, addr, data); default: diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 3149032..7beba07 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c @@ -158,22 +158,6 @@ static int enable_single_step(struct task_struct *child) } /* - * Install this value in MSR_IA32_DEBUGCTLMSR whenever child is running. - */ -static void write_debugctlmsr(struct task_struct *child, unsigned long val) -{ - if (child->thread.debugctlmsr == val) - return; - - child->thread.debugctlmsr = val; - - if (child != current) - return; - - update_debugctlmsr(val); -} - -/* * Enable single or block step. */ static void enable_step(struct task_struct *child, bool block) @@ -185,17 +169,9 @@ static void enable_step(struct task_struct *child, bool block) * So noone should try to use debugger block stepping in a program * that uses user-mode single stepping itself. */ - if (enable_single_step(child) && block) { - set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); - write_debugctlmsr(child, - child->thread.debugctlmsr | DEBUGCTLMSR_BTF); - } else { - write_debugctlmsr(child, - child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF); - - if (!child->thread.debugctlmsr) - clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); - } + if (!enable_single_step(child)) + return; + /* XXX */ } void user_enable_single_step(struct task_struct *child) @@ -213,11 +189,7 @@ void user_disable_single_step(struct task_struct *child) /* * Make sure block stepping (BTF) is disabled. */ - write_debugctlmsr(child, - child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF); - - if (!child->thread.debugctlmsr) - clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); + /* XXX */ /* Always clear TIF_SINGLESTEP... */ clear_tsk_thread_flag(child, TIF_SINGLESTEP); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 1168e44..e3da5d7 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -543,11 +543,6 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) /* DR6 may or may not be cleared by the CPU */ set_debugreg(0, 6); - /* - * The processor cleared BTF, so don't mark that we need it set. - */ - clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); - tsk->thread.debugctlmsr = 0; /* Store the virtualized DR6 value */ tsk->thread.debugreg6 = dr6; diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 01e6ade..cc12b3c 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -504,18 +504,6 @@ extern int ftrace_dump_on_oops; #define INIT_TRACE_RECURSION #endif -#ifdef CONFIG_HW_BRANCH_TRACER - -void trace_hw_branch(u64 from, u64 to); -void trace_hw_branch_oops(void); - -#else /* CONFIG_HW_BRANCH_TRACER */ - -static inline void trace_hw_branch(u64 from, u64 to) {} -static inline void trace_hw_branch_oops(void) {} - -#endif /* CONFIG_HW_BRANCH_TRACER */ - #ifdef CONFIG_FTRACE_SYSCALLS unsigned long arch_syscall_addr(int nr); diff --git a/include/linux/mm.h b/include/linux/mm.h index e70f21b..c8442b6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -19,7 +19,6 @@ struct anon_vma; struct file_ra_state; struct user_struct; struct writeback_control; -struct rlimit; #ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */ extern unsigned long max_mapnr; @@ -1449,9 +1448,6 @@ int vmemmap_populate_basepages(struct page *start_page, int vmemmap_populate(struct page *start_page, unsigned long pages, int node); void vmemmap_populate_print_last(void); -extern int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim, - size_t size); -extern void refund_locked_memory(struct mm_struct *mm, size_t size); enum mf_flags { MF_COUNT_INCREASED = 1 << 0, diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index e1fb607..4272521 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -345,18 +345,6 @@ static inline void user_single_step_siginfo(struct task_struct *tsk, #define arch_ptrace_stop(code, info) do { } while (0) #endif -#ifndef arch_ptrace_untrace -/* - * Do machine-specific work before untracing child. - * - * This is called for a normal detach as well as from ptrace_exit() - * when the tracing task dies. - * - * Called with write_lock(&tasklist_lock) held. - */ -#define arch_ptrace_untrace(task) do { } while (0) -#endif - extern int task_current_syscall(struct task_struct *target, long *callno, unsigned long args[6], unsigned int maxargs, unsigned long *sp, unsigned long *pc); diff --git a/include/linux/sched.h b/include/linux/sched.h index dad7f66..e0447c6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -99,7 +99,6 @@ struct futex_pi_state; struct robust_list_head; struct bio_list; struct fs_struct; -struct bts_context; struct perf_event_context; /* @@ -1272,12 +1271,6 @@ struct task_struct { struct list_head ptraced; struct list_head ptrace_entry; - /* - * This is the tracer handle for the ptrace BTS extension. - * This field actually belongs to the ptracer task. - */ - struct bts_context *bts; - /* PID/PID hash table linkage. */ struct pid_link pids[PIDTYPE_MAX]; struct list_head thread_group; @@ -2123,10 +2116,8 @@ extern void set_task_comm(struct task_struct *tsk, char *from); extern char *get_task_comm(char *to, struct task_struct *tsk); #ifdef CONFIG_SMP -extern void wait_task_context_switch(struct task_struct *p); extern unsigned long wait_task_inactive(struct task_struct *, long match_state); #else -static inline void wait_task_context_switch(struct task_struct *p) {} static inline unsigned long wait_task_inactive(struct task_struct *p, long match_state) { diff --git a/kernel/fork.c b/kernel/fork.c index 4799c5f..d67f1db 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1108,9 +1108,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->memcg_batch.do_batch = 0; p->memcg_batch.memcg = NULL; #endif - - p->bts = NULL; - p->stack_start = stack_start; /* Perform scheduler related setup. Assign this task to a CPU. */ diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 42ad8ae..9fb5123 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -76,7 +76,6 @@ void __ptrace_unlink(struct task_struct *child) child->parent = child->real_parent; list_del_init(&child->ptrace_entry); - arch_ptrace_untrace(child); if (task_is_traced(child)) ptrace_untrace(child); } diff --git a/kernel/sched.c b/kernel/sched.c index 9ab3cd7..117b7ca 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2077,49 +2077,6 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req) } /* - * wait_task_context_switch - wait for a thread to complete at least one - * context switch. - * - * @p must not be current. - */ -void wait_task_context_switch(struct task_struct *p) -{ - unsigned long nvcsw, nivcsw, flags; - int running; - struct rq *rq; - - nvcsw = p->nvcsw; - nivcsw = p->nivcsw; - for (;;) { - /* - * The runqueue is assigned before the actual context - * switch. We need to take the runqueue lock. - * - * We could check initially without the lock but it is - * very likely that we need to take the lock in every - * iteration. - */ - rq = task_rq_lock(p, &flags); - running = task_running(rq, p); - task_rq_unlock(rq, &flags); - - if (likely(!running)) - break; - /* - * The switch count is incremented before the actual - * context switch. We thus wait for two switches to be - * sure at least one completed. - */ - if ((p->nvcsw - nvcsw) > 1) - break; - if ((p->nivcsw - nivcsw) > 1) - break; - - cpu_relax(); - } -} - -/* * wait_task_inactive - wait for a thread to unschedule. * * If @match_state is nonzero, it's the @p->state value just checked and diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 13e13d4..8b1797c 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -44,9 +44,6 @@ config HAVE_FTRACE_MCOUNT_RECORD help See Documentation/trace/ftrace-design.txt -config HAVE_HW_BRANCH_TRACER - bool - config HAVE_SYSCALL_TRACEPOINTS bool help @@ -374,14 +371,6 @@ config STACK_TRACER Say N if unsure. -config HW_BRANCH_TRACER - depends on HAVE_HW_BRANCH_TRACER - bool "Trace hw branches" - select GENERIC_TRACER - help - This tracer records all branches on the system in a circular - buffer, giving access to the last N branches for each cpu. - config KMEMTRACE bool "Trace SLAB allocations" select GENERIC_TRACER diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 78edc64..ffb1a5b 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_BOOT_TRACER) += trace_boot.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o -obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o obj-$(CONFIG_KMEMTRACE) += kmemtrace.o obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2825ef2..bec2c97 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -34,7 +34,6 @@ enum trace_type { TRACE_GRAPH_RET, TRACE_GRAPH_ENT, TRACE_USER_STACK, - TRACE_HW_BRANCHES, TRACE_KMEM_ALLOC, TRACE_KMEM_FREE, TRACE_BLK, @@ -229,7 +228,6 @@ extern void __ftrace_bad_type(void); TRACE_GRAPH_ENT); \ IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \ TRACE_GRAPH_RET); \ - IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\ IF_ASSIGN(var, ent, struct kmemtrace_alloc_entry, \ TRACE_KMEM_ALLOC); \ IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ @@ -467,8 +465,6 @@ extern int trace_selftest_startup_sysprof(struct tracer *trace, struct trace_array *tr); extern int trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr); -extern int trace_selftest_startup_hw_branches(struct tracer *trace, - struct trace_array *tr); extern int trace_selftest_startup_ksym(struct tracer *trace, struct trace_array *tr); #endif /* CONFIG_FTRACE_STARTUP_TEST */ diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index c16a08f..dc008c1 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -318,18 +318,6 @@ FTRACE_ENTRY(branch, trace_branch, __entry->func, __entry->file, __entry->correct) ); -FTRACE_ENTRY(hw_branch, hw_branch_entry, - - TRACE_HW_BRANCHES, - - F_STRUCT( - __field( u64, from ) - __field( u64, to ) - ), - - F_printk("from: %llx to: %llx", __entry->from, __entry->to) -); - FTRACE_ENTRY(kmem_alloc, kmemtrace_alloc_entry, TRACE_KMEM_ALLOC, diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c deleted file mode 100644 index 7b97000..0000000 --- a/kernel/trace/trace_hw_branches.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * h/w branch tracer for x86 based on BTS - * - * Copyright (C) 2008-2009 Intel Corporation. - * Markus Metzger , 2008-2009 - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "trace_output.h" -#include "trace.h" - - -#define BTS_BUFFER_SIZE (1 << 13) - -static DEFINE_PER_CPU(struct bts_tracer *, hwb_tracer); -static DEFINE_PER_CPU(unsigned char[BTS_BUFFER_SIZE], hwb_buffer); - -#define this_tracer per_cpu(hwb_tracer, smp_processor_id()) - -static int trace_hw_branches_enabled __read_mostly; -static int trace_hw_branches_suspended __read_mostly; -static struct trace_array *hw_branch_trace __read_mostly; - - -static void bts_trace_init_cpu(int cpu) -{ - per_cpu(hwb_tracer, cpu) = - ds_request_bts_cpu(cpu, per_cpu(hwb_buffer, cpu), - BTS_BUFFER_SIZE, NULL, (size_t)-1, - BTS_KERNEL); - - if (IS_ERR(per_cpu(hwb_tracer, cpu))) - per_cpu(hwb_tracer, cpu) = NULL; -} - -static int bts_trace_init(struct trace_array *tr) -{ - int cpu; - - hw_branch_trace = tr; - trace_hw_branches_enabled = 0; - - get_online_cpus(); - for_each_online_cpu(cpu) { - bts_trace_init_cpu(cpu); - - if (likely(per_cpu(hwb_tracer, cpu))) - trace_hw_branches_enabled = 1; - } - trace_hw_branches_suspended = 0; - put_online_cpus(); - - /* If we could not enable tracing on a single cpu, we fail. */ - return trace_hw_branches_enabled ? 0 : -EOPNOTSUPP; -} - -static void bts_trace_reset(struct trace_array *tr) -{ - int cpu; - - get_online_cpus(); - for_each_online_cpu(cpu) { - if (likely(per_cpu(hwb_tracer, cpu))) { - ds_release_bts(per_cpu(hwb_tracer, cpu)); - per_cpu(hwb_tracer, cpu) = NULL; - } - } - trace_hw_branches_enabled = 0; - trace_hw_branches_suspended = 0; - put_online_cpus(); -} - -static void bts_trace_start(struct trace_array *tr) -{ - int cpu; - - get_online_cpus(); - for_each_online_cpu(cpu) - if (likely(per_cpu(hwb_tracer, cpu))) - ds_resume_bts(per_cpu(hwb_tracer, cpu)); - trace_hw_branches_suspended = 0; - put_online_cpus(); -} - -static void bts_trace_stop(struct trace_array *tr) -{ - int cpu; - - get_online_cpus(); - for_each_online_cpu(cpu) - if (likely(per_cpu(hwb_tracer, cpu))) - ds_suspend_bts(per_cpu(hwb_tracer, cpu)); - trace_hw_branches_suspended = 1; - put_online_cpus(); -} - -static int __cpuinit bts_hotcpu_handler(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - int cpu = (long)hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - /* The notification is sent with interrupts enabled. */ - if (trace_hw_branches_enabled) { - bts_trace_init_cpu(cpu); - - if (trace_hw_branches_suspended && - likely(per_cpu(hwb_tracer, cpu))) - ds_suspend_bts(per_cpu(hwb_tracer, cpu)); - } - break; - - case CPU_DOWN_PREPARE: - /* The notification is sent with interrupts enabled. */ - if (likely(per_cpu(hwb_tracer, cpu))) { - ds_release_bts(per_cpu(hwb_tracer, cpu)); - per_cpu(hwb_tracer, cpu) = NULL; - } - } - - return NOTIFY_DONE; -} - -static struct notifier_block bts_hotcpu_notifier __cpuinitdata = { - .notifier_call = bts_hotcpu_handler -}; - -static void bts_trace_print_header(struct seq_file *m) -{ - seq_puts(m, "# CPU# TO <- FROM\n"); -} - -static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) -{ - unsigned long symflags = TRACE_ITER_SYM_OFFSET; - struct trace_entry *entry = iter->ent; - struct trace_seq *seq = &iter->seq; - struct hw_branch_entry *it; - - trace_assign_type(it, entry); - - if (entry->type == TRACE_HW_BRANCHES) { - if (trace_seq_printf(seq, "%4d ", iter->cpu) && - seq_print_ip_sym(seq, it->to, symflags) && - trace_seq_printf(seq, "\t <- ") && - seq_print_ip_sym(seq, it->from, symflags) && - trace_seq_printf(seq, "\n")) - return TRACE_TYPE_HANDLED; - return TRACE_TYPE_PARTIAL_LINE; - } - return TRACE_TYPE_UNHANDLED; -} - -void trace_hw_branch(u64 from, u64 to) -{ - struct ftrace_event_call *call = &event_hw_branch; - struct trace_array *tr = hw_branch_trace; - struct ring_buffer_event *event; - struct ring_buffer *buf; - struct hw_branch_entry *entry; - unsigned long irq1; - int cpu; - - if (unlikely(!tr)) - return; - - if (unlikely(!trace_hw_branches_enabled)) - return; - - local_irq_save(irq1); - cpu = raw_smp_processor_id(); - if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) - goto out; - - buf = tr->buffer; - event = trace_buffer_lock_reserve(buf, TRACE_HW_BRANCHES, - sizeof(*entry), 0, 0); - if (!event) - goto out; - entry = ring_buffer_event_data(event); - tracing_generic_entry_update(&entry->ent, 0, from); - entry->ent.type = TRACE_HW_BRANCHES; - entry->from = from; - entry->to = to; - if (!filter_check_discard(call, entry, buf, event)) - trace_buffer_unlock_commit(buf, event, 0, 0); - - out: - atomic_dec(&tr->data[cpu]->disabled); - local_irq_restore(irq1); -} - -static void trace_bts_at(const struct bts_trace *trace, void *at) -{ - struct bts_struct bts; - int err = 0; - - WARN_ON_ONCE(!trace->read); - if (!trace->read) - return; - - err = trace->read(this_tracer, at, &bts); - if (err < 0) - return; - - switch (bts.qualifier) { - case BTS_BRANCH: - trace_hw_branch(bts.variant.lbr.from, bts.variant.lbr.to); - break; - } -} - -/* - * Collect the trace on the current cpu and write it into the ftrace buffer. - * - * pre: tracing must be suspended on the current cpu - */ -static void trace_bts_cpu(void *arg) -{ - struct trace_array *tr = (struct trace_array *)arg; - const struct bts_trace *trace; - unsigned char *at; - - if (unlikely(!tr)) - return; - - if (unlikely(atomic_read(&tr->data[raw_smp_processor_id()]->disabled))) - return; - - if (unlikely(!this_tracer)) - return; - - trace = ds_read_bts(this_tracer); - if (!trace) - return; - - for (at = trace->ds.top; (void *)at < trace->ds.end; - at += trace->ds.size) - trace_bts_at(trace, at); - - for (at = trace->ds.begin; (void *)at < trace->ds.top; - at += trace->ds.size) - trace_bts_at(trace, at); -} - -static void trace_bts_prepare(struct trace_iterator *iter) -{ - int cpu; - - get_online_cpus(); - for_each_online_cpu(cpu) - if (likely(per_cpu(hwb_tracer, cpu))) - ds_suspend_bts(per_cpu(hwb_tracer, cpu)); - /* - * We need to collect the trace on the respective cpu since ftrace - * implicitly adds the record for the current cpu. - * Once that is more flexible, we could collect the data from any cpu. - */ - on_each_cpu(trace_bts_cpu, iter->tr, 1); - - for_each_online_cpu(cpu) - if (likely(per_cpu(hwb_tracer, cpu))) - ds_resume_bts(per_cpu(hwb_tracer, cpu)); - put_online_cpus(); -} - -static void trace_bts_close(struct trace_iterator *iter) -{ - tracing_reset_online_cpus(iter->tr); -} - -void trace_hw_branch_oops(void) -{ - if (this_tracer) { - ds_suspend_bts_noirq(this_tracer); - trace_bts_cpu(hw_branch_trace); - ds_resume_bts_noirq(this_tracer); - } -} - -struct tracer bts_tracer __read_mostly = -{ - .name = "hw-branch-tracer", - .init = bts_trace_init, - .reset = bts_trace_reset, - .print_header = bts_trace_print_header, - .print_line = bts_trace_print_line, - .start = bts_trace_start, - .stop = bts_trace_stop, - .open = trace_bts_prepare, - .close = trace_bts_close, -#ifdef CONFIG_FTRACE_SELFTEST - .selftest = trace_selftest_startup_hw_branches, -#endif /* CONFIG_FTRACE_SELFTEST */ -}; - -__init static int init_bts_trace(void) -{ - register_hotcpu_notifier(&bts_hotcpu_notifier); - return register_tracer(&bts_tracer); -} -device_initcall(init_bts_trace); diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 280fea4..a7084e7 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -16,7 +16,6 @@ static inline int trace_valid_entry(struct trace_entry *entry) case TRACE_BRANCH: case TRACE_GRAPH_ENT: case TRACE_GRAPH_RET: - case TRACE_HW_BRANCHES: case TRACE_KSYM: return 1; } @@ -754,62 +753,6 @@ trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr) } #endif /* CONFIG_BRANCH_TRACER */ -#ifdef CONFIG_HW_BRANCH_TRACER -int -trace_selftest_startup_hw_branches(struct tracer *trace, - struct trace_array *tr) -{ - struct trace_iterator *iter; - struct tracer tracer; - unsigned long count; - int ret; - - if (!trace->open) { - printk(KERN_CONT "missing open function..."); - return -1; - } - - ret = tracer_init(trace, tr); - if (ret) { - warn_failed_init_tracer(trace, ret); - return ret; - } - - /* - * The hw-branch tracer needs to collect the trace from the various - * cpu trace buffers - before tracing is stopped. - */ - iter = kzalloc(sizeof(*iter), GFP_KERNEL); - if (!iter) - return -ENOMEM; - - memcpy(&tracer, trace, sizeof(tracer)); - - iter->trace = &tracer; - iter->tr = tr; - iter->pos = -1; - mutex_init(&iter->mutex); - - trace->open(iter); - - mutex_destroy(&iter->mutex); - kfree(iter); - - tracing_stop(); - - ret = trace_test_buffer(tr, &count); - trace->reset(tr); - tracing_start(); - - if (!ret && !count) { - printk(KERN_CONT "no entries found.."); - ret = -1; - } - - return ret; -} -#endif /* CONFIG_HW_BRANCH_TRACER */ - #ifdef CONFIG_KSYM_TRACER static int ksym_selftest_dummy; diff --git a/mm/mlock.c b/mm/mlock.c index 8f4e2df..3f82720 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -607,44 +607,3 @@ void user_shm_unlock(size_t size, struct user_struct *user) spin_unlock(&shmlock_user_lock); free_uid(user); } - -int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim, - size_t size) -{ - unsigned long lim, vm, pgsz; - int error = -ENOMEM; - - pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; - - down_write(&mm->mmap_sem); - - lim = ACCESS_ONCE(rlim[RLIMIT_AS].rlim_cur) >> PAGE_SHIFT; - vm = mm->total_vm + pgsz; - if (lim < vm) - goto out; - - lim = ACCESS_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur) >> PAGE_SHIFT; - vm = mm->locked_vm + pgsz; - if (lim < vm) - goto out; - - mm->total_vm += pgsz; - mm->locked_vm += pgsz; - - error = 0; - out: - up_write(&mm->mmap_sem); - return error; -} - -void refund_locked_memory(struct mm_struct *mm, size_t size) -{ - unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; - - down_write(&mm->mmap_sem); - - mm->total_vm -= pgsz; - mm->locked_vm -= pgsz; - - up_write(&mm->mmap_sem); -} -- cgit v0.10.2 From ea8e61b7bbc4a2faef77db34eb2db2a2c2372ff6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 25 Mar 2010 14:51:51 +0100 Subject: x86, ptrace: Fix block-step Implement ptrace-block-step using TIF_BLOCKSTEP which will set DEBUGCTLMSR_BTF when set for a task while preserving any other DEBUGCTLMSR bits. Signed-off-by: Peter Zijlstra LKML-Reference: <20100325135414.017536066@chello.nl> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 5bec21a..32428b4 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -799,7 +799,7 @@ extern void cpu_init(void); static inline unsigned long get_debugctlmsr(void) { - unsigned long debugctlmsr = 0; + unsigned long debugctlmsr = 0; #ifndef CONFIG_X86_DEBUGCTLMSR if (boot_cpu_data.x86 < 6) @@ -807,7 +807,7 @@ static inline unsigned long get_debugctlmsr(void) #endif rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr); - return debugctlmsr; + return debugctlmsr; } static inline void update_debugctlmsr(unsigned long debugctlmsr) diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index dc85e12..d017ed55 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -92,6 +92,7 @@ struct thread_info { #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ #define TIF_FREEZE 23 /* is freezing for suspend */ #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ +#define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */ #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ @@ -113,6 +114,7 @@ struct thread_info { #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FREEZE (1 << TIF_FREEZE) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) +#define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) @@ -143,7 +145,7 @@ struct thread_info { /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ - (_TIF_IO_BITMAP|_TIF_NOTSC) + (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP) #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 7a880ad..f2f56c0 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -422,12 +422,22 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, static void __kprobes clear_btf(void) { - /* XXX */ + if (test_thread_flag(TIF_BLOCKSTEP)) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl &= ~DEBUGCTLMSR_BTF; + update_debugctlmsr(debugctl); + } } static void __kprobes restore_btf(void) { - /* XXX */ + if (test_thread_flag(TIF_BLOCKSTEP)) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl |= DEBUGCTLMSR_BTF; + update_debugctlmsr(debugctl); + } } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 1a60beb..8328009 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -195,6 +195,17 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, prev = &prev_p->thread; next = &next_p->thread; + if (test_tsk_thread_flag(prev_p, TIF_BLOCKSTEP) ^ + test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl &= ~DEBUGCTLMSR_BTF; + if (test_tsk_thread_flag(next_p, TIF_BLOCKSTEP)) + debugctl |= DEBUGCTLMSR_BTF; + + update_debugctlmsr(debugctl); + } + if (test_tsk_thread_flag(prev_p, TIF_NOTSC) ^ test_tsk_thread_flag(next_p, TIF_NOTSC)) { /* prev and next are different */ diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 7beba07..58de45e 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c @@ -169,9 +169,19 @@ static void enable_step(struct task_struct *child, bool block) * So noone should try to use debugger block stepping in a program * that uses user-mode single stepping itself. */ - if (!enable_single_step(child)) - return; - /* XXX */ + if (enable_single_step(child) && block) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl |= DEBUGCTLMSR_BTF; + update_debugctlmsr(debugctl); + set_tsk_thread_flag(child, TIF_BLOCKSTEP); + } else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl &= ~DEBUGCTLMSR_BTF; + update_debugctlmsr(debugctl); + clear_tsk_thread_flag(child, TIF_BLOCKSTEP); + } } void user_enable_single_step(struct task_struct *child) @@ -189,7 +199,13 @@ void user_disable_single_step(struct task_struct *child) /* * Make sure block stepping (BTF) is disabled. */ - /* XXX */ + if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) { + unsigned long debugctl = get_debugctlmsr(); + + debugctl &= ~DEBUGCTLMSR_BTF; + update_debugctlmsr(debugctl); + clear_tsk_thread_flag(child, TIF_BLOCKSTEP); + } /* Always clear TIF_SINGLESTEP... */ clear_tsk_thread_flag(child, TIF_SINGLESTEP); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index e3da5d7..36f1bd9 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -544,6 +544,11 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) /* DR6 may or may not be cleared by the CPU */ set_debugreg(0, 6); + /* + * The processor cleared BTF, so don't mark that we need it set. + */ + clear_tsk_thread_flag(tsk, TIF_BLOCKSTEP); + /* Store the virtualized DR6 value */ tsk->thread.debugreg6 = dr6; -- cgit v0.10.2 From 11164cd4f6dab326a88bdf27f2f8f7c11977e91a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 26 Mar 2010 14:08:44 +0100 Subject: perf, x86: Add Nehelem PMU programming errata workaround Implement the workaround for Intel Errata AAK100 and AAP53. Also, remove the Core-i7 name for Nehalem events since there are also Westmere based i7 chips. Signed-off-by: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: <1269608924.12097.147.camel@laptop> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index f571f51..6f66d4a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -184,7 +184,7 @@ struct x86_pmu { int version; int (*handle_irq)(struct pt_regs *); void (*disable_all)(void); - void (*enable_all)(void); + void (*enable_all)(int added); void (*enable)(struct perf_event *); void (*disable)(struct perf_event *); int (*hw_config)(struct perf_event_attr *attr, struct hw_perf_event *hwc); @@ -576,7 +576,7 @@ void hw_perf_disable(void) x86_pmu.disable_all(); } -static void x86_pmu_enable_all(void) +static void x86_pmu_enable_all(int added) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; @@ -784,7 +784,7 @@ void hw_perf_enable(void) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct perf_event *event; struct hw_perf_event *hwc; - int i; + int i, added = cpuc->n_added; if (!x86_pmu_initialized()) return; @@ -836,7 +836,7 @@ void hw_perf_enable(void) cpuc->enabled = 1; barrier(); - x86_pmu.enable_all(); + x86_pmu.enable_all(added); } static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 044b843..676aac2 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -483,7 +483,7 @@ static void intel_pmu_disable_all(void) intel_pmu_lbr_disable_all(); } -static void intel_pmu_enable_all(void) +static void intel_pmu_enable_all(int added) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); @@ -502,6 +502,40 @@ static void intel_pmu_enable_all(void) } } +/* + * Workaround for: + * Intel Errata AAK100 (model 26) + * Intel Errata AAP53 (model 30) + * + * These chips need to be 'reset' when adding counters by programming + * the magic three (non counting) events 0x4300D2, 0x4300B1 and 0x4300B5 + * either in sequence on the same PMC or on different PMCs. + */ +static void intel_pmu_nhm_enable_all(int added) +{ + if (added) { + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int i; + + wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 0, 0x4300D2); + wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 1, 0x4300B1); + wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + 2, 0x4300B5); + + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x3); + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0); + + for (i = 0; i < 3; i++) { + struct perf_event *event = cpuc->events[i]; + + if (!event) + continue; + + __x86_pmu_enable_event(&event->hw); + } + } + intel_pmu_enable_all(added); +} + static inline u64 intel_pmu_get_status(void) { u64 status; @@ -658,7 +692,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) intel_pmu_drain_bts_buffer(); status = intel_pmu_get_status(); if (!status) { - intel_pmu_enable_all(); + intel_pmu_enable_all(0); return 0; } @@ -707,7 +741,7 @@ again: goto again; done: - intel_pmu_enable_all(); + intel_pmu_enable_all(0); return 1; } @@ -920,7 +954,8 @@ static __init int intel_pmu_init(void) intel_pmu_lbr_init_nhm(); x86_pmu.event_constraints = intel_nehalem_event_constraints; - pr_cont("Nehalem/Corei7 events, "); + x86_pmu.enable_all = intel_pmu_nhm_enable_all; + pr_cont("Nehalem events, "); break; case 28: /* Atom */ diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index f8fe069..0d1be36 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -535,7 +535,7 @@ static void p4_pmu_enable_event(struct perf_event *event) (cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE); } -static void p4_pmu_enable_all(void) +static void p4_pmu_enable_all(int added) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index 6ff4d01d..877182c 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -66,7 +66,7 @@ static void p6_pmu_disable_all(void) wrmsrl(MSR_P6_EVNTSEL0, val); } -static void p6_pmu_enable_all(void) +static void p6_pmu_enable_all(int added) { unsigned long val; -- cgit v0.10.2 From 085ea739adf107b5a5d131f3625e517ff4a5181e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 2 Apr 2010 12:50:39 -0400 Subject: perf probe: Fix --line syntax help and document Just fix typos. --line option accepts ':START-END' syntax, not ':START:END'. Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: systemtap Cc: DLE LKML-Reference: <20100402165038.23551.62590.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 0f944b3..bb671b3 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -85,7 +85,7 @@ LINE SYNTAX ----------- Line range is descripted by following syntax. - "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]" + "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]" FUNC specifies the function name of showing lines. 'RLN' is the start line number from function entry line, and 'RLN2' is the end line number. As same as diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index cf2ffa5..b3ba25a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -167,7 +167,7 @@ static const struct option options[] = { " with existing name"), #ifdef DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, - "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", + "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]", "Show source code lines.", opt_show_lines), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), -- cgit v0.10.2 From c9e385826d4f1ca5a72005ab8503598f791a8dc0 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 2 Apr 2010 12:50:45 -0400 Subject: perf probe: Fix not to return non-matched file Fix cu_find_realpath() not to return the last file path if that is not matched to input pattern. Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: systemtap Cc: DLE LKML-Reference: <20100402165045.23551.47780.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index db52ec2..b44132e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -183,6 +183,8 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) if (strtailcmp(src, fname) == 0) break; } + if (i == nfiles) + return NULL; return src; } -- cgit v0.10.2 From 12e5a7ae475ccb2733d740ffb95d9ca0a18392da Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 2 Apr 2010 12:50:53 -0400 Subject: perf probe: Correct error message for non-structure type perf probe outputs incorrect error message when it is called with non-existent field on a non-data structure local variable. # perf probe vfs_read 'count.hoge' Fatal: Structure on a register is not supported yet. # perf probe vfs_read 'count->hoge' Fatal: Semantic error: hoge must be referred by '.' This corrects the messsage. # perf probe vfs_read 'count.hoge' Fatal: count is not a data structure. # perf probe vfs_read 'count->hoge' Fatal: count is not a data structure. Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: systemtap Cc: DLE LKML-Reference: <20100402165052.23551.75866.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index b44132e..59b0115 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -429,12 +429,20 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, if (die_get_real_type(&type, &type) == NULL) die("Failed to get a type information of %s.", varname); + /* Verify it is a data structure */ + if (dwarf_tag(&type) != DW_TAG_structure_type) + die("%s is not a data structure.", varname); + ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); if (*ref_ptr) (*ref_ptr)->next = ref; else *ref_ptr = ref; } else { + /* Verify it is a data structure */ + if (dwarf_tag(&type) != DW_TAG_structure_type) + die("%s is not a data structure.", varname); + if (field->ref) die("Semantic error: %s must be referred by '.'", field->name); @@ -442,10 +450,6 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, die("Structure on a register is not supported yet."); } - /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) - die("%s is not a data structure.", varname); - if (die_find_member(&type, field->name, &member) == NULL) die("%s(tyep:%s) has no member %s.", varname, dwarf_diename(&type), field->name); -- cgit v0.10.2 From 75ec5a245c7763c397f31ec8964d0a46c54a7386 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 2 Apr 2010 12:50:59 -0400 Subject: perf probe: Fix to close dwarf when failing to analyze it Fix to close libdw routine when failing to analyze it in find_perf_probe_point(). Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: systemtap Cc: DLE LKML-Reference: <20100402165059.23551.95587.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 59b0115..a851377 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -818,8 +818,10 @@ int find_perf_probe_point(int fd, unsigned long addr, return -ENOENT; /* Find cu die */ - if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) - return -EINVAL; + if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { + ret = -EINVAL; + goto end; + } /* Find a corresponding line */ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); -- cgit v0.10.2 From 948b1bb89a44561560531394c18da4a99215f772 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 29 Mar 2010 18:36:50 +0200 Subject: perf, x86: Undo some some *_counter* -> *_event* renames The big rename: cdd6c48 perf: Do the big rename: Performance Counters -> Performance Events accidentally renamed some members of stucts that were named after registers in the spec. To avoid confusion this patch reverts some changes. The related specs are MSR descriptions in AMD's BKDGs and the ARCHITECTURAL PERFORMANCE MONITORING section in the Intel 64 and IA-32 Architectures Software Developer's Manuals. This patch does: $ sed -i -e 's:num_events:num_counters:g' \ arch/x86/include/asm/perf_event.h \ arch/x86/kernel/cpu/perf_event_amd.c \ arch/x86/kernel/cpu/perf_event.c \ arch/x86/kernel/cpu/perf_event_intel.c \ arch/x86/kernel/cpu/perf_event_p6.c \ arch/x86/kernel/cpu/perf_event_p4.c \ arch/x86/oprofile/op_model_ppro.c $ sed -i -e 's:event_bits:cntval_bits:g' -e 's:event_mask:cntval_mask:g' \ arch/x86/kernel/cpu/perf_event_amd.c \ arch/x86/kernel/cpu/perf_event.c \ arch/x86/kernel/cpu/perf_event_intel.c \ arch/x86/kernel/cpu/perf_event_p6.c \ arch/x86/kernel/cpu/perf_event_p4.c Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1269880612-25800-2-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 124dddd..987bf67 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -67,7 +67,7 @@ union cpuid10_eax { struct { unsigned int version_id:8; - unsigned int num_events:8; + unsigned int num_counters:8; unsigned int bit_width:8; unsigned int mask_length:8; } split; @@ -76,7 +76,7 @@ union cpuid10_eax { union cpuid10_edx { struct { - unsigned int num_events_fixed:4; + unsigned int num_counters_fixed:4; unsigned int reserved:28; } split; unsigned int full; diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index b534356..9daaa1e 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -195,10 +195,10 @@ struct x86_pmu { u64 (*event_map)(int); u64 (*raw_event)(u64); int max_events; - int num_events; - int num_events_fixed; - int event_bits; - u64 event_mask; + int num_counters; + int num_counters_fixed; + int cntval_bits; + u64 cntval_mask; int apic; u64 max_period; struct event_constraint * @@ -268,7 +268,7 @@ static u64 x86_perf_event_update(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - int shift = 64 - x86_pmu.event_bits; + int shift = 64 - x86_pmu.cntval_bits; u64 prev_raw_count, new_raw_count; int idx = hwc->idx; s64 delta; @@ -320,12 +320,12 @@ static bool reserve_pmc_hardware(void) if (nmi_watchdog == NMI_LOCAL_APIC) disable_lapic_nmi_watchdog(); - for (i = 0; i < x86_pmu.num_events; i++) { + for (i = 0; i < x86_pmu.num_counters; i++) { if (!reserve_perfctr_nmi(x86_pmu.perfctr + i)) goto perfctr_fail; } - for (i = 0; i < x86_pmu.num_events; i++) { + for (i = 0; i < x86_pmu.num_counters; i++) { if (!reserve_evntsel_nmi(x86_pmu.eventsel + i)) goto eventsel_fail; } @@ -336,7 +336,7 @@ eventsel_fail: for (i--; i >= 0; i--) release_evntsel_nmi(x86_pmu.eventsel + i); - i = x86_pmu.num_events; + i = x86_pmu.num_counters; perfctr_fail: for (i--; i >= 0; i--) @@ -352,7 +352,7 @@ static void release_pmc_hardware(void) { int i; - for (i = 0; i < x86_pmu.num_events; i++) { + for (i = 0; i < x86_pmu.num_counters; i++) { release_perfctr_nmi(x86_pmu.perfctr + i); release_evntsel_nmi(x86_pmu.eventsel + i); } @@ -547,7 +547,7 @@ static void x86_pmu_disable_all(void) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { u64 val; if (!test_bit(idx, cpuc->active_mask)) @@ -582,7 +582,7 @@ static void x86_pmu_enable_all(int added) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { struct perf_event *event = cpuc->events[idx]; u64 val; @@ -657,14 +657,14 @@ static int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) * assign events to counters starting with most * constrained events. */ - wmax = x86_pmu.num_events; + wmax = x86_pmu.num_counters; /* * when fixed event counters are present, * wmax is incremented by 1 to account * for one more choice */ - if (x86_pmu.num_events_fixed) + if (x86_pmu.num_counters_fixed) wmax++; for (w = 1, num = n; num && w <= wmax; w++) { @@ -714,7 +714,7 @@ static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, struct perf_event *event; int n, max_count; - max_count = x86_pmu.num_events + x86_pmu.num_events_fixed; + max_count = x86_pmu.num_counters + x86_pmu.num_counters_fixed; /* current number of events already accepted */ n = cpuc->n_events; @@ -904,7 +904,7 @@ x86_perf_event_set_period(struct perf_event *event) atomic64_set(&hwc->prev_count, (u64)-left); wrmsrl(hwc->event_base + idx, - (u64)(-left) & x86_pmu.event_mask); + (u64)(-left) & x86_pmu.cntval_mask); perf_event_update_userpage(event); @@ -987,7 +987,7 @@ void perf_event_print_debug(void) unsigned long flags; int cpu, idx; - if (!x86_pmu.num_events) + if (!x86_pmu.num_counters) return; local_irq_save(flags); @@ -1011,7 +1011,7 @@ void perf_event_print_debug(void) } pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { rdmsrl(x86_pmu.eventsel + idx, pmc_ctrl); rdmsrl(x86_pmu.perfctr + idx, pmc_count); @@ -1024,7 +1024,7 @@ void perf_event_print_debug(void) pr_info("CPU#%d: gen-PMC%d left: %016llx\n", cpu, idx, prev_left); } - for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { + for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count); pr_info("CPU#%d: fixed-PMC%d count: %016llx\n", @@ -1089,7 +1089,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { if (!test_bit(idx, cpuc->active_mask)) continue; @@ -1097,7 +1097,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) hwc = &event->hw; val = x86_perf_event_update(event); - if (val & (1ULL << (x86_pmu.event_bits - 1))) + if (val & (1ULL << (x86_pmu.cntval_bits - 1))) continue; /* @@ -1401,46 +1401,46 @@ void __init init_hw_perf_events(void) if (x86_pmu.quirks) x86_pmu.quirks(); - if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) { + if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) { WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!", - x86_pmu.num_events, X86_PMC_MAX_GENERIC); - x86_pmu.num_events = X86_PMC_MAX_GENERIC; + x86_pmu.num_counters, X86_PMC_MAX_GENERIC); + x86_pmu.num_counters = X86_PMC_MAX_GENERIC; } - x86_pmu.intel_ctrl = (1 << x86_pmu.num_events) - 1; - perf_max_events = x86_pmu.num_events; + x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1; + perf_max_events = x86_pmu.num_counters; - if (x86_pmu.num_events_fixed > X86_PMC_MAX_FIXED) { + if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) { WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!", - x86_pmu.num_events_fixed, X86_PMC_MAX_FIXED); - x86_pmu.num_events_fixed = X86_PMC_MAX_FIXED; + x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED); + x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED; } x86_pmu.intel_ctrl |= - ((1LL << x86_pmu.num_events_fixed)-1) << X86_PMC_IDX_FIXED; + ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; perf_events_lapic_init(); register_die_notifier(&perf_event_nmi_notifier); unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_events) - 1, - 0, x86_pmu.num_events); + __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, + 0, x86_pmu.num_counters); if (x86_pmu.event_constraints) { for_each_event_constraint(c, x86_pmu.event_constraints) { if (c->cmask != INTEL_ARCH_FIXED_MASK) continue; - c->idxmsk64 |= (1ULL << x86_pmu.num_events) - 1; - c->weight += x86_pmu.num_events; + c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1; + c->weight += x86_pmu.num_counters; } } pr_info("... version: %d\n", x86_pmu.version); - pr_info("... bit width: %d\n", x86_pmu.event_bits); - pr_info("... generic registers: %d\n", x86_pmu.num_events); - pr_info("... value mask: %016Lx\n", x86_pmu.event_mask); + pr_info("... bit width: %d\n", x86_pmu.cntval_bits); + pr_info("... generic registers: %d\n", x86_pmu.num_counters); + pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); pr_info("... max period: %016Lx\n", x86_pmu.max_period); - pr_info("... fixed-purpose events: %d\n", x86_pmu.num_events_fixed); + pr_info("... fixed-purpose events: %d\n", x86_pmu.num_counters_fixed); pr_info("... event mask: %016Lx\n", x86_pmu.intel_ctrl); perf_cpu_notifier(x86_pmu_notifier); diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 285623b..7753a5c 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -165,7 +165,7 @@ static void amd_put_event_constraints(struct cpu_hw_events *cpuc, * be removed on one CPU at a time AND PMU is disabled * when we come here */ - for (i = 0; i < x86_pmu.num_events; i++) { + for (i = 0; i < x86_pmu.num_counters; i++) { if (nb->owners[i] == event) { cmpxchg(nb->owners+i, event, NULL); break; @@ -215,7 +215,7 @@ amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) struct hw_perf_event *hwc = &event->hw; struct amd_nb *nb = cpuc->amd_nb; struct perf_event *old = NULL; - int max = x86_pmu.num_events; + int max = x86_pmu.num_counters; int i, j, k = -1; /* @@ -293,7 +293,7 @@ static struct amd_nb *amd_alloc_nb(int cpu, int nb_id) /* * initialize all possible NB constraints */ - for (i = 0; i < x86_pmu.num_events; i++) { + for (i = 0; i < x86_pmu.num_counters; i++) { __set_bit(i, nb->event_constraints[i].idxmsk); nb->event_constraints[i].weight = 1; } @@ -385,9 +385,9 @@ static __initconst struct x86_pmu amd_pmu = { .event_map = amd_pmu_event_map, .raw_event = amd_pmu_raw_event, .max_events = ARRAY_SIZE(amd_perfmon_event_map), - .num_events = 4, - .event_bits = 48, - .event_mask = (1ULL << 48) - 1, + .num_counters = 4, + .cntval_bits = 48, + .cntval_mask = (1ULL << 48) - 1, .apic = 1, /* use highest bit to detect overflow */ .max_period = (1ULL << 47) - 1, diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 676aac2..cc4d90a 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -653,20 +653,20 @@ static void intel_pmu_reset(void) unsigned long flags; int idx; - if (!x86_pmu.num_events) + if (!x86_pmu.num_counters) return; local_irq_save(flags); printk("clearing PMU state on CPU#%d\n", smp_processor_id()); - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { checking_wrmsrl(x86_pmu.eventsel + idx, 0ull); checking_wrmsrl(x86_pmu.perfctr + idx, 0ull); } - for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { + for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); - } + if (ds) ds->bts_index = ds->bts_buffer_base; @@ -901,16 +901,16 @@ static __init int intel_pmu_init(void) x86_pmu = intel_pmu; x86_pmu.version = version; - x86_pmu.num_events = eax.split.num_events; - x86_pmu.event_bits = eax.split.bit_width; - x86_pmu.event_mask = (1ULL << eax.split.bit_width) - 1; + x86_pmu.num_counters = eax.split.num_counters; + x86_pmu.cntval_bits = eax.split.bit_width; + x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1; /* * Quirk: v2 perfmon does not report fixed-purpose events, so * assume at least 3 events: */ if (version > 1) - x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); + x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); /* * v2 and above have a perf capabilities MSR diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 0d1be36..4139100 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -483,7 +483,7 @@ static void p4_pmu_disable_all(void) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { struct perf_event *event = cpuc->events[idx]; if (!test_bit(idx, cpuc->active_mask)) continue; @@ -540,7 +540,7 @@ static void p4_pmu_enable_all(int added) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); int idx; - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { struct perf_event *event = cpuc->events[idx]; if (!test_bit(idx, cpuc->active_mask)) continue; @@ -562,7 +562,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) cpuc = &__get_cpu_var(cpu_hw_events); - for (idx = 0; idx < x86_pmu.num_events; idx++) { + for (idx = 0; idx < x86_pmu.num_counters; idx++) { if (!test_bit(idx, cpuc->active_mask)) continue; @@ -579,7 +579,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) p4_pmu_clear_cccr_ovf(hwc); val = x86_perf_event_update(event); - if (val & (1ULL << (x86_pmu.event_bits - 1))) + if (val & (1ULL << (x86_pmu.cntval_bits - 1))) continue; /* @@ -794,10 +794,10 @@ static __initconst struct x86_pmu p4_pmu = { * though leave it restricted at moment assuming * HT is on */ - .num_events = ARCH_P4_MAX_CCCR, + .num_counters = ARCH_P4_MAX_CCCR, .apic = 1, - .event_bits = 40, - .event_mask = (1ULL << 40) - 1, + .cntval_bits = 40, + .cntval_mask = (1ULL << 40) - 1, .max_period = (1ULL << 39) - 1, .hw_config = p4_hw_config, .schedule_events = p4_pmu_schedule_events, diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index 877182c..b26fbc7e 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -119,7 +119,7 @@ static __initconst struct x86_pmu p6_pmu = { .apic = 1, .max_period = (1ULL << 31) - 1, .version = 0, - .num_events = 2, + .num_counters = 2, /* * Events have 40 bits implemented. However they are designed such * that bits [32-39] are sign extensions of bit 31. As such the @@ -127,8 +127,8 @@ static __initconst struct x86_pmu p6_pmu = { * * See IA-32 Intel Architecture Software developer manual Vol 3B */ - .event_bits = 32, - .event_mask = (1ULL << 32) - 1, + .cntval_bits = 32, + .cntval_mask = (1ULL << 32) - 1, .get_event_constraints = x86_get_event_constraints, .event_constraints = p6_event_constraints, }; diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c index 2bf90fa..c8abc4d 100644 --- a/arch/x86/oprofile/op_model_ppro.c +++ b/arch/x86/oprofile/op_model_ppro.c @@ -239,11 +239,11 @@ static void arch_perfmon_setup_counters(void) if (eax.split.version_id == 0 && current_cpu_data.x86 == 6 && current_cpu_data.x86_model == 15) { eax.split.version_id = 2; - eax.split.num_events = 2; + eax.split.num_counters = 2; eax.split.bit_width = 40; } - num_counters = eax.split.num_events; + num_counters = eax.split.num_counters; op_arch_perfmon_spec.num_counters = num_counters; op_arch_perfmon_spec.num_controls = num_counters; -- cgit v0.10.2 From a098f4484bc7dae23f5b62360954007b99b64600 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 30 Mar 2010 11:28:21 +0200 Subject: perf, x86: implement ARCH_PERFMON_EVENTSEL bit masks ARCH_PERFMON_EVENTSEL bit masks are often used in the kernel. This patch adds macros for the bit masks and removes local defines. The function intel_pmu_raw_event() becomes x86_pmu_raw_event() which is generic for x86 models and same also for p6. Duplicate code is removed. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <20100330092821.GH11907@erda.amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 987bf67..f6d43db 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -18,39 +18,31 @@ #define MSR_ARCH_PERFMON_EVENTSEL0 0x186 #define MSR_ARCH_PERFMON_EVENTSEL1 0x187 -#define ARCH_PERFMON_EVENTSEL_ENABLE (1 << 22) -#define ARCH_PERFMON_EVENTSEL_ANY (1 << 21) -#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) -#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) -#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) - -/* - * Includes eventsel and unit mask as well: - */ - - -#define INTEL_ARCH_EVTSEL_MASK 0x000000FFULL -#define INTEL_ARCH_UNIT_MASK 0x0000FF00ULL -#define INTEL_ARCH_EDGE_MASK 0x00040000ULL -#define INTEL_ARCH_INV_MASK 0x00800000ULL -#define INTEL_ARCH_CNT_MASK 0xFF000000ULL -#define INTEL_ARCH_EVENT_MASK (INTEL_ARCH_UNIT_MASK|INTEL_ARCH_EVTSEL_MASK) - -/* - * filter mask to validate fixed counter events. - * the following filters disqualify for fixed counters: - * - inv - * - edge - * - cnt-mask - * The other filters are supported by fixed counters. - * The any-thread option is supported starting with v3. - */ -#define INTEL_ARCH_FIXED_MASK \ - (INTEL_ARCH_CNT_MASK| \ - INTEL_ARCH_INV_MASK| \ - INTEL_ARCH_EDGE_MASK|\ - INTEL_ARCH_UNIT_MASK|\ - INTEL_ARCH_EVENT_MASK) +#define ARCH_PERFMON_EVENTSEL_EVENT 0x000000FFULL +#define ARCH_PERFMON_EVENTSEL_UMASK 0x0000FF00ULL +#define ARCH_PERFMON_EVENTSEL_USR (1ULL << 16) +#define ARCH_PERFMON_EVENTSEL_OS (1ULL << 17) +#define ARCH_PERFMON_EVENTSEL_EDGE (1ULL << 18) +#define ARCH_PERFMON_EVENTSEL_INT (1ULL << 20) +#define ARCH_PERFMON_EVENTSEL_ANY (1ULL << 21) +#define ARCH_PERFMON_EVENTSEL_ENABLE (1ULL << 22) +#define ARCH_PERFMON_EVENTSEL_INV (1ULL << 23) +#define ARCH_PERFMON_EVENTSEL_CMASK 0xFF000000ULL + +#define AMD64_EVENTSEL_EVENT \ + (ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32)) +#define INTEL_ARCH_EVENT_MASK \ + (ARCH_PERFMON_EVENTSEL_UMASK | ARCH_PERFMON_EVENTSEL_EVENT) + +#define X86_RAW_EVENT_MASK \ + (ARCH_PERFMON_EVENTSEL_EVENT | \ + ARCH_PERFMON_EVENTSEL_UMASK | \ + ARCH_PERFMON_EVENTSEL_EDGE | \ + ARCH_PERFMON_EVENTSEL_INV | \ + ARCH_PERFMON_EVENTSEL_CMASK) +#define AMD64_RAW_EVENT_MASK \ + (X86_RAW_EVENT_MASK | \ + AMD64_EVENTSEL_EVENT) #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c #define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 9daaa1e..1dd42c1 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -143,13 +143,21 @@ struct cpu_hw_events { * Constraint on the Event code. */ #define INTEL_EVENT_CONSTRAINT(c, n) \ - EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVTSEL_MASK) + EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT) /* * Constraint on the Event code + UMask + fixed-mask + * + * filter mask to validate fixed counter events. + * the following filters disqualify for fixed counters: + * - inv + * - edge + * - cnt-mask + * The other filters are supported by fixed counters. + * The any-thread option is supported starting with v3. */ #define FIXED_EVENT_CONSTRAINT(c, n) \ - EVENT_CONSTRAINT(c, (1ULL << (32+n)), INTEL_ARCH_FIXED_MASK) + EVENT_CONSTRAINT(c, (1ULL << (32+n)), X86_RAW_EVENT_MASK) /* * Constraint on the Event code + UMask @@ -437,6 +445,11 @@ static int x86_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc return 0; } +static u64 x86_pmu_raw_event(u64 hw_event) +{ + return hw_event & X86_RAW_EVENT_MASK; +} + /* * Setup the hardware configuration for a given attr_type */ @@ -1427,7 +1440,7 @@ void __init init_hw_perf_events(void) if (x86_pmu.event_constraints) { for_each_event_constraint(c, x86_pmu.event_constraints) { - if (c->cmask != INTEL_ARCH_FIXED_MASK) + if (c->cmask != X86_RAW_EVENT_MASK) continue; c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1; diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 7753a5c..37e9517 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -113,20 +113,7 @@ static u64 amd_pmu_event_map(int hw_event) static u64 amd_pmu_raw_event(u64 hw_event) { -#define K7_EVNTSEL_EVENT_MASK 0xF000000FFULL -#define K7_EVNTSEL_UNIT_MASK 0x00000FF00ULL -#define K7_EVNTSEL_EDGE_MASK 0x000040000ULL -#define K7_EVNTSEL_INV_MASK 0x000800000ULL -#define K7_EVNTSEL_REG_MASK 0x0FF000000ULL - -#define K7_EVNTSEL_MASK \ - (K7_EVNTSEL_EVENT_MASK | \ - K7_EVNTSEL_UNIT_MASK | \ - K7_EVNTSEL_EDGE_MASK | \ - K7_EVNTSEL_INV_MASK | \ - K7_EVNTSEL_REG_MASK) - - return hw_event & K7_EVNTSEL_MASK; + return hw_event & AMD64_RAW_EVENT_MASK; } /* diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index cc4d90a..dfdd6f9 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -452,24 +452,6 @@ static __initconst u64 atom_hw_cache_event_ids }, }; -static u64 intel_pmu_raw_event(u64 hw_event) -{ -#define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL -#define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL -#define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL -#define CORE_EVNTSEL_INV_MASK 0x00800000ULL -#define CORE_EVNTSEL_REG_MASK 0xFF000000ULL - -#define CORE_EVNTSEL_MASK \ - (INTEL_ARCH_EVTSEL_MASK | \ - INTEL_ARCH_UNIT_MASK | \ - INTEL_ARCH_EDGE_MASK | \ - INTEL_ARCH_INV_MASK | \ - INTEL_ARCH_CNT_MASK) - - return hw_event & CORE_EVNTSEL_MASK; -} - static void intel_pmu_disable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); @@ -788,7 +770,7 @@ static __initconst struct x86_pmu core_pmu = { .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, - .raw_event = intel_pmu_raw_event, + .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* @@ -827,7 +809,7 @@ static __initconst struct x86_pmu intel_pmu = { .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, - .raw_event = intel_pmu_raw_event, + .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index b26fbc7e..03c139a 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -27,24 +27,6 @@ static u64 p6_pmu_event_map(int hw_event) */ #define P6_NOP_EVENT 0x0000002EULL -static u64 p6_pmu_raw_event(u64 hw_event) -{ -#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL -#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL -#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL -#define P6_EVNTSEL_INV_MASK 0x00800000ULL -#define P6_EVNTSEL_REG_MASK 0xFF000000ULL - -#define P6_EVNTSEL_MASK \ - (P6_EVNTSEL_EVENT_MASK | \ - P6_EVNTSEL_UNIT_MASK | \ - P6_EVNTSEL_EDGE_MASK | \ - P6_EVNTSEL_INV_MASK | \ - P6_EVNTSEL_REG_MASK) - - return hw_event & P6_EVNTSEL_MASK; -} - static struct event_constraint p6_event_constraints[] = { INTEL_EVENT_CONSTRAINT(0xc1, 0x1), /* FLOPS */ @@ -114,7 +96,7 @@ static __initconst struct x86_pmu p6_pmu = { .eventsel = MSR_P6_EVNTSEL0, .perfctr = MSR_P6_PERFCTR0, .event_map = p6_pmu_event_map, - .raw_event = p6_pmu_raw_event, + .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(p6_perfmon_event_map), .apic = 1, .max_period = (1ULL << 31) - 1, -- cgit v0.10.2 From b4cdc5c264b35c67007800dec3928e9547a9d70b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 30 Mar 2010 17:00:06 +0200 Subject: perf, x86: Fix up the ANY flag stuff Stephane noticed that the ANY flag was in generic arch code, and Cyrill reported that it broke the P4 code. Solve this by merging x86_pmu::raw_event into x86_pmu::hw_config and provide intel_pmu and amd_pmu specific versions of this callback. The intel_pmu one deals with the ANY flag, the amd_pmu adds the few extra event bits AMD64 has. Reported-by: Stephane Eranian Reported-by: Cyrill Gorcunov Acked-by: Robert Richter Acked-by: Cyrill Gorcunov Acked-by: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <1269968113.5258.442.camel@laptop> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1dd42c1..65e9c5e 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -196,12 +196,11 @@ struct x86_pmu { void (*enable_all)(int added); void (*enable)(struct perf_event *); void (*disable)(struct perf_event *); - int (*hw_config)(struct perf_event_attr *attr, struct hw_perf_event *hwc); + int (*hw_config)(struct perf_event *event); int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); unsigned eventsel; unsigned perfctr; u64 (*event_map)(int); - u64 (*raw_event)(u64); int max_events; int num_counters; int num_counters_fixed; @@ -426,28 +425,26 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) return 0; } -static int x86_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) +static int x86_pmu_hw_config(struct perf_event *event) { /* * Generate PMC IRQs: * (keep 'enabled' bit clear for now) */ - hwc->config = ARCH_PERFMON_EVENTSEL_INT; + event->hw.config = ARCH_PERFMON_EVENTSEL_INT; /* * Count user and OS events unless requested not to */ - if (!attr->exclude_user) - hwc->config |= ARCH_PERFMON_EVENTSEL_USR; - if (!attr->exclude_kernel) - hwc->config |= ARCH_PERFMON_EVENTSEL_OS; + if (!event->attr.exclude_user) + event->hw.config |= ARCH_PERFMON_EVENTSEL_USR; + if (!event->attr.exclude_kernel) + event->hw.config |= ARCH_PERFMON_EVENTSEL_OS; - return 0; -} + if (event->attr.type == PERF_TYPE_RAW) + event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK; -static u64 x86_pmu_raw_event(u64 hw_event) -{ - return hw_event & X86_RAW_EVENT_MASK; + return 0; } /* @@ -489,7 +486,7 @@ static int __hw_perf_event_init(struct perf_event *event) hwc->last_tag = ~0ULL; /* Processor specifics */ - err = x86_pmu.hw_config(attr, hwc); + err = x86_pmu.hw_config(event); if (err) return err; @@ -508,16 +505,8 @@ static int __hw_perf_event_init(struct perf_event *event) return -EOPNOTSUPP; } - /* - * Raw hw_event type provide the config in the hw_event structure - */ - if (attr->type == PERF_TYPE_RAW) { - hwc->config |= x86_pmu.raw_event(attr->config); - if ((hwc->config & ARCH_PERFMON_EVENTSEL_ANY) && - perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) - return -EACCES; + if (attr->type == PERF_TYPE_RAW) return 0; - } if (attr->type == PERF_TYPE_HW_CACHE) return set_ext_hw_attr(hwc, attr); diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 37e9517..bbd7339 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -111,9 +111,19 @@ static u64 amd_pmu_event_map(int hw_event) return amd_perfmon_event_map[hw_event]; } -static u64 amd_pmu_raw_event(u64 hw_event) +static int amd_pmu_hw_config(struct perf_event *event) { - return hw_event & AMD64_RAW_EVENT_MASK; + int ret = x86_pmu_hw_config(event); + + if (ret) + return ret; + + if (event->attr.type != PERF_TYPE_RAW) + return 0; + + event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK; + + return 0; } /* @@ -365,12 +375,11 @@ static __initconst struct x86_pmu amd_pmu = { .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, - .hw_config = x86_hw_config, + .hw_config = amd_pmu_hw_config, .schedule_events = x86_schedule_events, .eventsel = MSR_K7_EVNTSEL0, .perfctr = MSR_K7_PERFCTR0, .event_map = amd_pmu_event_map, - .raw_event = amd_pmu_raw_event, .max_events = ARRAY_SIZE(amd_perfmon_event_map), .num_counters = 4, .cntval_bits = 48, diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index dfdd6f9..30bf10c 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -758,6 +758,30 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event return x86_get_event_constraints(cpuc, event); } +static int intel_pmu_hw_config(struct perf_event *event) +{ + int ret = x86_pmu_hw_config(event); + + if (ret) + return ret; + + if (event->attr.type != PERF_TYPE_RAW) + return 0; + + if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY)) + return 0; + + if (x86_pmu.version < 3) + return -EINVAL; + + if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + + event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY; + + return 0; +} + static __initconst struct x86_pmu core_pmu = { .name = "core", .handle_irq = x86_pmu_handle_irq, @@ -765,12 +789,11 @@ static __initconst struct x86_pmu core_pmu = { .enable_all = x86_pmu_enable_all, .enable = x86_pmu_enable_event, .disable = x86_pmu_disable_event, - .hw_config = x86_hw_config, + .hw_config = x86_pmu_hw_config, .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, - .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* @@ -804,12 +827,11 @@ static __initconst struct x86_pmu intel_pmu = { .enable_all = intel_pmu_enable_all, .enable = intel_pmu_enable_event, .disable = intel_pmu_disable_event, - .hw_config = x86_hw_config, + .hw_config = intel_pmu_hw_config, .schedule_events = x86_schedule_events, .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, .perfctr = MSR_ARCH_PERFMON_PERFCTR0, .event_map = intel_pmu_event_map, - .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(intel_perfmon_event_map), .apic = 1, /* diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 4139100..acd237d 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -419,20 +419,7 @@ static u64 p4_pmu_event_map(int hw_event) return config; } -/* - * We don't control raw events so it's up to the caller - * to pass sane values (and we don't count the thread number - * on HT machine but allow HT-compatible specifics to be - * passed on) - */ -static u64 p4_pmu_raw_event(u64 hw_event) -{ - return hw_event & - (p4_config_pack_escr(P4_ESCR_MASK_HT) | - p4_config_pack_cccr(P4_CCCR_MASK_HT)); -} - -static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) +static int p4_hw_config(struct perf_event *event) { int cpu = raw_smp_processor_id(); u32 escr, cccr; @@ -444,11 +431,29 @@ static int p4_hw_config(struct perf_event_attr *attr, struct hw_perf_event *hwc) */ cccr = p4_default_cccr_conf(cpu); - escr = p4_default_escr_conf(cpu, attr->exclude_kernel, attr->exclude_user); - hwc->config = p4_config_pack_escr(escr) | p4_config_pack_cccr(cccr); + escr = p4_default_escr_conf(cpu, event->attr.exclude_kernel, + event->attr.exclude_user); + event->hw.config = p4_config_pack_escr(escr) | + p4_config_pack_cccr(cccr); if (p4_ht_active() && p4_ht_thread(cpu)) - hwc->config = p4_set_ht_bit(hwc->config); + event->hw.config = p4_set_ht_bit(event->hw.config); + + if (event->attr.type != PERF_TYPE_RAW) + return 0; + + /* + * We don't control raw events so it's up to the caller + * to pass sane values (and we don't count the thread number + * on HT machine but allow HT-compatible specifics to be + * passed on) + * + * XXX: HT wide things should check perf_paranoid_cpu() && + * CAP_SYS_ADMIN + */ + event->hw.config |= event->attr.config & + (p4_config_pack_escr(P4_ESCR_MASK_HT) | + p4_config_pack_cccr(P4_CCCR_MASK_HT)); return 0; } @@ -785,7 +790,6 @@ static __initconst struct x86_pmu p4_pmu = { .eventsel = MSR_P4_BPU_CCCR0, .perfctr = MSR_P4_BPU_PERFCTR0, .event_map = p4_pmu_event_map, - .raw_event = p4_pmu_raw_event, .max_events = ARRAY_SIZE(p4_general_events), .get_event_constraints = x86_get_event_constraints, /* diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index 03c139a..9123e8e 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -91,12 +91,11 @@ static __initconst struct x86_pmu p6_pmu = { .enable_all = p6_pmu_enable_all, .enable = p6_pmu_enable_event, .disable = p6_pmu_disable_event, - .hw_config = x86_hw_config, + .hw_config = x86_pmu_hw_config, .schedule_events = x86_schedule_events, .eventsel = MSR_P6_EVNTSEL0, .perfctr = MSR_P6_PERFCTR0, .event_map = p6_pmu_event_map, - .raw_event = x86_pmu_raw_event, .max_events = ARRAY_SIZE(p6_perfmon_event_map), .apic = 1, .max_period = (1ULL << 31) - 1, -- cgit v0.10.2 From caaa8be3b6707cb9664e573a28b00f845ce9f32e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 29 Mar 2010 13:09:53 +0200 Subject: perf, x86: Fix __initconst vs const All variables that have __initconst should also be const. Suggested-by: Stephen Rothwell Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index bbd7339..611df11 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -2,7 +2,7 @@ static DEFINE_RAW_SPINLOCK(amd_nb_lock); -static __initconst u64 amd_hw_cache_event_ids +static __initconst const u64 amd_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -368,7 +368,7 @@ static void amd_pmu_cpu_dead(int cpu) raw_spin_unlock(&amd_nb_lock); } -static __initconst struct x86_pmu amd_pmu = { +static __initconst const struct x86_pmu amd_pmu = { .name = "AMD", .handle_irq = x86_pmu_handle_irq, .disable_all = x86_pmu_disable_all, diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 30bf10c..1957e3f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -88,7 +88,7 @@ static u64 intel_pmu_event_map(int hw_event) return intel_perfmon_event_map[hw_event]; } -static __initconst u64 westmere_hw_cache_event_ids +static __initconst const u64 westmere_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -179,7 +179,7 @@ static __initconst u64 westmere_hw_cache_event_ids }, }; -static __initconst u64 nehalem_hw_cache_event_ids +static __initconst const u64 nehalem_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -270,7 +270,7 @@ static __initconst u64 nehalem_hw_cache_event_ids }, }; -static __initconst u64 core2_hw_cache_event_ids +static __initconst const u64 core2_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -361,7 +361,7 @@ static __initconst u64 core2_hw_cache_event_ids }, }; -static __initconst u64 atom_hw_cache_event_ids +static __initconst const u64 atom_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -782,7 +782,7 @@ static int intel_pmu_hw_config(struct perf_event *event) return 0; } -static __initconst struct x86_pmu core_pmu = { +static __initconst const struct x86_pmu core_pmu = { .name = "core", .handle_irq = x86_pmu_handle_irq, .disable_all = x86_pmu_disable_all, @@ -820,7 +820,7 @@ static void intel_pmu_cpu_dying(int cpu) fini_debug_store_on_cpu(cpu); } -static __initconst struct x86_pmu intel_pmu = { +static __initconst const struct x86_pmu intel_pmu = { .name = "Intel", .handle_irq = intel_pmu_handle_irq, .disable_all = intel_pmu_disable_all, diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index acd237d..15367cc 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -287,7 +287,7 @@ static struct p4_event_bind p4_event_bind_map[] = { p4_config_pack_cccr(cache_event | \ P4_CCCR_ESEL(P4_OPCODE_ESEL(P4_OPCODE(event)))) -static __initconst u64 p4_hw_cache_event_ids +static __initconst const u64 p4_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = @@ -780,7 +780,7 @@ done: return num ? -ENOSPC : 0; } -static __initconst struct x86_pmu p4_pmu = { +static __initconst const struct x86_pmu p4_pmu = { .name = "Netburst P4/Xeon", .handle_irq = p4_pmu_handle_irq, .disable_all = p4_pmu_disable_all, diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index 9123e8e..34ba07b 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -84,7 +84,7 @@ static void p6_pmu_enable_event(struct perf_event *event) (void)checking_wrmsrl(hwc->config_base + hwc->idx, val); } -static __initconst struct x86_pmu p6_pmu = { +static __initconst const struct x86_pmu p6_pmu = { .name = "p6", .handle_irq = x86_pmu_handle_irq, .disable_all = p6_pmu_disable_all, -- cgit v0.10.2 From 40b91cd10f000b4c4934e48e2e5c0bec66def144 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 29 Mar 2010 16:37:17 +0200 Subject: perf, x86: Add Nehalem programming quirk to Westmere According to the Xeon-5600 errata the Westmere suffers the same PMU programming bug as the original Nehalem did. Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 1957e3f..f168b40 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -488,6 +488,7 @@ static void intel_pmu_enable_all(int added) * Workaround for: * Intel Errata AAK100 (model 26) * Intel Errata AAP53 (model 30) + * Intel Errata BD53 (model 44) * * These chips need to be 'reset' when adding counters by programming * the magic three (non counting) events 0x4300D2, 0x4300B1 and 0x4300B5 @@ -980,6 +981,7 @@ static __init int intel_pmu_init(void) intel_pmu_lbr_init_nhm(); x86_pmu.event_constraints = intel_westmere_event_constraints; + x86_pmu.enable_all = intel_pmu_nhm_enable_all; pr_cont("Westmere events, "); break; -- cgit v0.10.2 From c6e718ff8cdcf5e7855077687720b37c4a07650a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 12:11:06 -0300 Subject: perf symbols: Move more map_groups methods to map.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While writing a standalone test app that uses the symbol system to find kernel space symbols I noticed these also need to be moved. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 09e09e7..de31901 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -658,7 +658,7 @@ print_entries: if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, - MAP__FUNCTION, fp); + MAP__FUNCTION, verbose, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 9f2963f..e21f980 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,4 +1,5 @@ #include "symbol.h" +#include #include #include #include @@ -234,6 +235,37 @@ u64 map__objdump_2ip(struct map *map, u64 addr) return ip; } +void map_groups__init(struct map_groups *self) +{ + int i; + for (i = 0; i < MAP__NR_TYPES; ++i) { + self->maps[i] = RB_ROOT; + INIT_LIST_HEAD(&self->removed_maps[i]); + } +} + +void map_groups__flush(struct map_groups *self) +{ + int type; + + for (type = 0; type < MAP__NR_TYPES; type++) { + struct rb_root *root = &self->maps[type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for + * instance in some hist_entry instances, so + * just move them to a separate list. + */ + list_add_tail(&pos->node, &self->removed_maps[pos->type]); + } + } +} + struct symbol *map_groups__find_symbol(struct map_groups *self, enum map_type type, u64 addr, symbol_filter_t filter) @@ -246,6 +278,142 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, return NULL; } +size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, int verbose, FILE *fp) +{ + size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); + struct rb_node *nd; + + for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 2) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + + return printed; +} + +size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_maps(self, i, verbose, fp); + return printed; +} + +static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, + enum map_type type, + int verbose, FILE *fp) +{ + struct map *pos; + size_t printed = 0; + + list_for_each_entry(pos, &self->removed_maps[type], node) { + printed += fprintf(fp, "Map:"); + printed += map__fprintf(pos, fp); + if (verbose > 1) { + printed += dso__fprintf(pos->dso, type, fp); + printed += fprintf(fp, "--\n"); + } + } + return printed; +} + +static size_t map_groups__fprintf_removed_maps(struct map_groups *self, + int verbose, FILE *fp) +{ + size_t printed = 0, i; + for (i = 0; i < MAP__NR_TYPES; ++i) + printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); + return printed; +} + +size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) +{ + size_t printed = map_groups__fprintf_maps(self, verbose, fp); + printed += fprintf(fp, "Removed maps:\n"); + return printed + map_groups__fprintf_removed_maps(self, verbose, fp); +} + +int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, + int verbose, FILE *fp) +{ + struct rb_root *root = &self->maps[map->type]; + struct rb_node *next = rb_first(root); + + while (next) { + struct map *pos = rb_entry(next, struct map, rb_node); + next = rb_next(&pos->rb_node); + + if (!map__overlap(pos, map)) + continue; + + if (verbose >= 2) { + fputs("overlapping maps:\n", fp); + map__fprintf(map, fp); + map__fprintf(pos, fp); + } + + rb_erase(&pos->rb_node, root); + /* + * We may have references to this map, for instance in some + * hist_entry instances, so just move them to a separate + * list. + */ + list_add_tail(&pos->node, &self->removed_maps[map->type]); + /* + * Now check if we need to create new maps for areas not + * overlapped by the new map: + */ + if (map->start > pos->start) { + struct map *before = map__clone(pos); + + if (before == NULL) + return -ENOMEM; + + before->end = map->start - 1; + map_groups__insert(self, before); + if (verbose >= 2) + map__fprintf(before, fp); + } + + if (map->end < pos->end) { + struct map *after = map__clone(pos); + + if (after == NULL) + return -ENOMEM; + + after->start = map->end + 1; + map_groups__insert(self, after); + if (verbose >= 2) + map__fprintf(after, fp); + } + } + + return 0; +} + +/* + * XXX This should not really _copy_ te maps, but refcount them. + */ +int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type) +{ + struct rb_node *nd; + for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { + struct map *map = rb_entry(nd, struct map, rb_node); + struct map *new = map__clone(map); + if (new == NULL) + return -ENOMEM; + map_groups__insert(self, new); + } + return 0; +} + static u64 map__reloc_map_ip(struct map *map, u64 ip) { return ip + (s64)map->pgoff; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 6a703fa..4e7a11d 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -97,11 +97,14 @@ struct map_groups { }; size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, FILE *fp); + enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); void map_groups__init(struct map_groups *self); -size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); +int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type); +size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); +size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); static inline void map_groups__insert(struct map_groups *self, struct map *map) { @@ -125,6 +128,9 @@ static inline struct symbol *map_groups__find_function(struct map_groups *self, return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); } +int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, + int verbose, FILE *fp); + struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); int __map_groups__create_kernel_maps(struct map_groups *self, @@ -134,5 +140,6 @@ int map_groups__create_kernel_maps(struct map_groups *self, struct map *vmlinux_maps[MAP__NR_TYPES]); struct map *map_groups__new_module(struct map_groups *self, u64 start, const char *filename); +void map_groups__flush(struct map_groups *self); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9bbe27d..1f7ecd4 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -38,15 +38,6 @@ failure: return ret; } -void map_groups__init(struct map_groups *self) -{ - int i; - for (i = 0; i < MAP__NR_TYPES; ++i) { - self->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&self->removed_maps[i]); - } -} - static struct thread *thread__new(pid_t pid) { struct thread *self = zalloc(sizeof(*self)); @@ -62,28 +53,6 @@ static struct thread *thread__new(pid_t pid) return self; } -static void map_groups__flush(struct map_groups *self) -{ - int type; - - for (type = 0; type < MAP__NR_TYPES; type++) { - struct rb_root *root = &self->maps[type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for - * instance in some hist_entry instances, so - * just move them to a separate list. - */ - list_add_tail(&pos->node, &self->removed_maps[pos->type]); - } - } -} - int thread__set_comm(struct thread *self, const char *comm) { int err; @@ -110,69 +79,10 @@ int thread__comm_len(struct thread *self) return self->comm_len; } -size_t __map_groups__fprintf_maps(struct map_groups *self, - enum map_type type, FILE *fp) -{ - size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); - struct rb_node *nd; - - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { - struct map *pos = rb_entry(nd, struct map, rb_node); - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 2) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - - return printed; -} - -size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(self, i, fp); - return printed; -} - -static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, - enum map_type type, FILE *fp) -{ - struct map *pos; - size_t printed = 0; - - list_for_each_entry(pos, &self->removed_maps[type], node) { - printed += fprintf(fp, "Map:"); - printed += map__fprintf(pos, fp); - if (verbose > 1) { - printed += dso__fprintf(pos->dso, type, fp); - printed += fprintf(fp, "--\n"); - } - } - return printed; -} - -static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) -{ - size_t printed = 0, i; - for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(self, i, fp); - return printed; -} - -static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) -{ - size_t printed = map_groups__fprintf_maps(self, fp); - printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(self, fp); -} - static size_t thread__fprintf(struct thread *self, FILE *fp) { return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + - map_groups__fprintf(&self->mg, fp); + map_groups__fprintf(&self->mg, verbose, fp); } struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) @@ -214,87 +124,12 @@ struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) return th; } -static int map_groups__fixup_overlappings(struct map_groups *self, - struct map *map) -{ - struct rb_root *root = &self->maps[map->type]; - struct rb_node *next = rb_first(root); - - while (next) { - struct map *pos = rb_entry(next, struct map, rb_node); - next = rb_next(&pos->rb_node); - - if (!map__overlap(pos, map)) - continue; - - if (verbose >= 2) { - fputs("overlapping maps:\n", stderr); - map__fprintf(map, stderr); - map__fprintf(pos, stderr); - } - - rb_erase(&pos->rb_node, root); - /* - * We may have references to this map, for instance in some - * hist_entry instances, so just move them to a separate - * list. - */ - list_add_tail(&pos->node, &self->removed_maps[map->type]); - /* - * Now check if we need to create new maps for areas not - * overlapped by the new map: - */ - if (map->start > pos->start) { - struct map *before = map__clone(pos); - - if (before == NULL) - return -ENOMEM; - - before->end = map->start - 1; - map_groups__insert(self, before); - if (verbose >= 2) - map__fprintf(before, stderr); - } - - if (map->end < pos->end) { - struct map *after = map__clone(pos); - - if (after == NULL) - return -ENOMEM; - - after->start = map->end + 1; - map_groups__insert(self, after); - if (verbose >= 2) - map__fprintf(after, stderr); - } - } - - return 0; -} - void thread__insert_map(struct thread *self, struct map *map) { - map_groups__fixup_overlappings(&self->mg, map); + map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); map_groups__insert(&self->mg, map); } -/* - * XXX This should not really _copy_ te maps, but refcount them. - */ -static int map_groups__clone(struct map_groups *self, - struct map_groups *parent, enum map_type type) -{ - struct rb_node *nd; - for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { - struct map *map = rb_entry(nd, struct map, rb_node); - struct map *new = map__clone(map); - if (new == NULL) - return -ENOMEM; - map_groups__insert(self, new); - } - return 0; -} - int thread__fork(struct thread *self, struct thread *parent) { int i; -- cgit v0.10.2 From 7e5e1b1404c30db5f6bc3f5203b6c21c1d244f99 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 12:30:40 -0300 Subject: perf symbols: map_groups__find_symbol must return the map too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tools need to know from which map in the map_group a symbol was resolved to, so that, for isntance, we can annotate kernel modules symbols by getting its precise name, etc. Also add the _by_name variants for completeness. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 924a951..32edb6a 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -369,7 +369,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = map_groups__find_function(&session->kmaps, addr, NULL); + sym = map_groups__find_function(&session->kmaps, + addr, NULL, NULL); } else addr = data->ptr; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e21f980..37913b2 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -268,12 +268,38 @@ void map_groups__flush(struct map_groups *self) struct symbol *map_groups__find_symbol(struct map_groups *self, enum map_type type, u64 addr, + struct map **mapp, symbol_filter_t filter) { struct map *map = map_groups__find(self, type, addr); - if (map != NULL) + if (map != NULL) { + if (mapp != NULL) + *mapp = map; return map__find_symbol(map, map->map_ip(map, addr), filter); + } + + return NULL; +} + +struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, + enum map_type type, + const char *name, + struct map **mapp, + symbol_filter_t filter) +{ + struct rb_node *nd; + + for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + struct symbol *sym = map__find_symbol_by_name(pos, name, filter); + + if (sym == NULL) + continue; + if (mapp != NULL) + *mapp = pos; + return sym; + } return NULL; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4e7a11d..2031278 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -119,13 +119,28 @@ static inline struct map *map_groups__find(struct map_groups *self, struct symbol *map_groups__find_symbol(struct map_groups *self, enum map_type type, u64 addr, + struct map **mapp, symbol_filter_t filter); -static inline struct symbol *map_groups__find_function(struct map_groups *self, - u64 addr, - symbol_filter_t filter) +struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, + enum map_type type, + const char *name, + struct map **mapp, + symbol_filter_t filter); + +static inline +struct symbol *map_groups__find_function(struct map_groups *self, u64 addr, + struct map **mapp, symbol_filter_t filter) +{ + return map_groups__find_symbol(self, MAP__FUNCTION, addr, mapp, filter); +} + +static inline +struct symbol *map_groups__find_function_by_name(struct map_groups *self, + const char *name, struct map **mapp, + symbol_filter_t filter) { - return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); + return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); } int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, -- cgit v0.10.2 From 5f4d3f8816461300ce54505c9117bf85b3044aa0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Mar 2010 21:16:22 -0300 Subject: perf report: Add progress bars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For when we are processing the events and inserting the entries in the browser. Experimentation here: naming "ui_something" we may be treading into creating a TUI/GUI set of routines that can then be implemented in terms of multiple backends. Also the time it takes for adding things to the "browser" takes, visually (I guess I should do some profiling here ;-) ), more time than for processing the events... That means we probably need to create a custom hist_entry browser, so that we reuse the structures we have in place instead of duplicating them in newt. But progress was made and at least we can see something while long files are being loaded, that must be one of UI 101 bullet points :-) Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6ab1698..3819185 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -303,13 +303,14 @@ static int __cmd_report(void) next = rb_first(&session->stats_by_id); while (next) { struct event_stat_id *stats; + u64 nr_hists; stats = rb_entry(next, struct event_stat_id, rb_node); perf_session__collapse_resort(&stats->hists); - perf_session__output_resort(&stats->hists, stats->stats.total); - + nr_hists = perf_session__output_resort(&stats->hists, + stats->stats.total); if (use_browser) - perf_session__browse_hists(&stats->hists, + perf_session__browse_hists(&stats->hists, nr_hists, stats->stats.total, help); else { if (rb_first(&session->stats_by_id) == diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 0172edf..5cb0a1b 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -10,13 +10,29 @@ extern int dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); +struct ui_progress; + #ifdef NO_NEWT_SUPPORT static inline int browser__show_help(const char *format __used, va_list ap __used) { return 0; } + +static inline struct ui_progress *ui_progress__new(const char *title __used, + u64 total __used) +{ + return (struct ui_progress *)1; +} + +static inline void ui_progress__update(struct ui_progress *self __used, + u64 curr __used) {} + +static inline void ui_progress__delete(struct ui_progress *self __used) {} #else int browser__show_help(const char *format, va_list ap); +struct ui_progress *ui_progress__new(const char *title, u64 total); +void ui_progress__update(struct ui_progress *self, u64 curr); +void ui_progress__delete(struct ui_progress *self); #endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index de31901..a46d093 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -185,12 +185,13 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -void perf_session__output_resort(struct rb_root *hists, u64 total_samples) +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; + u64 nr_hists = 0; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); @@ -205,9 +206,11 @@ void perf_session__output_resort(struct rb_root *hists, u64 total_samples) rb_erase(&n->rb_node, hists); perf_session__insert_output_hist_entry(&tmp, n, min_callchain_hits); + ++nr_hists; } *hists = tmp; + return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index fe366ce..da6a8c1 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -25,7 +25,7 @@ size_t hist_entry__fprintf(struct hist_entry *self, u64 session_total); void hist_entry__free(struct hist_entry *); -void perf_session__output_resort(struct rb_root *hists, u64 total_samples); +u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); void perf_session__collapse_resort(struct rb_root *hists); size_t perf_session__fprintf_hists(struct rb_root *hists, struct perf_session *pair, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e99bcc8..b0210ae 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -12,6 +12,72 @@ #include "sort.h" #include "symbol.h" +struct ui_progress { + newtComponent form, scale; +}; + +struct ui_progress *ui_progress__new(const char *title, u64 total) +{ + struct ui_progress *self = malloc(sizeof(*self)); + + if (self != NULL) { + int cols; + newtGetScreenSize(&cols, NULL); + cols -= 4; + newtCenteredWindow(cols, 1, title); + self->form = newtForm(NULL, NULL, 0); + if (self->form == NULL) + goto out_free_self; + self->scale = newtScale(0, 0, cols, total); + if (self->scale == NULL) + goto out_free_form; + newtFormAddComponents(self->form, self->scale, NULL); + newtRefresh(); + } + + return self; + +out_free_form: + newtFormDestroy(self->form); +out_free_self: + free(self); + return NULL; +} + +void ui_progress__update(struct ui_progress *self, u64 curr) +{ + newtScaleSet(self->scale, curr); + newtRefresh(); +} + +void ui_progress__delete(struct ui_progress *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + free(self); +} + +static char browser__last_msg[1024]; + +int browser__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + + ret = vsnprintf(browser__last_msg + backlog, + sizeof(browser__last_msg) - backlog, format, ap); + backlog += ret; + + if (browser__last_msg[backlog - 1] == '\n') { + newtPopHelpLine(); + newtPushHelpLine(browser__last_msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} + static void newt_form__set_exit_keys(newtComponent self) { newtFormAddHotKey(self, NEWT_KEY_ESCAPE); @@ -364,8 +430,8 @@ static void perf_session__selection(newtComponent self, void *data) *symbol_ptr = newt__symbol_tree_get_current(self); } -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline) +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline) { struct sort_entry *se; struct rb_node *nd; @@ -378,6 +444,12 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, newtComponent form, tree; struct newtExitStruct es; const struct map_symbol *selection; + u64 curr_hist = 0; + struct ui_progress *progress; + + progress = ui_progress__new("Adding entries to the browser...", nr_hists); + if (progress == NULL) + return -1; snprintf(str, sizeof(str), "Samples: %Ld", session_total); newtDrawRootText(0, 0, str); @@ -419,8 +491,13 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total, max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, tree, session_total, idx++); + ++curr_hist; + if (curr_hist % 5) + ui_progress__update(progress, curr_hist); } + ui_progress__delete(progress); + if (max_len > cols) max_len = cols - 3; @@ -480,27 +557,7 @@ do_annotate: newtFormDestroy(form); newtPopWindow(); -} - -static char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; + return 0; } void setup_browser(void) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 76b4ac6..32765cd 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -397,6 +397,10 @@ int __perf_session__process_events(struct perf_session *self, event_t *event; uint32_t size; char *buf; + struct ui_progress *progress = ui_progress__new("Processing events...", + self->size); + if (progress == NULL) + return -1; perf_event_ops__fill_defaults(ops); @@ -425,6 +429,7 @@ remap: more: event = (event_t *)(buf + head); + ui_progress__update(progress, offset); if (self->header.needs_swap) perf_event_header__bswap(&event->header); @@ -475,6 +480,7 @@ more: done: err = 0; out_err: + ui_progress__delete(progress); return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 631f815..6a15dae 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -88,11 +88,15 @@ static inline struct map * } #ifdef NO_NEWT_SUPPORT -static inline void perf_session__browse_hists(struct rb_root *hists __used, +static inline int perf_session__browse_hists(struct rb_root *hists __used, + u64 nr_hists __used, u64 session_total __used, - const char *helpline __used) {} + const char *helpline __used) +{ + return 0; +} #else -void perf_session__browse_hists(struct rb_root *hists, u64 session_total, - const char *helpline); +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline); #endif #endif /* __PERF_SESSION_H */ -- cgit v0.10.2 From 8b2c551f9635bf1c5c2d38de300137998915478f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 27 Mar 2010 11:43:36 -0300 Subject: perf tools: Use -o $(BITBUCKET) in one more case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As described in 1703f2c some gcc versions has issues using /dev/null, so use the mechanism used elsewhere. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6903645..120736c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -542,7 +542,7 @@ PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` endif -ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o /dev/null $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) +ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o $(BITBUCKET) $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) BASIC_CFLAGS += -DNO_LIBPYTHON else ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) -- cgit v0.10.2 From c29ede615fd35a640e771fbbb1778e915fac43a7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 27 Mar 2010 14:30:45 -0300 Subject: perf tools: Allow specifying O= to build files in a separate directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoiding polluting the source tree with build files. Reported-by: Steven Rostedt Cc: Steven Rostedt Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 120736c..93029c0 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,3 +1,7 @@ +ifeq ("$(origin O)", "command line") + OUTPUT := $(O)/ +endif + # The default target of this Makefile is... all:: @@ -153,9 +157,13 @@ all:: # # Define NO_DWARF if you do not want debug-info analysis feature at all. -PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE - @$(SHELL_PATH) util/PERF-VERSION-GEN --include PERF-VERSION-FILE +$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null) +$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) + +$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE + @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) +-include $(OUTPUT)PERF-VERSION-FILE uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') @@ -310,7 +318,7 @@ PROGRAMS += $(EXTRA_PROGRAMS) # # Single 'perf' binary right now: # -PROGRAMS += perf +PROGRAMS += $(OUTPUT)perf # List built-in command $C whose implementation cmd_$C() is not in # builtin-$C.o but is linked in as part of some other command. @@ -320,7 +328,7 @@ PROGRAMS += perf ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) # what 'all' will build but not install in perfexecdir -OTHER_PROGRAMS = perf$X +OTHER_PROGRAMS = $(OUTPUT)perf$X # Set paths to tools early so that they can be used for version tests. ifndef SHELL_PATH @@ -332,7 +340,7 @@ endif export PERL_PATH -LIB_FILE=libperf.a +LIB_FILE=$(OUTPUT)libperf.a LIB_H += ../../include/linux/perf_event.h LIB_H += ../../include/linux/rbtree.h @@ -393,77 +401,77 @@ LIB_H += util/probe-finder.h LIB_H += util/probe-event.h LIB_H += util/cpumap.h -LIB_OBJS += util/abspath.o -LIB_OBJS += util/alias.o -LIB_OBJS += util/build-id.o -LIB_OBJS += util/config.o -LIB_OBJS += util/ctype.o -LIB_OBJS += util/debugfs.o -LIB_OBJS += util/environment.o -LIB_OBJS += util/event.o -LIB_OBJS += util/exec_cmd.o -LIB_OBJS += util/help.o -LIB_OBJS += util/levenshtein.o -LIB_OBJS += util/parse-options.o -LIB_OBJS += util/parse-events.o -LIB_OBJS += util/path.o -LIB_OBJS += util/rbtree.o -LIB_OBJS += util/bitmap.o -LIB_OBJS += util/hweight.o -LIB_OBJS += util/find_next_bit.o -LIB_OBJS += util/run-command.o -LIB_OBJS += util/quote.o -LIB_OBJS += util/strbuf.o -LIB_OBJS += util/string.o -LIB_OBJS += util/strlist.o -LIB_OBJS += util/usage.o -LIB_OBJS += util/wrapper.o -LIB_OBJS += util/sigchain.o -LIB_OBJS += util/symbol.o -LIB_OBJS += util/color.o -LIB_OBJS += util/pager.o -LIB_OBJS += util/header.o -LIB_OBJS += util/callchain.o -LIB_OBJS += util/values.o -LIB_OBJS += util/debug.o -LIB_OBJS += util/map.o -LIB_OBJS += util/session.o -LIB_OBJS += util/thread.o -LIB_OBJS += util/trace-event-parse.o -LIB_OBJS += util/trace-event-read.o -LIB_OBJS += util/trace-event-info.o -LIB_OBJS += util/trace-event-scripting.o -LIB_OBJS += util/svghelper.o -LIB_OBJS += util/sort.o -LIB_OBJS += util/hist.o -LIB_OBJS += util/probe-event.o -LIB_OBJS += util/util.o -LIB_OBJS += util/cpumap.o - -BUILTIN_OBJS += builtin-annotate.o - -BUILTIN_OBJS += builtin-bench.o +LIB_OBJS += $(OUTPUT)util/abspath.o +LIB_OBJS += $(OUTPUT)util/alias.o +LIB_OBJS += $(OUTPUT)util/build-id.o +LIB_OBJS += $(OUTPUT)util/config.o +LIB_OBJS += $(OUTPUT)util/ctype.o +LIB_OBJS += $(OUTPUT)util/debugfs.o +LIB_OBJS += $(OUTPUT)util/environment.o +LIB_OBJS += $(OUTPUT)util/event.o +LIB_OBJS += $(OUTPUT)util/exec_cmd.o +LIB_OBJS += $(OUTPUT)util/help.o +LIB_OBJS += $(OUTPUT)util/levenshtein.o +LIB_OBJS += $(OUTPUT)util/parse-options.o +LIB_OBJS += $(OUTPUT)util/parse-events.o +LIB_OBJS += $(OUTPUT)util/path.o +LIB_OBJS += $(OUTPUT)util/rbtree.o +LIB_OBJS += $(OUTPUT)util/bitmap.o +LIB_OBJS += $(OUTPUT)util/hweight.o +LIB_OBJS += $(OUTPUT)util/find_next_bit.o +LIB_OBJS += $(OUTPUT)util/run-command.o +LIB_OBJS += $(OUTPUT)util/quote.o +LIB_OBJS += $(OUTPUT)util/strbuf.o +LIB_OBJS += $(OUTPUT)util/string.o +LIB_OBJS += $(OUTPUT)util/strlist.o +LIB_OBJS += $(OUTPUT)util/usage.o +LIB_OBJS += $(OUTPUT)util/wrapper.o +LIB_OBJS += $(OUTPUT)util/sigchain.o +LIB_OBJS += $(OUTPUT)util/symbol.o +LIB_OBJS += $(OUTPUT)util/color.o +LIB_OBJS += $(OUTPUT)util/pager.o +LIB_OBJS += $(OUTPUT)util/header.o +LIB_OBJS += $(OUTPUT)util/callchain.o +LIB_OBJS += $(OUTPUT)util/values.o +LIB_OBJS += $(OUTPUT)util/debug.o +LIB_OBJS += $(OUTPUT)util/map.o +LIB_OBJS += $(OUTPUT)util/session.o +LIB_OBJS += $(OUTPUT)util/thread.o +LIB_OBJS += $(OUTPUT)util/trace-event-parse.o +LIB_OBJS += $(OUTPUT)util/trace-event-read.o +LIB_OBJS += $(OUTPUT)util/trace-event-info.o +LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o +LIB_OBJS += $(OUTPUT)util/svghelper.o +LIB_OBJS += $(OUTPUT)util/sort.o +LIB_OBJS += $(OUTPUT)util/hist.o +LIB_OBJS += $(OUTPUT)util/probe-event.o +LIB_OBJS += $(OUTPUT)util/util.o +LIB_OBJS += $(OUTPUT)util/cpumap.o + +BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o + +BUILTIN_OBJS += $(OUTPUT)builtin-bench.o # Benchmark modules -BUILTIN_OBJS += bench/sched-messaging.o -BUILTIN_OBJS += bench/sched-pipe.o -BUILTIN_OBJS += bench/mem-memcpy.o - -BUILTIN_OBJS += builtin-diff.o -BUILTIN_OBJS += builtin-help.o -BUILTIN_OBJS += builtin-sched.o -BUILTIN_OBJS += builtin-buildid-list.o -BUILTIN_OBJS += builtin-buildid-cache.o -BUILTIN_OBJS += builtin-list.o -BUILTIN_OBJS += builtin-record.o -BUILTIN_OBJS += builtin-report.o -BUILTIN_OBJS += builtin-stat.o -BUILTIN_OBJS += builtin-timechart.o -BUILTIN_OBJS += builtin-top.o -BUILTIN_OBJS += builtin-trace.o -BUILTIN_OBJS += builtin-probe.o -BUILTIN_OBJS += builtin-kmem.o -BUILTIN_OBJS += builtin-lock.o +BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o +BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o +BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o + +BUILTIN_OBJS += $(OUTPUT)builtin-diff.o +BUILTIN_OBJS += $(OUTPUT)builtin-help.o +BUILTIN_OBJS += $(OUTPUT)builtin-sched.o +BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o +BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o +BUILTIN_OBJS += $(OUTPUT)builtin-list.o +BUILTIN_OBJS += $(OUTPUT)builtin-record.o +BUILTIN_OBJS += $(OUTPUT)builtin-report.o +BUILTIN_OBJS += $(OUTPUT)builtin-stat.o +BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o +BUILTIN_OBJS += $(OUTPUT)builtin-top.o +BUILTIN_OBJS += $(OUTPUT)builtin-trace.o +BUILTIN_OBJS += $(OUTPUT)builtin-probe.o +BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o +BUILTIN_OBJS += $(OUTPUT)builtin-lock.o PERFLIBS = $(LIB_FILE) @@ -494,6 +502,10 @@ ifeq ($(uname_S),Darwin) PTHREAD_LIBS = endif +ifneq ($(OUTPUT),) + BASIC_CFLAGS += -I$(OUTPUT) +endif + ifeq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); @@ -512,7 +524,7 @@ else ifndef NO_DWARF BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT EXTLIBS += -lelf -ldw - LIB_OBJS += util/probe-finder.o + LIB_OBJS += $(OUTPUT)util/probe-finder.o endif endif @@ -521,7 +533,7 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni BASIC_CFLAGS += -DNO_NEWT_SUPPORT else EXTLIBS += -lnewt - LIB_OBJS += util/newt.o + LIB_OBJS += $(OUTPUT)util/newt.o endif ifndef NO_LIBPERL @@ -533,8 +545,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; e BASIC_CFLAGS += -DNO_LIBPERL else ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) - LIB_OBJS += util/scripting-engines/trace-event-perl.o - LIB_OBJS += scripts/perl/Perf-Trace-Util/Context.o + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o + LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o endif ifndef NO_LIBPYTHON @@ -546,8 +558,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Py_In BASIC_CFLAGS += -DNO_LIBPYTHON else ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) - LIB_OBJS += util/scripting-engines/trace-event-python.o - LIB_OBJS += scripts/python/Perf-Trace-Util/Context.o + LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o + LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o endif ifdef NO_DEMANGLE @@ -618,53 +630,53 @@ ifdef NO_C99_FORMAT endif ifdef SNPRINTF_RETURNS_BOGUS COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS - COMPAT_OBJS += compat/snprintf.o + COMPAT_OBJS += $(OUTPUT)compat/snprintf.o endif ifdef FREAD_READS_DIRECTORIES COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES - COMPAT_OBJS += compat/fopen.o + COMPAT_OBJS += $(OUTPUT)compat/fopen.o endif ifdef NO_SYMLINK_HEAD BASIC_CFLAGS += -DNO_SYMLINK_HEAD endif ifdef NO_STRCASESTR COMPAT_CFLAGS += -DNO_STRCASESTR - COMPAT_OBJS += compat/strcasestr.o + COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o endif ifdef NO_STRTOUMAX COMPAT_CFLAGS += -DNO_STRTOUMAX - COMPAT_OBJS += compat/strtoumax.o + COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o endif ifdef NO_STRTOULL COMPAT_CFLAGS += -DNO_STRTOULL endif ifdef NO_SETENV COMPAT_CFLAGS += -DNO_SETENV - COMPAT_OBJS += compat/setenv.o + COMPAT_OBJS += $(OUTPUT)compat/setenv.o endif ifdef NO_MKDTEMP COMPAT_CFLAGS += -DNO_MKDTEMP - COMPAT_OBJS += compat/mkdtemp.o + COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV - COMPAT_OBJS += compat/unsetenv.o + COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o endif ifdef NO_SYS_SELECT_H BASIC_CFLAGS += -DNO_SYS_SELECT_H endif ifdef NO_MMAP COMPAT_CFLAGS += -DNO_MMAP - COMPAT_OBJS += compat/mmap.o + COMPAT_OBJS += $(OUTPUT)compat/mmap.o else ifdef USE_WIN32_MMAP COMPAT_CFLAGS += -DUSE_WIN32_MMAP - COMPAT_OBJS += compat/win32mmap.o + COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o endif endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD - COMPAT_OBJS += compat/pread.o + COMPAT_OBJS += $(OUTPUT)compat/pread.o endif ifdef NO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY @@ -686,10 +698,10 @@ else endif endif ifdef NO_INET_NTOP - LIB_OBJS += compat/inet_ntop.o + LIB_OBJS += $(OUTPUT)compat/inet_ntop.o endif ifdef NO_INET_PTON - LIB_OBJS += compat/inet_pton.o + LIB_OBJS += $(OUTPUT)compat/inet_pton.o endif ifdef NO_ICONV @@ -706,15 +718,15 @@ endif ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" - LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o + LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o else ifdef ARM_SHA1 SHA1_HEADER = "arm/sha1.h" - LIB_OBJS += arm/sha1.o arm/sha1_arm.o + LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o else ifdef MOZILLA_SHA1 SHA1_HEADER = "mozilla-sha1/sha1.h" - LIB_OBJS += mozilla-sha1/sha1.o + LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o else SHA1_HEADER = EXTLIBS += $(LIB_4_CRYPTO) @@ -726,15 +738,15 @@ ifdef NO_PERL_MAKEMAKER endif ifdef NO_HSTRERROR COMPAT_CFLAGS += -DNO_HSTRERROR - COMPAT_OBJS += compat/hstrerror.o + COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o endif ifdef NO_MEMMEM COMPAT_CFLAGS += -DNO_MEMMEM - COMPAT_OBJS += compat/memmem.o + COMPAT_OBJS += $(OUTPUT)compat/memmem.o endif ifdef INTERNAL_QSORT COMPAT_CFLAGS += -DINTERNAL_QSORT - COMPAT_OBJS += compat/qsort.o + COMPAT_OBJS += $(OUTPUT)compat/qsort.o endif ifdef RUNTIME_PREFIX COMPAT_CFLAGS += -DRUNTIME_PREFIX @@ -814,7 +826,7 @@ export TAR INSTALL DESTDIR SHELL_PATH SHELL = $(SHELL_PATH) -all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS +all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS ifneq (,$X) $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) endif @@ -826,39 +838,39 @@ please_set_SHELL_PATH_to_a_more_modern_shell: shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell -strip: $(PROGRAMS) perf$X - $(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X +strip: $(PROGRAMS) $(OUTPUT)perf$X + $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X -perf.o: perf.c common-cmds.h PERF-CFLAGS +$(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ - $(ALL_CFLAGS) -c $(filter %.c,$^) + $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ -perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS) - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \ +$(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(OUTPUT)perf.o \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) -builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ +$(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ '-DPERF_MAN_PATH="$(mandir_SQ)"' \ '-DPERF_INFO_PATH="$(infodir_SQ)"' $< -builtin-timechart.o: builtin-timechart.c common-cmds.h PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ +$(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ '-DPERF_MAN_PATH="$(mandir_SQ)"' \ '-DPERF_INFO_PATH="$(infodir_SQ)"' $< -$(BUILT_INS): perf$X +$(BUILT_INS): $(OUTPUT)perf$X $(QUIET_BUILT_IN)$(RM) $@ && \ ln perf$X $@ 2>/dev/null || \ ln -s perf$X $@ 2>/dev/null || \ cp perf$X $@ -common-cmds.h: util/generate-cmdlist.sh command-list.txt +$(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt -common-cmds.h: $(wildcard Documentation/perf-*.txt) +$(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh @@ -870,7 +882,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ $@.sh >$@+ && \ chmod +x $@+ && \ - mv $@+ $@ + mv $@+ $(OUTPUT)$@ configure: configure.ac $(QUIET_GEN)$(RM) $@ $<+ && \ @@ -880,60 +892,60 @@ configure: configure.ac $(RM) $<+ # These can record PERF_VERSION -perf.o perf.spec \ +$(OUTPUT)perf.o perf.spec \ $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - : PERF-VERSION-FILE + : $(OUTPUT)PERF-VERSION-FILE -%.o: %.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< -%.s: %.c PERF-CFLAGS +$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< +$(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< -%.o: %.S - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< +$(OUTPUT)%.o: %.S + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< -util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \ +$(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ '-DBINDIR="$(bindir_relative_SQ)"' \ '-DPREFIX="$(prefix_SQ)"' \ $< -builtin-init-db.o: builtin-init-db.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< +$(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< -util/config.o: util/config.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< # some perf warning policies can't fit to lib/bitmap.c, eg: it warns about variable shadowing # from that comes from kernel headers wrapping. KBITMAP_FLAGS=`echo $(ALL_CFLAGS) | sed s/-Wshadow// | sed s/-Wswitch-default// | sed s/-Wextra//` -util/bitmap.o: ../../lib/bitmap.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/bitmap.o -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/bitmap.o: ../../lib/bitmap.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -util/hweight.o: ../../lib/hweight.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/hweight.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/hweight.o: ../../lib/hweight.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -util/find_next_bit.o: ../../lib/find_next_bit.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/find_next_bit.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/find_next_bit.o: ../../lib/find_next_bit.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-perl.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< +$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< -scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o scripts/perl/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< +$(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< -util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-python.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< +$(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< -scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c PERF-CFLAGS - $(QUIET_CC)$(CC) -o scripts/python/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< +$(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< -perf-%$X: %.o $(PERFLIBS) +$(OUTPUT)perf-%$X: %.o $(PERFLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) @@ -974,17 +986,17 @@ cscope: TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) -PERF-CFLAGS: .FORCE-PERF-CFLAGS +$(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS @FLAGS='$(TRACK_CFLAGS)'; \ - if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`" ; then \ + if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ echo 1>&2 " * new build flags or prefix"; \ - echo "$$FLAGS" >PERF-CFLAGS; \ + echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ fi # We need to apply sq twice, once to protect from the shell -# that runs PERF-BUILD-OPTIONS, and then again to protect it +# that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it # and the first level quoting from the shell that runs "echo". -PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS +$(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ @@ -1005,7 +1017,7 @@ all:: $(TEST_PROGRAMS) export NO_SVN_TESTS -check: common-cmds.h +check: $(OUTPUT)common-cmds.h if sparse; \ then \ for i in *.c */*.c; \ @@ -1039,10 +1051,10 @@ export perfexec_instdir install: all $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' - $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' - $(INSTALL) perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' + $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' @@ -1056,7 +1068,7 @@ ifdef BUILT_INS $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' ifneq (,$X) - $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) endif endif @@ -1140,14 +1152,14 @@ clean: $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE) $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X $(RM) $(TEST_PROGRAMS) - $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* + $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(RM) -r autom4te.cache $(RM) config.log config.mak.autogen config.mak.append config.status config.cache $(RM) -r $(PERF_TARNAME) .doc-tmp-dir $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz $(MAKE) -C Documentation/ clean - $(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS + $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS .PHONY: all install clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 54552a0..49ece79 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -1,6 +1,10 @@ #!/bin/sh -GVF=PERF-VERSION-FILE +if [ $# -eq 1 ] ; then + OUTPUT=$1 +fi + +GVF=${OUTPUT}PERF-VERSION-FILE DEF_VER=v0.0.2.PERF LF=' -- cgit v0.10.2 From 70162138c91b040da3162fe1f34fe8aaf6506f10 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 30 Mar 2010 18:27:39 -0300 Subject: perf record: Add a fallback to the reference relocation symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usually "_text" is enough, but I received reports that its not always available, so fallback to "_stext" for the symbol we use to check if we need to apply any relocation to all the symbols in the kernel symtab, for when, for instance, kexec is being used. Reported-by: Darren Hart Cc: Darren Hart Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 60ecdd3..80dc444 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -575,6 +575,9 @@ static int __cmd_record(int argc, const char **argv) err = event__synthesize_kernel_mmap(process_synthesized_event, session, "_text"); + if (err < 0) + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, "_stext"); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; -- cgit v0.10.2 From a4e3b956a820162b7c1d616117b4f23b6017f504 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 31 Mar 2010 11:33:40 -0300 Subject: perf hist: Replace ->print() routines by ->snprintf() equivalents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Then hist_entry__fprintf will just us the newly introduced hist_entry__snprintf, add the newline and fprintf it to the supplied FILE descriptor. This allows us to remove the use_browser checking in the color_printf routines, that now got color_snprintf variants too. The newt TUI browser (and other GUIs that may come in the future) don't have to worry about stdio specific stuff in the strings they get from the se->snprintf routines and instead use whatever means to do the equivalent. Also the newt TUI browser don't have to use the fmemopen() hack, instead it can use the se->snprintf routines directly. For now tho use the hist_entry__snprintf routine to reduce the patch size. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 9da0191..e191eb9 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -166,6 +166,31 @@ int perf_color_default_config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } +static int __color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args, const char *trail) +{ + int r = 0; + + /* + * Auto-detect: + */ + if (perf_use_color_default < 0) { + if (isatty(1) || pager_in_use()) + perf_use_color_default = 1; + else + perf_use_color_default = 0; + } + + if (perf_use_color_default && *color) + r += snprintf(bf, size, "%s", color); + r += vsnprintf(bf + r, size - r, fmt, args); + if (perf_use_color_default && *color) + r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); + if (trail) + r += snprintf(bf + r, size - r, "%s", trail); + return r; +} + static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { @@ -191,11 +216,28 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, return r; } +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args) +{ + return __color_vsnprintf(bf, size, color, fmt, args, NULL); +} + int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args) { return __color_vfprintf(fp, color, fmt, args, NULL); } +int color_snprintf(char *bf, size_t size, const char *color, + const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = color_vsnprintf(bf, size, color, fmt, args); + va_end(args); + return r; +} int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) { @@ -203,10 +245,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int r; va_start(args, fmt); - if (use_browser) - r = vfprintf(fp, fmt, args); - else - r = color_vfprintf(fp, color, fmt, args); + r = color_vfprintf(fp, color, fmt, args); va_end(args); return r; } @@ -277,3 +316,9 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } + +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent) +{ + const char *color = get_percent_color(percent); + return color_snprintf(bf, size, color, fmt, percent); +} diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 24e8809..dea082b 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -32,10 +32,14 @@ int perf_color_default_config(const char *var, const char *value, void *cb); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); +int color_vsnprintf(char *bf, size_t size, const char *color, + const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); +int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a46d093..f079491 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -455,16 +455,17 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__snprintf(struct hist_entry *self, + char *s, size_t size, struct perf_session *pair_session, bool show_displacement, - long displacement, FILE *fp, + long displacement, bool color, u64 session_total) { struct sort_entry *se; u64 count, total; const char *sep = symbol_conf.field_sep; - size_t ret; + int ret; if (symbol_conf.exclude_other && !self->parent) return 0; @@ -477,17 +478,22 @@ size_t hist_entry__fprintf(struct hist_entry *self, total = session_total; } - if (total) - ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); - else - ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); + if (total) { + if (color) + ret = percent_color_snprintf(s, size, + sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + else + ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + } else + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); if (symbol_conf.show_nr_samples) { if (sep) - ret += fprintf(fp, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); else - ret += fprintf(fp, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", count); } if (pair_session) { @@ -507,9 +513,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%11.11s", bf); + ret += snprintf(s + ret, size - ret, "%11.11s", bf); if (show_displacement) { if (displacement) @@ -518,9 +524,9 @@ size_t hist_entry__fprintf(struct hist_entry *self, snprintf(bf, sizeof(bf), " "); if (sep) - ret += fprintf(fp, "%c%s", *sep, bf); + ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); else - ret += fprintf(fp, "%6.6s", bf); + ret += snprintf(s + ret, size - ret, "%6.6s", bf); } } @@ -528,11 +534,25 @@ size_t hist_entry__fprintf(struct hist_entry *self, if (se->elide) continue; - ret += fprintf(fp, "%s", sep ?: " "); - ret += se->print(fp, self, se->width ? *se->width : 0); + ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); + ret += se->snprintf(self, s + ret, size - ret, + se->width ? *se->width : 0); } - return ret + fprintf(fp, "\n"); + return ret; +} + +int hist_entry__fprintf(struct hist_entry *self, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp, + u64 session_total) +{ + char bf[512]; + hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + show_displacement, displacement, + true, session_total); + return fprintf(fp, "%s\n", bf); } static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6a8c1..ad17f0a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -18,11 +18,16 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -size_t hist_entry__fprintf(struct hist_entry *self, +int hist_entry__fprintf(struct hist_entry *self, struct perf_session *pair_session, bool show_displacement, long displacement, FILE *fp, u64 session_total); +int hist_entry__snprintf(struct hist_entry *self, + char *bf, size_t size, + struct perf_session *pair_session, + bool show_displacement, long displacement, + bool color, u64 session_total); void hist_entry__free(struct hist_entry *); u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index b0210ae..edd628f 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -294,60 +294,17 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } } -/* - * FIXME: get lib/string.c linked with perf somehow - */ -static char *skip_spaces(const char *str) -{ - while (isspace(*str)) - ++str; - return (char *)str; -} - -static char *strim(char *s) -{ - size_t size; - char *end; - - s = skip_spaces(s); - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - return s; -} - static size_t hist_entry__append_browser(struct hist_entry *self, newtComponent tree, u64 total) { - char bf[1024], *s; - FILE *fp; + char s[256]; + size_t ret; if (symbol_conf.exclude_other && !self->parent) return 0; - fp = fmemopen(bf, sizeof(bf), "w"); - if (fp == NULL) - return 0; - - hist_entry__fprintf(self, NULL, false, 0, fp, total); - fclose(fp); - - /* - * FIXME: We shouldn't need to trim, as the printing routines shouldn't - * add spaces it in the first place, the stdio output routines should - * call a __snprintf method instead of the current __print (that - * actually is a __fprintf) one, but get the raw string and _then_ add - * the newline, as this is a detail of stdio printing, not needed in - * other UIs, e.g. newt. - */ - s = strim(bf); - + ret = hist_entry__snprintf(self, s, sizeof(s), NULL, + false, 0, false, total); if (symbol_conf.use_callchain) { int indexes[2]; @@ -357,7 +314,7 @@ static size_t hist_entry__append_browser(struct hist_entry *self, } else newtListboxAppendEntry(tree, s, &self->ms); - return strlen(s); + return ret; } static void map_symbol__annotate_browser(const struct map_symbol *self) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9b80c13..31329a1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -18,10 +18,21 @@ char * field_sep; LIST_HEAD(hist_entry__sort_list); +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width); + struct sort_entry sort_thread = { .header = "Command: Pid", .cmp = sort__thread_cmp, - .print = sort__thread_print, + .snprintf = hist_entry__thread_snprintf, .width = &threads__col_width, }; @@ -29,27 +40,27 @@ struct sort_entry sort_comm = { .header = "Command", .cmp = sort__comm_cmp, .collapse = sort__comm_collapse, - .print = sort__comm_print, + .snprintf = hist_entry__comm_snprintf, .width = &comms__col_width, }; struct sort_entry sort_dso = { .header = "Shared Object", .cmp = sort__dso_cmp, - .print = sort__dso_print, + .snprintf = hist_entry__dso_snprintf, .width = &dsos__col_width, }; struct sort_entry sort_sym = { .header = "Symbol", .cmp = sort__sym_cmp, - .print = sort__sym_print, + .snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { .header = "Parent symbol", .cmp = sort__parent_cmp, - .print = sort__parent_print, + .snprintf = hist_entry__parent_snprintf, .width = &parent_symbol__col_width, }; @@ -85,45 +96,38 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) return right->thread->pid - left->thread->pid; } -int repsep_fprintf(FILE *fp, const char *fmt, ...) +static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) { int n; va_list ap; va_start(ap, fmt); - if (!field_sep) - n = vfprintf(fp, fmt, ap); - else { - char *bf = NULL; - n = vasprintf(&bf, fmt, ap); - if (n > 0) { - char *sep = bf; - - while (1) { - sep = strchr(sep, *field_sep); - if (sep == NULL) - break; - *sep = '.'; - } + n = vsnprintf(bf, size, fmt, ap); + if (field_sep && n > 0) { + char *sep = bf; + + while (1) { + sep = strchr(sep, *field_sep); + if (sep == NULL) + break; + *sep = '.'; } - fputs(bf, fp); - free(bf); } va_end(ap); return n; } -size_t -sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s:%5d", width - 6, + return repsep_snprintf(bf, size, "%*s:%5d", width, self->thread->comm ?: "", self->thread->pid); } -size_t -sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%*s", width, self->thread->comm); + return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); } /* --sort dso */ @@ -149,16 +153,16 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(dso_name_l, dso_name_r); } -size_t -sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { if (self->ms.map && self->ms.map->dso) { const char *dso_name = !verbose ? self->ms.map->dso->short_name : self->ms.map->dso->long_name; - return repsep_fprintf(fp, "%-*s", width, dso_name); + return repsep_snprintf(bf, size, "%-*s", width, dso_name); } - return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); + return repsep_snprintf(bf, size, "%*Lx", width, self->ip); } /* --sort symbol */ @@ -177,22 +181,22 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) return (int64_t)(ip_r - ip_l); } - -size_t -sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width __used) { size_t ret = 0; if (verbose) { char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); + ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); } - ret += repsep_fprintf(fp, "[%c] ", self->level); + ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); if (self->ms.sym) - ret += repsep_fprintf(fp, "%s", self->ms.sym->name); + ret += repsep_snprintf(bf + ret, size - ret, "%s", + self->ms.sym->name); else - ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); + ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); return ret; } @@ -231,10 +235,10 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) return strcmp(sym_l->name, sym_r->name); } -size_t -sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) +static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, + size_t size, unsigned int width) { - return repsep_fprintf(fp, "%-*s", width, + return repsep_snprintf(bf, size, "%-*s", width, self->parent ? self->parent->name : "[other]"); } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5985686..439ec5f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -76,7 +76,8 @@ struct sort_entry { int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); + int (*snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); unsigned int *width; bool elide; }; @@ -86,7 +87,6 @@ extern struct list_head hist_entry__sort_list; void setup_sorting(const char * const usagestr[], const struct option *opts); -extern int repsep_fprintf(FILE *fp, const char *fmt, ...); extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); -- cgit v0.10.2 From e727ca73f85d4c5be3547eda674168219d1c22d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 1 Apr 2010 19:12:13 -0300 Subject: perf kmem: Resolve kernel symbols again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to the assumption in perf_session__new that the kernel maps would be created using the fake PERF_RECORD_MMAP event in a perf.data file 'perf kmem --stat caller', that doesn't have such event, ends up not being able to resolve the kernel addresses. Fix it by calling perf_session__create_kernel_maps() in __cmd_kmem(). LKML-Reference: Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 32edb6a..7cbb5eb 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -489,6 +489,9 @@ static int __cmd_kmem(void) if (session == NULL) return -ENOMEM; + if (perf_session__create_kernel_maps(session) < 0) + goto out_delete; + if (!perf_session__has_traces(session, "kmem record")) goto out_delete; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 32765cd..9da5e72 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -52,11 +52,6 @@ out_close: return -1; } -static inline int perf_session__create_kernel_maps(struct perf_session *self) -{ - return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); -} - struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 6a15dae..dffaff5 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -80,6 +80,11 @@ static inline int __perf_session__create_kernel_maps(struct perf_session *self, self->vmlinux_maps, kernel); } +static inline int perf_session__create_kernel_maps(struct perf_session *self) +{ + return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); +} + static inline struct map * perf_session__new_module_map(struct perf_session *self, u64 start, const char *filename) -- cgit v0.10.2 From 71cf8b8ff7d6a79af086be9e4c72628da9d62d58 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 1 Apr 2010 21:24:38 -0300 Subject: perf kmem: Fixup the symbol address before using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We get absolute addresses in the events, but relative ones from the symbol subsystem, so calculate the absolute address by asking for the map where the symbol was found, that has the place where the DSO was actually loaded. For the core kernel this poses no problems if the kernel is not relocated by things like kexec, or if we use /proc/kallsyms, but for modules we were getting really large, negative offsets. LKML-Reference: Cc: Frédéric Weisbecker Cc: Li Zefan Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 7cbb5eb..513aa8a 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -363,6 +363,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; + struct map *map; char buf[BUFSIZ]; u64 addr; @@ -370,13 +371,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session, addr = data->call_site; if (!raw_ip) sym = map_groups__find_function(&session->kmaps, - addr, NULL, NULL); + addr, &map, NULL); } else addr = data->ptr; if (sym != NULL) snprintf(buf, sizeof(buf), "%s+%Lx", sym->name, - addr - sym->start); + addr - map->unmap_ip(map, sym->start)); else snprintf(buf, sizeof(buf), "%#Lx", addr); printf(" %-34s |", buf); -- cgit v0.10.2 From b9fb93047756c5e4129dfda7591612de61b0e877 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 2 Apr 2010 09:50:42 -0300 Subject: perf hist: Only allocate callchain_node if processing callchains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The struct callchain_node size is 120 bytes, that are never used when there are no callchains or '-g none' is specified, so conditionally allocate it, reducing sizeof(struct hist_entry) from 210 bytes to only 96, greatly speeding the non-callchain processing. LKML-Reference: Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3819185..1fb13e5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -110,8 +110,8 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (symbol_conf.use_callchain) { if (!hit) - callchain_init(&he->callchain); - err = append_chain(&he->callchain, data->callchain, syms); + callchain_init(he->callchain); + err = append_chain(he->callchain, data->callchain, syms); free(syms); if (err) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f079491..18cf8b3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -50,7 +50,8 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he)); + he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? + sizeof(struct callchain_node) : 0)); if (!he) return NULL; *he = entry; @@ -168,7 +169,7 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, struct hist_entry *iter; if (symbol_conf.use_callchain) - callchain_param.sort(&he->sorted_chain, &he->callchain, + callchain_param.sort(&he->sorted_chain, he->callchain, min_callchain_hits, &callchain_param); while (*p != NULL) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 439ec5f..5bf2b74 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -49,12 +49,12 @@ struct hist_entry { u64 ip; char level; struct symbol *parent; - struct callchain_node callchain; union { unsigned long position; struct hist_entry *pair; struct rb_root sorted_chain; }; + struct callchain_node callchain[0]; }; enum sort_type { -- cgit v0.10.2 From ad5b217b152d99ca3922153500c619d9758dd87a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 2 Apr 2010 10:04:18 -0300 Subject: perf session: Remove one more exit() call from library code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return NULL instead and make the caller propagate the error. LKML-Reference: Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1fb13e5..6767f10 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -89,9 +89,12 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct event_stat_id *stats; struct perf_event_attr *attr; - if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) + if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { syms = perf_session__resolve_callchain(self, al->thread, data->callchain, &parent); + if (syms == NULL) + return -ENOMEM; + } attr = perf_header__find_attr(data->id, &self->header); if (attr) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9da5e72..ddf288f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -118,16 +118,11 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, struct symbol **parent) { u8 cpumode = PERF_RECORD_MISC_USER; - struct map_symbol *syms = NULL; unsigned int i; + struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); - if (symbol_conf.use_callchain) { - syms = calloc(chain->nr, sizeof(*syms)); - if (!syms) { - fprintf(stderr, "Can't allocate memory for symbols\n"); - exit(-1); - } - } + if (!syms) + return NULL; for (i = 0; i < chain->nr; i++) { u64 ip = chain->ips[i]; -- cgit v0.10.2 From 2aefa4f733f2c5ce51dd2316ffecb258463fde71 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 2 Apr 2010 12:30:57 -0300 Subject: perf tools: sort_dimension__add shouldn't die MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate error instead. LKML-Reference: Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6767f10..b13a7e2 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -473,7 +473,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) setup_sorting(report_usage, options); if (parent_pattern != default_parent_pattern) { - sort_dimension__add("parent"); + if (sort_dimension__add("parent") < 0) + return -1; sort_parent.elide = 1; } else symbol_conf.exclude_other = false; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 31329a1..9d24d4b 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -264,9 +264,8 @@ int sort_dimension__add(const char *tok) char err[BUFSIZ]; regerror(ret, &parent_regex, err, sizeof(err)); - fprintf(stderr, "Invalid regex: %s\n%s", - parent_pattern, err); - exit(-1); + pr_err("Invalid regex: %s\n%s", parent_pattern, err); + return -EINVAL; } sort__has_parent = 1; } -- cgit v0.10.2 From e206d556c5793ac5e28c0aaba2e07432e5f9a098 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 10:19:26 -0300 Subject: perf tools: Move the prototypes in util/string.h to util.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we avoid conflict with libc's string.h header. Reviewed-by: KOSAKI Motohiro Suggested-by: KOSAKI Motohiro Cc: KOSAKI Motohiro Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 93029c0..9f5a47e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -385,7 +385,6 @@ LIB_H += util/header.h LIB_H += util/help.h LIB_H += util/session.h LIB_H += util/strbuf.h -LIB_H += util/string.h LIB_H += util/strlist.h LIB_H += util/svghelper.h LIB_H += util/run-command.h diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 8977317..52e646e 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -10,7 +10,6 @@ #include "../perf.h" #include "../util/util.h" #include "../util/parse-options.h" -#include "../util/string.h" #include "../util/header.h" #include "bench.h" diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 887e8e0..ee0d917 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -14,7 +14,6 @@ #include "util/cache.h" #include #include "util/symbol.h" -#include "util/string.h" #include "perf.h" #include "util/debug.h" diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 80dc444..dc61f1b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -15,7 +15,6 @@ #include "util/util.h" #include "util/parse-options.h" #include "util/parse-events.h" -#include "util/string.h" #include "util/header.h" #include "util/event.h" diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b13a7e2..6615e09 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -14,7 +14,6 @@ #include "util/cache.h" #include #include "util/symbol.h" -#include "util/string.h" #include "util/callchain.h" #include "util/strlist.h" #include "util/values.h" diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 0d4d8ff..266e7aa 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -21,7 +21,6 @@ #include "util/cache.h" #include #include "util/symbol.h" -#include "util/string.h" #include "util/callchain.h" #include "util/strlist.h" diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 2826e70..d4be55b 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,7 +13,6 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" -#include "util/string.h" #include "util/debugfs.h" bool use_browser; diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h deleted file mode 100644 index 7005824..0000000 --- a/tools/perf/util/string.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __PERF_STRING_H_ -#define __PERF_STRING_H_ - -#include -#include "types.h" - -s64 perf_atoll(const char *str); -char **argv_split(const char *str, int *argcp); -void argv_free(char **argv); -bool strglobmatch(const char *str, const char *pat); -bool strlazymatch(const char *str, const char *pat); - -#define _STR(x) #x -#define STR(x) _STR(x) - -#endif /* __PERF_STRING_H */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 5270108..fbf45d1 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -42,12 +42,14 @@ #define _ALL_SOURCE 1 #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 +#define HAS_BOOL #include #include #include #include #include +#include #include #include #include @@ -78,6 +80,7 @@ #include #include #include "../../../include/linux/magic.h" +#include "types.h" #ifndef NO_ICONV @@ -415,4 +418,13 @@ void git_qsort(void *base, size_t nmemb, size_t size, int mkdir_p(char *path, mode_t mode); int copyfile(const char *from, const char *to); +s64 perf_atoll(const char *str); +char **argv_split(const char *str, int *argcp); +void argv_free(char **argv); +bool strglobmatch(const char *str, const char *pat); +bool strlazymatch(const char *str, const char *pat); + +#define _STR(x) #x +#define STR(x) _STR(x) + #endif -- cgit v0.10.2 From 4af8b35db6634dd1e0d616de689582b6c93550af Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sat, 3 Apr 2010 22:53:31 +1100 Subject: perf symbols: Fill in pgoff in mmap synthesized events When we synthesize mmap events we need to fill in the pgoff field. I wasn't able to test this completely since I couldn't find an executable region with a non 0 offset. We will see it when we start doing data profiling. Signed-off-by: Anton Blanchard Cc: David Miller Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <20100403115331.GK5594@kryten> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 052eaec..571fb25 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -130,6 +130,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, continue; pbf += n + 3; if (*pbf == 'x') { /* vm_exec */ + u64 vm_pgoff; char *execname = strchr(bf, '/'); /* Catch VDSO */ @@ -139,6 +140,14 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, if (execname == NULL) continue; + pbf += 3; + n = hex2u64(pbf, &vm_pgoff); + /* pgoff is in bytes, not pages */ + if (n >= 0) + ev.mmap.pgoff = vm_pgoff << getpagesize(); + else + ev.mmap.pgoff = 0; + size = strlen(execname); execname[size - 1] = '\0'; /* Remove \n */ memcpy(ev.mmap.filename, execname, size); -- cgit v0.10.2 From fb6b893180faec03e1d32149ef5cc412df9714df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:04:55 -0300 Subject: perf newt: Remove useless column width calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not used in the TUI interface. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index edd628f..a2ba3ff 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -390,11 +390,8 @@ static void perf_session__selection(newtComponent self, void *data) int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline) { - struct sort_entry *se; struct rb_node *nd; char seq[] = "."; - unsigned int width; - char *col_width = symbol_conf.col_width_list_str; int rows, cols, idx; int max_len = 0; char str[1024]; @@ -423,23 +420,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, newtComponentAddCallback(tree, perf_session__selection, &selection); - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - width = strlen(se->header); - if (se->width) { - if (symbol_conf.col_width_list_str) { - if (col_width) { - *se->width = atoi(col_width); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } - } - *se->width = max(*se->width, width); - } - } - idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); -- cgit v0.10.2 From e65713ea1e61e92d28284a55df2aa039ebe10003 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:25:56 -0300 Subject: perf newt: Move the hist browser population bits to separare function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next patches will use that when applying filtes to then repopulate the browser with the narrowed vision. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index a2ba3ff..509d921 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -381,53 +381,69 @@ static const void *newt__symbol_tree_get_current(newtComponent self) return newtListboxGetCurrent(self); } -static void perf_session__selection(newtComponent self, void *data) +static void hist_browser__selection(newtComponent self, void *data) { const struct map_symbol **symbol_ptr = data; *symbol_ptr = newt__symbol_tree_get_current(self); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline) +struct hist_browser { + newtComponent form, tree; + const struct map_symbol *selection; +}; + +static struct hist_browser *hist_browser__new(void) +{ + struct hist_browser *self = malloc(sizeof(*self)); + + if (self != NULL) { + char seq[] = "."; + int rows; + + newtGetScreenSize(NULL, &rows); + + if (symbol_conf.use_callchain) + self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + self->tree = newtListbox(0, 0, rows - 5, + (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); + newtComponentAddCallback(self->tree, hist_browser__selection, + &self->selection); + } + + return self; +} + +static void hist_browser__delete(struct hist_browser *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + free(self); +} + +static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, + u64 nr_hists, u64 session_total) { + int max_len = 0, idx, cols, rows; + struct ui_progress *progress; struct rb_node *nd; - char seq[] = "."; - int rows, cols, idx; - int max_len = 0; - char str[1024]; - newtComponent form, tree; - struct newtExitStruct es; - const struct map_symbol *selection; u64 curr_hist = 0; - struct ui_progress *progress; progress = ui_progress__new("Adding entries to the browser...", nr_hists); if (progress == NULL) return -1; - snprintf(str, sizeof(str), "Samples: %Ld", session_total); - newtDrawRootText(0, 0, str); - newtPushHelpLine(helpline); - - newtGetScreenSize(&cols, &rows); - - if (symbol_conf.use_callchain) - tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(tree, perf_session__selection, &selection); - idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len = hist_entry__append_browser(h, tree, session_total); + int len = hist_entry__append_browser(h, self->tree, session_total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, tree, session_total, idx++); + hist_entry__append_callchain_browser(h, self->tree, + session_total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -435,27 +451,50 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, ui_progress__delete(progress); + newtGetScreenSize(&cols, &rows); + if (max_len > cols) max_len = cols - 3; if (!symbol_conf.use_callchain) - newtListboxSetWidth(tree, max_len); + newtListboxSetWidth(self->tree, max_len); newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), rows - 5, "Report"); - form = newt_form__new(); - newtFormAddHotKey(form, 'A'); - newtFormAddHotKey(form, 'a'); - newtFormAddHotKey(form, NEWT_KEY_RIGHT); - newtFormAddComponents(form, tree, NULL); - selection = newt__symbol_tree_get_current(tree); + self->form = newt_form__new(); + newtFormAddHotKey(self->form, 'A'); + newtFormAddHotKey(self->form, 'a'); + newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); + newtFormAddComponents(self->form, self->tree, NULL); + self->selection = newt__symbol_tree_get_current(self->tree); + + return 0; +} + +int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, + u64 session_total, const char *helpline) +{ + struct newtExitStruct es; + char str[1024]; + int err = -1; + struct hist_browser *browser = hist_browser__new(); + + if (browser == NULL) + return -1; + + snprintf(str, sizeof(str), "Samples: %Ld", session_total); + newtDrawRootText(0, 0, str); + newtPushHelpLine(helpline); + + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; while (1) { char annotate[512]; const char *options[2]; int nr_options = 0, choice = 0; - newtFormRun(form, &es); + newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { if (toupper(es.u.key) == 'A') goto do_annotate; @@ -469,9 +508,9 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, } } - if (selection->sym != NULL) { + if (browser->selection->sym != NULL) { snprintf(annotate, sizeof(annotate), - "Annotate %s", selection->sym->name); + "Annotate %s", browser->selection->sym->name); options[nr_options++] = annotate; } @@ -480,21 +519,21 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (choice == nr_options - 1) break; do_annotate: - if (selection->sym != NULL && choice >= 0) { - if (selection->map->dso->origin == DSO__ORIG_KERNEL) { + if (browser->selection->sym != NULL && choice >= 0) { + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { newtPopHelpLine(); newtPushHelpLine("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(selection); + map_symbol__annotate_browser(browser->selection); } } - - newtFormDestroy(form); - newtPopWindow(); - return 0; + err = 0; +out: + hist_browser__delete(browser); + return err; } void setup_browser(void) -- cgit v0.10.2 From 533c46c31c0e82f19dbb087c77d85eaccd6fefdb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 11:54:35 -0300 Subject: perf newt: Pass the input_name to perf_session__browse_hists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that it can use it in the 'perf annotate' command line, otherwise it'll use the default and not the specified -i filename passed to 'perf report'. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6615e09..e93c69a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -313,7 +313,8 @@ static int __cmd_report(void) stats->stats.total); if (use_browser) perf_session__browse_hists(&stats->hists, nr_hists, - stats->stats.total, help); + stats->stats.total, help, + input_name); else { if (rb_first(&session->stats_by_id) == rb_last(&session->stats_by_id)) diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 509d921..c93bc2a 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -317,7 +317,8 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self) +static void map_symbol__annotate_browser(const struct map_symbol *self, + const char *input_name) { FILE *fp; int cols, rows; @@ -331,8 +332,8 @@ static void map_symbol__annotate_browser(const struct map_symbol *self) if (self->sym == NULL) return; - if (asprintf(&str, "perf annotate -d \"%s\" %s 2>&1 | expand", - self->map->dso->name, self->sym->name) < 0) + if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", + input_name, self->map->dso->name, self->sym->name) < 0) return; fp = popen(str, "r"); @@ -472,7 +473,8 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline) + u64 session_total, const char *helpline, + const char *input_name) { struct newtExitStruct es; char str[1024]; @@ -527,7 +529,8 @@ do_annotate: "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection); + map_symbol__annotate_browser(browser->selection, + input_name); } } err = 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index dffaff5..27f4c2d 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -96,12 +96,14 @@ static inline struct map * static inline int perf_session__browse_hists(struct rb_root *hists __used, u64 nr_hists __used, u64 session_total __used, - const char *helpline __used) + const char *helpline __used, + const char *input_name __used) { return 0; } #else int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline); + u64 session_total, const char *helpline, + const char *input_name); #endif #endif /* __PERF_SESSION_H */ -- cgit v0.10.2 From 83753190c136901c916df267703937e60f24b8b8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 16:30:44 -0300 Subject: perf newt: Add a "Zoom into foo.so DSO" and reverse operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking on -> will bring as one of the popup menu options a "Zoom into CURRENT DSO", i.e. CURRENT will be replaced by the name of the DSO in the current line. Choosing this option will filter out all samples that didn't took place in a symbol in this DSO. After that the option reverts to "Zoom out of CURRENT DSO", to allow going back to the more compreensive view, not filtered by DSO. Future similar operations will include zooming into a particular thread, COMM, CPU, "last minute", "last N usecs", etc. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c93bc2a..bbf725d 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -94,7 +94,7 @@ static newtComponent newt_form__new(void) return self; } -static int popup_menu(int argc, const char *argv[]) +static int popup_menu(int argc, char * const argv[]) { struct newtExitStruct es; int i, rc = -1, max_len = 5; @@ -397,22 +397,8 @@ static struct hist_browser *hist_browser__new(void) { struct hist_browser *self = malloc(sizeof(*self)); - if (self != NULL) { - char seq[] = "."; - int rows; - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - } + if (self != NULL) + self->form = NULL; return self; } @@ -431,6 +417,30 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; + char seq[] = "."; + char str[256]; + + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + snprintf(str, sizeof(str), "Samples: %Ld ", + session_total); + newtDrawRootText(0, 0, str); + + newtGetScreenSize(NULL, &rows); + + if (symbol_conf.use_callchain) + self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, + NEWT_FLAG_SCROLL); + else + self->tree = newtListbox(0, 0, rows - 5, + (NEWT_FLAG_SCROLL | + NEWT_FLAG_RETURNEXIT)); + + newtComponentAddCallback(self->tree, hist_browser__selection, + &self->selection); progress = ui_progress__new("Adding entries to the browser...", nr_hists); if (progress == NULL) @@ -439,7 +449,12 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his idx = 0; for (nd = rb_first(hists); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len = hist_entry__append_browser(h, self->tree, session_total); + int len; + + if (h->filtered) + continue; + + len = hist_entry__append_browser(h, self->tree, session_total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) @@ -463,6 +478,9 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), rows - 5, "Report"); self->form = newt_form__new(); + if (self->form == NULL) + return -1; + newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); @@ -472,29 +490,50 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } +static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, + u64 *session_total) +{ + struct rb_node *nd; + u64 nr_hists = 0; + + *session_total = 0; + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered = true; + continue; + } + h->filtered = false; + ++nr_hists; + *session_total += h->count; + } + + return nr_hists; +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { struct newtExitStruct es; - char str[1024]; + bool dso_filtered = false; int err = -1; struct hist_browser *browser = hist_browser__new(); if (browser == NULL) return -1; - snprintf(str, sizeof(str), "Samples: %Ld", session_total); - newtDrawRootText(0, 0, str); newtPushHelpLine(helpline); if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) goto out; while (1) { - char annotate[512]; - const char *options[2]; - int nr_options = 0, choice = 0; + char *options[16]; + int nr_options = 0, choice = 0, i, + annotate = -2, zoom_dso = -2; newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { @@ -510,18 +549,29 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, } } - if (browser->selection->sym != NULL) { - snprintf(annotate, sizeof(annotate), - "Annotate %s", browser->selection->sym->name); - options[nr_options++] = annotate; - } + if (browser->selection->sym != NULL && + asprintf(&options[nr_options], "Annotate %s", + browser->selection->sym->name) > 0) + annotate = nr_options++; + + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + dso_filtered ? "out of" : "into", + (browser->selection->map->dso->kernel ? "the Kernel" : + browser->selection->map->dso->short_name)) > 0) + zoom_dso = nr_options++; + + options[nr_options++] = (char *)"Exit"; - options[nr_options++] = "Exit"; choice = popup_menu(nr_options, options); + + for (i = 0; i < nr_options - 1; ++i) + free(options[i]); + if (choice == nr_options - 1) break; do_annotate: - if (browser->selection->sym != NULL && choice >= 0) { + if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { newtPopHelpLine(); newtPushHelpLine("No vmlinux file found, can't " @@ -531,6 +581,13 @@ do_annotate: } map_symbol__annotate_browser(browser->selection, input_name); + } if (choice == zoom_dso) { + hists__filter_by_dso(hists, + dso_filtered ? NULL : browser->selection->map->dso, + &session_total); + dso_filtered = !dso_filtered; + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; } } err = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5bf2b74..dce79d3 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,7 +48,8 @@ struct hist_entry { struct map_symbol ms; u64 ip; char level; - struct symbol *parent; + bool filtered; + struct symbol *parent; union { unsigned long position; struct hist_entry *pair; -- cgit v0.10.2 From a5e29aca02fcecd086ac160ea29244cae6b4305e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 3 Apr 2010 22:44:37 -0300 Subject: perf TUI: Add a "Zoom into COMM(PID) thread" and reverse operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now one can press the right arrow key and in addition to being able to filter by DSO, filter out by thread too, or a combination of both filters. With this one can start collecting events for the whole system, then focus on a subset of the collected data quickly. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index bbf725d..6d6e022 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -490,6 +490,11 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, u64 *session_total) { @@ -502,10 +507,10 @@ static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered = true; + h->filtered |= (1 << HIST_FILTER__DSO); continue; } - h->filtered = false; + h->filtered &= ~(1 << HIST_FILTER__DSO); ++nr_hists; *session_total += h->count; } @@ -513,12 +518,54 @@ static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, return nr_hists; } +static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, + u64 *session_total) +{ + struct rb_node *nd; + u64 nr_hists = 0; + + *session_total = 0; + + for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + ++nr_hists; + *session_total += h->count; + } + + return nr_hists; +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + int *indexes; + + if (!symbol_conf.use_callchain) + goto out; + + indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); + if (indexes) { + bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; + free(indexes); + if (is_hist_entry) + goto out; + } + return NULL; +out: + return *(struct thread **)(self->selection + 1); +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { struct newtExitStruct es; - bool dso_filtered = false; + bool dso_filtered = false, thread_filtered = false; int err = -1; struct hist_browser *browser = hist_browser__new(); @@ -531,9 +578,10 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, goto out; while (1) { + const struct thread *thread; char *options[16]; int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2; + annotate = -2, zoom_dso = -2, zoom_thread = -2; newtFormRun(browser->form, &es); if (es.reason == NEWT_EXIT_HOTKEY) { @@ -561,6 +609,13 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, browser->selection->map->dso->short_name)) > 0) zoom_dso = nr_options++; + thread = hist_browser__selected_thread(browser); + if (thread != NULL && + asprintf(&options[nr_options], "Zoom %s %s(%d) thread", + (thread_filtered ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), thread->pid) > 0) + zoom_thread = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -570,6 +625,9 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (choice == nr_options - 1) break; + + if (choice == -1) + continue; do_annotate: if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { @@ -581,13 +639,21 @@ do_annotate: } map_symbol__annotate_browser(browser->selection, input_name); - } if (choice == zoom_dso) { - hists__filter_by_dso(hists, - dso_filtered ? NULL : browser->selection->map->dso, - &session_total); + } else if (choice == zoom_dso) { + nr_hists = hists__filter_by_dso(hists, + (dso_filtered ? NULL : + browser->selection->map->dso), + &session_total); dso_filtered = !dso_filtered; if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) goto out; + } else if (choice == zoom_thread) { + nr_hists = hists__filter_by_thread(hists, + (thread_filtered ? NULL : thread), + &session_total); + thread_filtered = !thread_filtered; + if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + goto out; } } err = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index dce79d3..6d7b4be 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -44,11 +44,16 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; u64 count; - struct thread *thread; + /* + * XXX WARNING! + * thread _has_ to come after ms, see + * hist_browser__selected_thread in util/newt.c + */ struct map_symbol ms; + struct thread *thread; u64 ip; char level; - bool filtered; + u8 filtered; struct symbol *parent; union { unsigned long position; -- cgit v0.10.2 From 6f4dee06fbf0133917f3d76fa3fb50e18b10c1f5 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 18 Mar 2010 23:47:01 +0100 Subject: perf: Drop the frame reliablity check It is useless now that we have a pure stack frame walker, as given addr are always reliable. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 65e9c5e..353a174 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1602,8 +1602,7 @@ static void backtrace_address(void *data, unsigned long addr, int reliable) { struct perf_callchain_entry *entry = data; - if (reliable) - callchain_store(entry, addr); + callchain_store(entry, addr); } static const struct stacktrace_ops backtrace_ops = { -- cgit v0.10.2 From 6cc8a7c1d8560c042f486b23318a6291569ab96b Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 19 Mar 2010 01:23:53 +0100 Subject: perf: Fetch hot regs from the template caller Trace events can be defined from a template using DECLARE_EVENT_CLASS/DEFINE_EVENT or directly with TRACE_EVENT. In both cases we have a template tracepoint handler, used to record the trace, to which we pass our ftrace event instance. In the function level, if the class is named "foo" and the event is named "blah", we have the following chain of calls: perf_trace_blah() -> perf_trace_templ_foo() In the case we have several events sharing the class "blah", we'll have multiple users of perf_trace_templ_foo(), and it won't be inlined by the compiler. This is usually what happens with the DECLARE_EVENT_CLASS/DEFINE_EVENT based definition. But if perf_trace_blah() is the only caller of perf_trace_templ_foo() there are fair chances that it will be inlined. The problem is that we fetch the regs from perf_trace_templ_foo() after we rewinded the frame pointer to the second caller, we want to reach the caller of perf_trace_blah() to get the right source of the event. And we do this by always assuming that perf_trace_templ_foo() is not inlined. But as shown above this is not always true. And if it is inlined we miss the first caller, losing the most important level of precision. We get: 61.31% ls [kernel.kallsyms] [k] do_softirq | --- do_softirq irq_exit do_IRQ common_interrupt | |--25.00%-- tty_buffer_request_room Instead of: 61.31% ls [kernel.kallsyms] [k] __do_softirq | --- __do_softirq do_softirq irq_exit do_IRQ common_interrupt | |--25.00%-- tty_buffer_request_room To fix this, we fetch the regs from perf_trace_blah() rather than perf_trace_templ_foo() so that we don't have to deal with inlining surprises. That also bring us the advantage of having the true source of the event even if we don't have frame pointers. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index ea6f9d4..882c648 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -758,13 +758,12 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ static notrace void \ perf_trace_templ_##call(struct ftrace_event_call *event_call, \ - proto) \ + struct pt_regs *__regs, proto) \ { \ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ struct ftrace_raw_##call *entry; \ u64 __addr = 0, __count = 1; \ unsigned long irq_flags; \ - struct pt_regs *__regs; \ int __entry_size; \ int __data_size; \ int rctx; \ @@ -785,20 +784,22 @@ perf_trace_templ_##call(struct ftrace_event_call *event_call, \ \ { assign; } \ \ - __regs = &__get_cpu_var(perf_trace_regs); \ - perf_fetch_caller_regs(__regs, 2); \ - \ perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ __count, irq_flags, __regs); \ } #undef DEFINE_EVENT -#define DEFINE_EVENT(template, call, proto, args) \ -static notrace void perf_trace_##call(proto) \ -{ \ - struct ftrace_event_call *event_call = &event_##call; \ - \ - perf_trace_templ_##template(event_call, args); \ +#define DEFINE_EVENT(template, call, proto, args) \ +static notrace void perf_trace_##call(proto) \ +{ \ + struct ftrace_event_call *event_call = &event_##call; \ + struct pt_regs *__regs = &get_cpu_var(perf_trace_regs); \ + \ + perf_fetch_caller_regs(__regs, 1); \ + \ + perf_trace_templ_##template(event_call, __regs, args); \ + \ + put_cpu_var(perf_trace_regs); \ } #undef DEFINE_EVENT_PRINT -- cgit v0.10.2 From 3326c1ceee234e63160852720d48be8a8f7a6d08 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Mar 2010 19:09:33 +0100 Subject: perf_event: Make perf fd non seekable Perf_event does not need seeking, so prevent it in order to get rid of default_llseek, which uses the BKL. Signed-off-by: Arnd Bergmann Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar [drop the nonseekable_open, not needed for anon inodes] Signed-off-by: Frederic Weisbecker diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 63fbce1..4aa50ff 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2645,6 +2645,7 @@ static int perf_fasync(int fd, struct file *filp, int on) } static const struct file_operations perf_fops = { + .llseek = no_llseek, .release = perf_release, .read = perf_read, .poll = perf_poll, -- cgit v0.10.2 From 8141d0050d76e5695011b5ab577ec66fb51a998c Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Sun, 4 Apr 2010 17:13:18 +0900 Subject: perf: Swap inclusion order of util.h and string.h in util/string.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently util/string.c includes headers in this order: string.h, util.h But this causes a build error because __USE_GNU definition is needed for strndup() definition: % make -j touch .perf.dev.null CC util/string.o cc1: warnings being treated as errors util/string.c: In function ‘argv_split’: util/string.c:171: error: implicit declaration of function ‘strndup’ util/string.c:171: error: incompatible implicit declaration of built-in function ‘strndup’ So this patch swaps the headers inclusion order. util.h defines _GNU_SOURCE, and /usr/include/features.h defines __USE_GNU as 1 if _GNU_SOURCE is defined. Signed-off-by: Hitoshi Mitake Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo LKML-Reference: <1270368798-27232-1-git-send-email-mitake@dcl.info.waseda.ac.jp> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index d438924..0409fc7 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -1,5 +1,5 @@ -#include "string.h" #include "util.h" +#include "string.h" #define K 1024LL /* -- cgit v0.10.2 From 6e7ab4c649eb7ed403d970b8eda32ca3745e8024 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 12:02:18 -0300 Subject: perf TUI: Show filters on the title and add help line about how to zoom out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 6d6e022..c0e71aa 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -411,7 +411,7 @@ static void hist_browser__delete(struct hist_browser *self) } static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total) + u64 nr_hists, u64 session_total, const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -476,7 +476,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtListboxSetWidth(self->tree, max_len); newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, "Report"); + rows - 5, title); self->form = newt_form__new(); if (self->form == NULL) return -1; @@ -495,7 +495,7 @@ enum hist_filter { HIST_FILTER__THREAD, }; -static u64 hists__filter_by_dso(struct rb_root *hists, struct dso *dso, +static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, u64 *session_total) { struct rb_node *nd; @@ -560,25 +560,47 @@ out: return *(struct thread **)(self->selection + 1); } +static int hist_browser__title(char *bf, size_t size, const char *input_name, + const struct dso *dso, const struct thread *thread) +{ + int printed = 0; + + if (thread) + printed += snprintf(bf + printed, size - printed, + "Thread: %s(%d)", + (thread->comm_set ? thread->comm : ""), + thread->pid); + if (dso) + printed += snprintf(bf + printed, size - printed, + "%sDSO: %s", thread ? " " : "", + dso->short_name); + return printed ?: snprintf(bf, size, "Report: %s", input_name); +} + int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name) { + struct hist_browser *browser = hist_browser__new(); + const struct thread *thread_filter = NULL; + const struct dso *dso_filter = NULL; struct newtExitStruct es; - bool dso_filtered = false, thread_filtered = false; + char msg[160]; int err = -1; - struct hist_browser *browser = hist_browser__new(); if (browser == NULL) return -1; newtPushHelpLine(helpline); - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; while (1) { const struct thread *thread; + const struct dso *dso; char *options[16]; int nr_options = 0, choice = 0, i, annotate = -2, zoom_dso = -2, zoom_thread = -2; @@ -602,20 +624,21 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, browser->selection->sym->name) > 0) annotate = nr_options++; - if (browser->selection->map != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - dso_filtered ? "out of" : "into", - (browser->selection->map->dso->kernel ? "the Kernel" : - browser->selection->map->dso->short_name)) > 0) - zoom_dso = nr_options++; - thread = hist_browser__selected_thread(browser); if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filtered ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), thread->pid) > 0) + (thread_filter ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), + thread->pid) > 0) zoom_thread = nr_options++; + dso = browser->selection->map ? browser->selection->map->dso : NULL; + if (dso != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + (dso_filter ? "out of" : "into"), + (dso->kernel ? "the Kernel" : dso->short_name)) > 0) + zoom_dso = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -637,22 +660,39 @@ do_annotate: "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, - input_name); + map_symbol__annotate_browser(browser->selection, input_name); } else if (choice == zoom_dso) { - nr_hists = hists__filter_by_dso(hists, - (dso_filtered ? NULL : - browser->selection->map->dso), - &session_total); - dso_filtered = !dso_filtered; - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + if (dso_filter) { + newtPopHelpLine(); + dso_filter = NULL; + } else { + snprintf(msg, sizeof(msg), + "To zoom out press -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); + newtPushHelpLine(msg); + dso_filter = dso; + } + nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; } else if (choice == zoom_thread) { - nr_hists = hists__filter_by_thread(hists, - (thread_filtered ? NULL : thread), - &session_total); - thread_filtered = !thread_filtered; - if (hist_browser__populate(browser, hists, nr_hists, session_total) < 0) + if (thread_filter) { + newtPopHelpLine(); + thread_filter = NULL; + } else { + snprintf(msg, sizeof(msg), + "To zoom out press -> + \"Zoom out of %s(%d) thread\"", + (thread->comm_set ? thread->comm : ""), + thread->pid); + newtPushHelpLine(msg); + thread_filter = thread; + } + nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hist_browser__title(msg, sizeof(msg), input_name, + dso_filter, thread_filter); + if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) goto out; } } -- cgit v0.10.2 From c0ed55d2e4f600335193612725c0d7c8211a7a4a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 12:04:23 -0300 Subject: perf TUI: Move "Yes" button to before "No" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Esc + Enter should be enough warning to avoid accidentaly exiting from the browser. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c0e71aa..7a123a9 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -132,7 +132,7 @@ static bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ char yes[] = "Yes", no[] = "No"; - return newtWinChoice(NULL, no, yes, (char *)msg) == 2; + return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } /* -- cgit v0.10.2 From eed05fe70f96b04ebeb218b07ae8898e605f9b23 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 12:53:45 -0300 Subject: perf tools: Reorganize some structs to save space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using 'pahole --packable' I found some structs that could be reorganized to eliminate alignment holes, in some cases getting them to be cacheline multiples. [acme@doppio linux-2.6-tip]$ codiff perf.old ~/bin/perf builtin-annotate.c: struct perf_session | -8 struct perf_header | -8 2 structs changed builtin-diff.c: struct sample_data | -8 1 struct changed diff__process_sample_event | -8 1 function changed, 8 bytes removed, diff: -8 builtin-sched.c: struct sched_atom | -8 1 struct changed builtin-timechart.c: struct per_pid | -8 1 struct changed cmd_timechart | -16 1 function changed, 16 bytes removed, diff: -16 builtin-probe.c: struct perf_probe_point | -8 struct perf_probe_event | -8 2 structs changed opt_add_probe_event | -3 1 function changed, 3 bytes removed, diff: -3 util/probe-finder.c: struct probe_finder | -8 1 struct changed find_kprobe_trace_events | -16 1 function changed, 16 bytes removed, diff: -16 /home/acme/bin/perf: 4 functions changed, 43 bytes removed, diff: -43 [acme@doppio linux-2.6-tip]$ Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 4f5a03e..5e59c0c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -68,10 +68,10 @@ enum sched_event_type { struct sched_atom { enum sched_event_type type; + int specific_wait; u64 timestamp; u64 duration; unsigned long nr; - int specific_wait; sem_t *wait_sem; struct task_desc *wakee; }; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 266e7aa..369c1b4 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -77,8 +77,6 @@ struct per_pid { struct per_pidcomm *all; struct per_pidcomm *current; - - int painted; }; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a33b949..7f7cf85 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -68,11 +68,11 @@ struct sample_data { u64 addr; u64 id; u64 stream_id; - u32 cpu; u64 period; - struct ip_callchain *callchain; + u32 cpu; u32 raw_size; void *raw_data; + struct ip_callchain *callchain; }; #define BUILD_ID_SIZE 20 diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 82a6af7..c059f08 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -47,13 +47,13 @@ int perf_file_header__read(struct perf_file_header *self, struct perf_header { int frozen; int attrs, size; + bool needs_swap; struct perf_header_attr **attr; s64 attr_offset; u64 data_offset; u64 data_size; u64 event_offset; u64 event_size; - bool needs_swap; DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index cd308b0..9d99fc2 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -40,9 +40,9 @@ struct perf_probe_point { char *file; /* File path */ char *function; /* Function name */ int line; /* Line number */ + bool retprobe; /* Return probe flag */ char *lazy_line; /* Lazy matching pattern */ unsigned long offset; /* Offset from function entry */ - bool retprobe; /* Return probe flag */ }; /* Perf probe probing argument field chain */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 3564f22..2a27132 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -31,13 +31,13 @@ extern int find_line_range(int fd, struct line_range *lr); struct probe_finder { struct perf_probe_event *pev; /* Target probe event */ - int ntevs; /* number of trace events */ struct kprobe_trace_event *tevs; /* Result trace events */ + int ntevs; /* number of trace events */ /* For function searching */ + int lno; /* Line number */ Dwarf_Addr addr; /* Address */ const char *fname; /* Real file name */ - int lno; /* Line number */ Dwarf_Die cu_die; /* Current CU */ struct list_head lcache; /* Line cache for lazy match */ -- cgit v0.10.2 From f0e9c4fcefa42b5d28b915768dab81a7fdbbc00c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 31 Mar 2010 11:30:56 -0700 Subject: perf bench: fix spello Fix spello in user message. Cc: Peter Zijlstra , Cc: Paul Mackerra s LKML-Reference: <20100331113056.2c7df509.randy.dunlap@oracle.com> Signed-off-by: Randy Dunlap diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 4f77c7c..d9ab3ce 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -93,7 +93,7 @@ int bench_sched_pipe(int argc, const char **argv, switch (bench_format) { case BENCH_FORMAT_DEFAULT: - printf("# Extecuted %d pipe operations between two tasks\n\n", + printf("# Executed %d pipe operations between two tasks\n\n", loops); result_usec = diff.tv_sec * 1000000; -- cgit v0.10.2 From 854c5548dfad017920a36201d40449fdbad90bfb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 31 Mar 2010 11:31:00 -0700 Subject: perf: cleanup some Documentation Correct typos in perf bench & perf sched help text. Cc: Peter Zijlstra , Cc: Paul Mackerras LKML-Reference: <20100331113100.cc898487.randy.dunlap@oracle.com> Signed-off-by: Randy Dunlap diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index ae525ac..0181ddd 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -19,12 +19,12 @@ COMMON OPTIONS -f:: --format=:: Specify format style. -Current available format styles are, +Current available format styles are: 'default':: Default style. This is mainly for human reading. --------------------- -% perf bench sched pipe # with no style specify +% perf bench sched pipe # with no style specified (executing 1000000 pipe operations between two tasks) Total time:5.855 sec 5.855061 usecs/op @@ -79,7 +79,7 @@ options (20 sender and receiver processes per group) Total time:0.308 sec -% perf bench sched messaging -t -g 20 # be multi-thread,with 20 groups +% perf bench sched messaging -t -g 20 # be multi-thread, with 20 groups (20 sender and receiver threads per group) (20 groups == 800 threads run) diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 1ce7919..8417644 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -12,7 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -There's four variants of perf sched: +There are four variants of perf sched: 'perf sched record ' to record the scheduling events of an arbitrary workload. @@ -27,7 +27,7 @@ There's four variants of perf sched: via perf sched record. (this is done by starting up mockup threads that mimic the workload based on the events in the trace. These threads can then replay the timings (CPU runtime and sleep patterns) - of the workload as it occured when it was recorded - and can repeat + of the workload as it occurred when it was recorded - and can repeat it a number of times, measuring its performance.) OPTIONS -- cgit v0.10.2 From e9e94e3bd862d31777335722e747e97d9821bc1d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 5 Apr 2010 18:01:10 -0300 Subject: perf trace: Ignore "overwrite" field if present in /events/header_page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is not used in perf where we have the LOST events. Without this patch we get: [root@doppio ~]# perf lock report | head -3 Warning: Error: expected 'data' but read 'overwrite' So, to make the same perf command work with kernels with and without this field, introduce variants for the parsing routines to not warn the user in such case. Discussed-with: Steven Rostedt Cc: Frédéric Weisbecker Cc: Hitoshi Mitake Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 9b3c20f..3b81250 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -37,6 +37,8 @@ int header_page_ts_offset; int header_page_ts_size; int header_page_size_offset; int header_page_size_size; +int header_page_overwrite_offset; +int header_page_overwrite_size; int header_page_data_offset; int header_page_data_size; @@ -628,23 +630,32 @@ static int test_type(enum event_type type, enum event_type expect) return 0; } -static int test_type_token(enum event_type type, char *token, - enum event_type expect, const char *expect_tok) +static int __test_type_token(enum event_type type, char *token, + enum event_type expect, const char *expect_tok, + bool warn) { if (type != expect) { - warning("Error: expected type %d but read %d", - expect, type); + if (warn) + warning("Error: expected type %d but read %d", + expect, type); return -1; } if (strcmp(token, expect_tok) != 0) { - warning("Error: expected '%s' but read '%s'", - expect_tok, token); + if (warn) + warning("Error: expected '%s' but read '%s'", + expect_tok, token); return -1; } return 0; } +static int test_type_token(enum event_type type, char *token, + enum event_type expect, const char *expect_tok) +{ + return __test_type_token(type, token, expect, expect_tok, true); +} + static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) { enum event_type type; @@ -661,7 +672,8 @@ static int read_expect_type(enum event_type expect, char **tok) return __read_expect_type(expect, tok, 1); } -static int __read_expected(enum event_type expect, const char *str, int newline_ok) +static int __read_expected(enum event_type expect, const char *str, + int newline_ok, bool warn) { enum event_type type; char *token; @@ -672,21 +684,26 @@ static int __read_expected(enum event_type expect, const char *str, int newline_ else type = read_token_item(&token); - ret = test_type_token(type, token, expect, str); + ret = __test_type_token(type, token, expect, str, warn); free_token(token); return ret; } +static int read_expected_warn(enum event_type expect, const char *str, bool warn) +{ + return __read_expected(expect, str, 1, warn); +} + static int read_expected(enum event_type expect, const char *str) { - return __read_expected(expect, str, 1); + return __read_expected(expect, str, 1, true); } static int read_expected_item(enum event_type expect, const char *str) { - return __read_expected(expect, str, 0); + return __read_expected(expect, str, 0, true); } static char *event_read_name(void) @@ -3088,7 +3105,7 @@ static void print_args(struct print_arg *args) } static void parse_header_field(const char *field, - int *offset, int *size) + int *offset, int *size, bool warn) { char *token; int type; @@ -3103,7 +3120,7 @@ static void parse_header_field(const char *field, goto fail; free_token(token); - if (read_expected(EVENT_ITEM, field) < 0) + if (read_expected_warn(EVENT_ITEM, field, warn) < 0) return; if (read_expected(EVENT_OP, ";") < 0) return; @@ -3160,11 +3177,13 @@ int parse_header_page(char *buf, unsigned long size) init_input_buf(buf, size); parse_header_field("timestamp", &header_page_ts_offset, - &header_page_ts_size); + &header_page_ts_size, true); parse_header_field("commit", &header_page_size_offset, - &header_page_size_size); + &header_page_size_size, true); + parse_header_field("overwrite", &header_page_overwrite_offset, + &header_page_overwrite_size, false); parse_header_field("data", &header_page_data_offset, - &header_page_data_size); + &header_page_data_size, true); return 0; } -- cgit v0.10.2 From 53e5b5c215ce8372250e227f2c9acf9892de8434 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Apr 2010 13:33:54 -0300 Subject: perf tools: Fix perl support installation when O= is used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to create the $O/scripts/perl/Perf-Trace-Util/ directory too. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index f578b05..57b3569 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -158,6 +158,7 @@ all:: # Define NO_DWARF if you do not want debug-info analysis feature at all. $(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) $(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null) $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) -- cgit v0.10.2 From c05556421742eb47f80301767653a4bcb19de9de Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Tue, 13 Apr 2010 18:37:33 +1000 Subject: perf: Fix endianness argument compatibility with OPT_BOOLEAN() and introduce OPT_INCR() Parsing an option from the command line with OPT_BOOLEAN on a bool data type would not work on a big-endian machine due to the manner in which the boolean was being cast into an int and incremented. For example, running 'perf probe --list' on a PowerPC machine would fail to properly set the list_events bool and would therefore print out the usage information and terminate. This patch makes OPT_BOOLEAN work as expected with a bool datatype. For cases where the original OPT_BOOLEAN was intentionally being used to increment an int each time it was passed in on the command line, this patch introduces OPT_INCR with the old behaviour of OPT_BOOLEAN (the verbose variable is currently the only such example of this). I have reviewed every use of OPT_BOOLEAN to verify that a true C99 bool was passed. Where integers were used, I verified that they were only being used for boolean logic and changed them to bools to ensure that they would not be mistakenly used as ints. The major exception was the verbose variable which now uses OPT_INCR instead of OPT_BOOLEAN. Signed-off-by: Ian Munsie Acked-by: David S. Miller Cc: # NOTE: wont apply to .3[34].x cleanly, please backport Cc: Git development list Cc: Ian Munsie Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: KOSAKI Motohiro Cc: Hitoshi Mitake Cc: Rusty Russell Cc: Frederic Weisbecker Cc: Eric B Munson Cc: Valdis.Kletnieks@vt.edu Cc: WANG Cong Cc: Thiago Farina Cc: Masami Hiramatsu Cc: Xiao Guangrong Cc: Jaswinder Singh Rajput Cc: Arjan van de Ven Cc: OGAWA Hirofumi Cc: Mike Galbraith Cc: Tom Zanussi Cc: Anton Blanchard Cc: John Kacur Cc: Li Zefan Cc: Steven Rostedt LKML-Reference: <1271147857-11604-1-git-send-email-imunsie@au.ibm.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 52e646e..38dae74 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -23,7 +23,7 @@ static const char *length_str = "1MB"; static const char *routine = "default"; -static int use_clock = 0; +static bool use_clock = false; static int clock_fd; static const struct option options[] = { diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index 81cee78..da1b2e9 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -31,9 +31,9 @@ #define DATASIZE 100 -static int use_pipes = 0; +static bool use_pipes = false; static unsigned int loops = 100; -static unsigned int thread_mode = 0; +static bool thread_mode = false; static unsigned int num_groups = 10; struct sender_context { diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ee0d917..06eaebe 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -28,11 +28,11 @@ static char const *input_name = "perf.data"; -static int force; +static bool force; -static int full_paths; +static bool full_paths; -static int print_line; +static bool print_line; struct sym_hist { u64 sum; @@ -595,7 +595,7 @@ static const struct option options[] = { OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", "symbol to annotate"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 30a05f5..f8e3d18 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -27,7 +27,7 @@ static const struct option buildid_cache_options[] = { "file list", "file(s) to add"), OPT_STRING('r', "remove", &remove_name_list_str, "file list", "file(s) to remove"), - OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose"), + OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_END() }; diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index d0675c0..af2ad8b 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -16,7 +16,7 @@ #include "util/symbol.h" static char const *input_name = "perf.data"; -static int force; +static bool force; static bool with_hits; static const char * const buildid_list_usage[] = { @@ -29,7 +29,7 @@ static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_END() }; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1ea15d8..3a1d94d 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -19,7 +19,7 @@ static char const *input_old = "perf.data.old", *input_new = "perf.data"; static char diff__default_sort_order[] = "dso,symbol"; -static int force; +static bool force; static bool show_displacement; static int perf_session__add_hist_entry(struct perf_session *self, @@ -188,7 +188,7 @@ static const char * const diff_usage[] = { }; static const struct option options[] = { - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('m', "displacement", &show_displacement, "Show position displacement relative to baseline"), diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 215b584..81e3ecc 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -29,7 +29,7 @@ enum help_format { HELP_FORMAT_WEB, }; -static int show_all = 0; +static bool show_all = false; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index e12c844..6c38e4f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -744,7 +744,7 @@ static const char * const lock_usage[] = { static const struct option lock_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_END() }; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index b3ba25a..bfc47ff 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -133,7 +133,7 @@ static const char * const probe_usage[] = { }; static const struct option options[] = { - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), OPT_BOOLEAN('l', "list", ¶ms.list_events, "list up current probe events"), diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index dc61f1b..9a95136 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -38,22 +38,22 @@ static int output; static const char *output_name = "perf.data"; static int group = 0; static unsigned int realtime_prio = 0; -static int raw_samples = 0; -static int system_wide = 0; +static bool raw_samples = false; +static bool system_wide = false; static int profile_cpu = -1; static pid_t target_pid = -1; static pid_t target_tid = -1; static pid_t *all_tids = NULL; static int thread_num = 0; static pid_t child_pid = -1; -static int inherit = 1; -static int force = 0; -static int append_file = 0; -static int call_graph = 0; -static int inherit_stat = 0; -static int no_samples = 0; -static int sample_address = 0; -static int multiplex = 0; +static bool inherit = true; +static bool force = false; +static bool append_file = false; +static bool call_graph = false; +static bool inherit_stat = false; +static bool no_samples = false; +static bool sample_address = false; +static bool multiplex = false; static int multiplex_fd = -1; static long samples = 0; @@ -465,7 +465,7 @@ static int __cmd_record(int argc, const char **argv) rename(output_name, oldname); } } else { - append_file = 0; + append_file = false; } flags = O_CREAT|O_RDWR; @@ -701,7 +701,7 @@ static const struct option options[] = { "number of mmap data pages"), OPT_BOOLEAN('g', "call-graph", &call_graph, "do call-graph (stack chain/backtrace) recording"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_BOOLEAN('s', "stat", &inherit_stat, "per thread counts"), diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e93c69a..daee082 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -32,11 +32,11 @@ static char const *input_name = "perf.data"; -static int force; +static bool force; static bool hide_unresolved; static bool dont_use_callchains; -static int show_threads; +static bool show_threads; static struct perf_read_values show_threads_values; static char default_pretty_printing_style[] = "normal"; @@ -418,7 +418,7 @@ static const char * const report_usage[] = { static const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5e59c0c..09ddc8e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1790,7 +1790,7 @@ static const char * const sched_usage[] = { static const struct option sched_options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), @@ -1805,7 +1805,7 @@ static const char * const latency_usage[] = { static const struct option latency_options[] = { OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): runtime, switch, avg, max"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_INTEGER('C', "CPU", &profile_cpu, "CPU to profile on"), @@ -1822,7 +1822,7 @@ static const char * const replay_usage[] = { static const struct option replay_options[] = { OPT_INTEGER('r', "repeat", &replay_repeat, "repeat the workload replay N times (-1: infinite)"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1036ca7..e619ac8 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -67,19 +67,19 @@ static struct perf_event_attr default_attrs[] = { }; -static int system_wide = 0; +static bool system_wide = false; static unsigned int nr_cpus = 0; static int run_idx = 0; static int run_count = 1; -static int inherit = 1; -static int scale = 1; +static bool inherit = true; +static bool scale = true; static pid_t target_pid = -1; static pid_t target_tid = -1; static pid_t *all_tids = NULL; static int thread_num = 0; static pid_t child_pid = -1; -static int null_run = 0; +static bool null_run = false; static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; @@ -528,7 +528,7 @@ static const struct option options[] = { "system-wide collection from all CPUs"), OPT_BOOLEAN('c', "scale", &scale, "scale/normalize counters"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_INTEGER('r', "repeat", &run_count, "repeat command and print average + stddev (max: 100)"), diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 369c1b4..96f4a09 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -42,7 +42,7 @@ static u64 turbo_frequency; static u64 first_time, last_time; -static int power_only; +static bool power_only; struct per_pid; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4abdd9b..40f24dd 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -57,7 +57,7 @@ static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; -static int system_wide = 0; +static bool system_wide = false; static int default_interval = 0; @@ -68,18 +68,18 @@ static int target_pid = -1; static int target_tid = -1; static pid_t *all_tids = NULL; static int thread_num = 0; -static int inherit = 0; +static bool inherit = false; static int profile_cpu = -1; static int nr_cpus = 0; static unsigned int realtime_prio = 0; -static int group = 0; +static bool group = false; static unsigned int page_size; static unsigned int mmap_pages = 16; static int freq = 1000; /* 1 KHz */ static int delay_secs = 2; -static int zero = 0; -static int dump_symtab = 0; +static bool zero = false; +static bool dump_symtab = false; static bool hide_kernel_symbols = false; static bool hide_user_symbols = false; @@ -854,7 +854,7 @@ static void handle_keypress(int c) display_weighted = ~display_weighted; break; case 'z': - zero = ~zero; + zero = !zero; break; default: break; @@ -1335,7 +1335,7 @@ static const struct option options[] = { "display this many functions"), OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, "hide user symbols"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_END() }; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 407041d..8fc50d8 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -505,7 +505,7 @@ static const char * const trace_usage[] = { static const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('L', "Latency", &latency_format, "show latency attributes (irqs/preemption disabled, etc)"), diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 033d66d..dd824cf 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -13,7 +13,7 @@ #include "util.h" int verbose = 0; -int dump_trace = 0; +bool dump_trace = false; int eprintf(int level, const char *fmt, ...) { diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 5cb0a1b..047ac33 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -2,10 +2,11 @@ #ifndef __PERF_DEBUG_H #define __PERF_DEBUG_H +#include #include "event.h" extern int verbose; -extern int dump_trace; +extern bool dump_trace; int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(event_t *event); diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 79dfa0c..ed88764 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -49,6 +49,7 @@ static int get_value(struct parse_opt_ctx_t *p, break; /* FALLTHROUGH */ case OPTION_BOOLEAN: + case OPTION_INCR: case OPTION_BIT: case OPTION_SET_INT: case OPTION_SET_PTR: @@ -73,6 +74,10 @@ static int get_value(struct parse_opt_ctx_t *p, return 0; case OPTION_BOOLEAN: + *(bool *)opt->value = unset ? false : true; + return 0; + + case OPTION_INCR: *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; @@ -478,6 +483,7 @@ int usage_with_options_internal(const char * const *usagestr, case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: + case OPTION_INCR: case OPTION_SET_INT: case OPTION_SET_PTR: case OPTION_LONG: diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 948805a..b2da725 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -8,7 +8,8 @@ enum parse_opt_type { OPTION_GROUP, /* options with no arguments */ OPTION_BIT, - OPTION_BOOLEAN, /* _INCR would have been a better name */ + OPTION_BOOLEAN, + OPTION_INCR, OPTION_SET_INT, OPTION_SET_PTR, /* options with arguments (usually) */ @@ -95,6 +96,7 @@ struct option { #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 3b81250..17d6d66 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -42,7 +42,7 @@ int header_page_overwrite_size; int header_page_data_offset; int header_page_data_size; -int latency_format; +bool latency_format; static char *input_buf; static unsigned long long input_buf_ptr; diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index c3269b9..81f2fd2 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -1,6 +1,7 @@ #ifndef __PERF_TRACE_EVENTS_H #define __PERF_TRACE_EVENTS_H +#include #include "parse-events.h" #define __unused __attribute__((unused)) @@ -241,7 +242,7 @@ extern int header_page_size_size; extern int header_page_data_offset; extern int header_page_data_size; -extern int latency_format; +extern bool latency_format; int parse_header_page(char *buf, unsigned long size); int trace_parse_common_type(void *data); -- cgit v0.10.2 From 8dc58101f2c838355d44402aa77646649d10dbec Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:15 -0500 Subject: perf: Add pipe-specific header read/write and event processing code This patch makes several changes to allow the perf event stream to be sent and received over a pipe: - adds pipe-specific versions of the header read/write code - adds pipe-specific version of the event processing code - adds a range of event types to be used for header or other pseudo events, above the range used by the kernel - checks the return value of event handlers, which they can use to skip over large events during event processing rather than actually reading them into event objects. - unifies the multiple do_read() functions and updates its users. Note that none of these changes affect the existing perf data file format or processing - this code only comes into play if perf output is sent to stdout (or is read from stdin). Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-2-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9a95136..d060fc5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -487,7 +487,7 @@ static int __cmd_record(int argc, const char **argv) } if (!file_new) { - err = perf_header__read(&session->header, output); + err = perf_header__read(session, output); if (err < 0) return err; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 7f7cf85..5c1eba6 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -83,6 +83,10 @@ struct build_id_event { char filename[]; }; +enum perf_header_event_type { /* above any possible kernel type */ + PERF_RECORD_HEADER_MAX = 64, +}; + typedef union event_union { struct perf_event_header header; struct ip_event ip; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6c9aa16..8d05337 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -427,6 +427,25 @@ out_free: return err; } +int perf_header__write_pipe(int fd) +{ + struct perf_pipe_file_header f_header; + int err; + + f_header = (struct perf_pipe_file_header){ + .magic = PERF_MAGIC, + .size = sizeof(f_header), + }; + + err = do_write(fd, &f_header, sizeof(f_header)); + if (err < 0) { + pr_debug("failed to write perf pipe header\n"); + return err; + } + + return 0; +} + int perf_header__write(struct perf_header *self, int fd, bool at_exit) { struct perf_file_header f_header; @@ -518,25 +537,10 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) return 0; } -static int do_read(int fd, void *buf, size_t size) -{ - while (size) { - int ret = read(fd, buf, size); - - if (ret <= 0) - return -1; - - size -= ret; - buf += ret; - } - - return 0; -} - static int perf_header__getbuffer64(struct perf_header *self, int fd, void *buf, size_t size) { - if (do_read(fd, buf, size)) + if (do_read(fd, buf, size) <= 0) return -1; if (self->needs_swap) @@ -592,7 +596,7 @@ int perf_file_header__read(struct perf_file_header *self, { lseek(fd, 0, SEEK_SET); - if (do_read(fd, self, sizeof(*self)) || + if (do_read(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; @@ -662,13 +666,51 @@ static int perf_file_section__process(struct perf_file_section *self, return 0; } -int perf_header__read(struct perf_header *self, int fd) +static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, + struct perf_header *ph, int fd) { + if (do_read(fd, self, sizeof(*self)) <= 0 || + memcmp(&self->magic, __perf_magic, sizeof(self->magic))) + return -1; + + if (self->size != sizeof(*self)) { + u64 size = bswap_64(self->size); + + if (size != sizeof(*self)) + return -1; + + ph->needs_swap = true; + } + + return 0; +} + +static int perf_header__read_pipe(struct perf_session *session, int fd) +{ + struct perf_header *self = &session->header; + struct perf_pipe_file_header f_header; + + if (perf_file_header__read_pipe(&f_header, self, fd) < 0) { + pr_debug("incompatible file format\n"); + return -EINVAL; + } + + session->fd = fd; + + return 0; +} + +int perf_header__read(struct perf_session *session, int fd) +{ + struct perf_header *self = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; int nr_attrs, nr_ids, i, j; + if (session->fd_pipe) + return perf_header__read_pipe(session, fd); + if (perf_file_header__read(&f_header, self, fd) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index c059f08..6562ece 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -39,6 +39,11 @@ struct perf_file_header { DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; +struct perf_pipe_file_header { + u64 magic; + u64 size; +}; + struct perf_header; int perf_file_header__read(struct perf_file_header *self, @@ -60,8 +65,9 @@ struct perf_header { int perf_header__init(struct perf_header *self); void perf_header__exit(struct perf_header *self); -int perf_header__read(struct perf_header *self, int fd); +int perf_header__read(struct perf_session *session, int fd); int perf_header__write(struct perf_header *self, int fd, bool at_exit); +int perf_header__write_pipe(int fd); int perf_header__add_attr(struct perf_header *self, struct perf_header_attr *attr); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ddf288f..2c1277c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force) { struct stat input_stat; + if (!strcmp(self->filename, "-")) { + self->fd_pipe = true; + self->fd = STDIN_FILENO; + + if (perf_header__read(self, self->fd) < 0) + pr_err("incompatible file format"); + + return 0; + } + self->fd = open(self->filename, O_RDONLY); if (self->fd < 0) { pr_err("failed to open file: %s", self->filename); @@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force) goto out_close; } - if (perf_header__read(&self->header, self->fd) < 0) { + if (perf_header__read(self, self->fd) < 0) { pr_err("incompatible file format"); goto out_close; } @@ -52,6 +62,11 @@ out_close: return -1; } +void perf_session__update_sample_type(struct perf_session *self) +{ + self->sample_type = perf_header__sample_type(&self->header); +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -85,7 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc goto out_delete; } - self->sample_type = perf_header__sample_type(&self->header); + perf_session__update_sample_type(self); out: return self; out_free: @@ -200,14 +215,17 @@ static const char *event__name[] = { [PERF_RECORD_SAMPLE] = "SAMPLE", }; -unsigned long event__total[PERF_RECORD_MAX]; +unsigned long event__total[PERF_RECORD_HEADER_MAX]; void event__print_totals(void) { int i; - for (i = 0; i < PERF_RECORD_MAX; ++i) + for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { + if (!event__name[i]) + continue; pr_info("%10s events: %10ld\n", event__name[i], event__total[i]); + } } void mem_bswap_64(void *src, int byte_size) @@ -271,7 +289,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_LOST] = event__all64_swap, [PERF_RECORD_READ] = event__read_swap, [PERF_RECORD_SAMPLE] = event__all64_swap, - [PERF_RECORD_MAX] = NULL, + [PERF_RECORD_HEADER_MAX] = NULL, }; static int perf_session__process_event(struct perf_session *self, @@ -281,7 +299,7 @@ static int perf_session__process_event(struct perf_session *self, { trace_event(event); - if (event->header.type < PERF_RECORD_MAX) { + if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); @@ -376,6 +394,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se return thread; } +int do_read(int fd, void *buf, size_t size) +{ + void *buf_start = buf; + + while (size) { + int ret = read(fd, buf, size); + + if (ret <= 0) + return ret; + + size -= ret; + buf += ret; + } + + return buf - buf_start; +} + +#define session_done() (*(volatile int *)(&session_done)) +volatile int session_done; + +static int __perf_session__process_pipe_events(struct perf_session *self, + struct perf_event_ops *ops) +{ + event_t event; + uint32_t size; + int skip = 0; + u64 head; + int err; + void *p; + + perf_event_ops__fill_defaults(ops); + + head = 0; +more: + err = do_read(self->fd, &event, sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) + goto done; + + pr_err("failed to read event header\n"); + goto out_err; + } + + if (self->header.needs_swap) + perf_event_header__bswap(&event.header); + + size = event.header.size; + if (size == 0) + size = 8; + + p = &event; + p += sizeof(struct perf_event_header); + + err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) { + pr_err("unexpected end of event stream\n"); + goto done; + } + + pr_err("failed to read event data\n"); + goto out_err; + } + + if (size == 0 || + (skip = perf_session__process_event(self, &event, ops, + 0, head)) < 0) { + dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", + head, event.header.size, event.header.type); + /* + * assume we lost track of the stream, check alignment, and + * increment a single u64 in the hope to catch on again 'soon'. + */ + if (unlikely(head & 7)) + head &= ~7ULL; + + size = 8; + } + + head += size; + + dump_printf("\n%#Lx [%#x]: event: %d\n", + head, event.header.size, event.header.type); + + if (skip > 0) + head += skip; + + if (!session_done()) + goto more; +done: + err = 0; +out_err: + return err; +} + int __perf_session__process_events(struct perf_session *self, u64 data_offset, u64 data_size, u64 file_size, struct perf_event_ops *ops) @@ -499,9 +612,13 @@ out_getcwd_err: self->cwdlen = strlen(self->cwd); } - err = __perf_session__process_events(self, self->header.data_offset, - self->header.data_size, - self->size, ops); + if (!self->fd_pipe) + err = __perf_session__process_events(self, + self->header.data_offset, + self->header.data_size, + self->size, ops); + else + err = __perf_session__process_pipe_events(self, ops); out_err: return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 27f4c2d..5f78911 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -27,6 +27,7 @@ struct perf_session { u64 sample_type; struct ref_reloc_sym ref_reloc_sym; int fd; + bool fd_pipe; int cwdlen; char *cwd; char filename[0]; @@ -92,6 +93,9 @@ static inline struct map * return map_groups__new_module(&self->kmaps, start, filename); } +int do_read(int fd, void *buf, size_t size); +void perf_session__update_sample_type(struct perf_session *self); + #ifdef NO_NEWT_SUPPORT static inline int perf_session__browse_hists(struct rb_root *hists __used, u64 nr_hists __used, -- cgit v0.10.2 From 529870e37473a9fc609078f03cc5b4148cf06a87 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:16 -0500 Subject: perf record: Introduce special handling for pipe output Adds special treatment for stdout - if the user specifies '-o -' to perf record, the intent is that the event stream be written to stdout rather than to a disk file. Also, redirect stdout of forked child to stderr - in pipe mode, stdout of the forked child interferes with the stdout perf stream, so redirect it to stderr where it can still be seen but won't be mixed in with the perf output. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-3-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d060fc5..d4464f7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -35,6 +35,7 @@ static unsigned int page_size; static unsigned int mmap_pages = 128; static int freq = 1000; static int output; +static int pipe_output = 0; static const char *output_name = "perf.data"; static int group = 0; static unsigned int realtime_prio = 0; @@ -449,7 +450,9 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - if (!stat(output_name, &st) && st.st_size) { + if (!strcmp(output_name, "-")) + pipe_output = 1; + else if (!stat(output_name, &st) && st.st_size) { if (!force) { if (!append_file) { pr_err("Error, output file %s exists, use -A " @@ -474,7 +477,10 @@ static int __cmd_record(int argc, const char **argv) else flags |= O_TRUNC; - output = open(output_name, flags, S_IRUSR|S_IWUSR); + if (pipe_output) + output = STDOUT_FILENO; + else + output = open(output_name, flags, S_IRUSR | S_IWUSR); if (output < 0) { perror("failed to create output file"); exit(-1); @@ -513,6 +519,8 @@ static int __cmd_record(int argc, const char **argv) } if (!child_pid) { + if (pipe_output) + dup2(2, 1); close(child_ready_pipe[0]); close(go_pipe[1]); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); @@ -564,7 +572,11 @@ static int __cmd_record(int argc, const char **argv) open_counters(cpumap[i]); } - if (file_new) { + if (pipe_output) { + err = perf_header__write_pipe(output); + if (err < 0) + return err; + } else if (file_new) { err = perf_header__write(&session->header, output, false); if (err < 0) return err; -- cgit v0.10.2 From 46656ac7fb3252f8a3db29b18638e0e8067849ba Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:17 -0500 Subject: perf report: Introduce special handling for pipe input Adds special treatment for stdin - if the user specifies '-i -' to perf report, the intent is that the event stream be written to stdin rather than from a disk file. The actual handling of the '-' filename is done by the session; this just adds a signal handler to stop reporting, and turns off interference by the pager. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-4-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index daee082..00b358f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -269,6 +269,13 @@ static struct perf_event_ops event_ops = { .read = process_read_event, }; +extern volatile int session_done; + +static void sig_handler(int sig __attribute__((__unused__))) +{ + session_done = 1; +} + static int __cmd_report(void) { int ret = -EINVAL; @@ -276,6 +283,8 @@ static int __cmd_report(void) struct rb_node *next; const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + signal(SIGINT, sig_handler); + session = perf_session__new(input_name, O_RDONLY, force); if (session == NULL) return -ENOMEM; @@ -465,7 +474,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) { argc = parse_options(argc, argv, options, report_usage, 0); - setup_browser(); + if (strcmp(input_name, "-") != 0) + setup_browser(); if (symbol__init() < 0) return -1; -- cgit v0.10.2 From c239da3b4b55dbb8f30bcb8d1a0d63fc44a567c3 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:18 -0500 Subject: perf trace: Introduce special handling for pipe input Adds special treatment for stdin - if the user specifies '-i -' to perf trace, the intent is that the event stream be read from stdin rather than from a disk file. The actual handling of the '-' filename is done by the session; this just adds a signal handler to stop reporting, and turns off interference by the pager. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-5-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8fc50d8..c681e85 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -106,8 +106,17 @@ static struct perf_event_ops event_ops = { .comm = event__process_comm, }; +extern volatile int session_done; + +static void sig_handler(int sig __unused) +{ + session_done = 1; +} + static int __cmd_trace(struct perf_session *session) { + signal(SIGINT, sig_handler); + return perf_session__process_events(session, &event_ops); } @@ -580,7 +589,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) if (session == NULL) return -ENOMEM; - if (!perf_session__has_traces(session, "record -R")) + if (strcmp(input_name, "-") && + !perf_session__has_traces(session, "record -R")) return -EINVAL; if (generate_script_lang) { -- cgit v0.10.2 From 2c46dbb517a10b18d459e6ceffefde5bfb290cf6 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:19 -0500 Subject: perf: Convert perf header attrs into attr events Bypasses the attr perf header code and replaces it with a synthesized event and processing function that accomplishes the same thing, used when reading/writing perf data to/from a pipe. Making the attrs into events allows them to be streamed over a pipe along with the rest of the header data (in later patches). It also paves the way to allowing events to be added and removed from perf sessions dynamically. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-6-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d4464f7..289d9cf 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -584,6 +584,16 @@ static int __cmd_record(int argc, const char **argv) post_processing_offset = lseek(output, 0, SEEK_CUR); + if (pipe_output) { + err = event__synthesize_attrs(&session->header, + process_synthesized_event, + session); + if (err < 0) { + pr_err("Couldn't synthesize attrs.\n"); + return err; + } + } + err = event__synthesize_kernel_mmap(process_synthesized_event, session, "_text"); if (err < 0) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 00b358f..f0486ce 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -267,6 +267,7 @@ static struct perf_event_ops event_ops = { .fork = event__process_task, .lost = event__process_lost, .read = process_read_event, + .attr = event__process_attr, }; extern volatile int session_done; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c681e85..e30eac6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -104,6 +104,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, + .attr = event__process_attr, }; extern volatile int session_done; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 5c1eba6..b4fbf25 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -84,7 +84,14 @@ struct build_id_event { }; enum perf_header_event_type { /* above any possible kernel type */ - PERF_RECORD_HEADER_MAX = 64, + PERF_RECORD_HEADER_ATTR = 64, + PERF_RECORD_HEADER_MAX +}; + +struct attr_event { + struct perf_event_header header; + struct perf_event_attr attr; + u64 id[]; }; typedef union event_union { @@ -96,6 +103,7 @@ typedef union event_union { struct lost_event lost; struct read_event read; struct sample_event sample; + struct attr_event attr; } event_t; struct events_stats { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 8d05337..e361739 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -807,3 +807,82 @@ perf_header__find_attr(u64 id, struct perf_header *header) return NULL; } + +int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, + event__handler_t process, + struct perf_session *session) +{ + event_t *ev; + size_t size; + int err; + + size = sizeof(struct perf_event_attr); + size = ALIGN(size, sizeof(u64)); + size += sizeof(struct perf_event_header); + size += ids * sizeof(u64); + + ev = malloc(size); + + ev->attr.attr = *attr; + memcpy(ev->attr.id, id, ids * sizeof(u64)); + + ev->attr.header.type = PERF_RECORD_HEADER_ATTR; + ev->attr.header.size = size; + + err = process(ev, session); + + free(ev); + + return err; +} + +int event__synthesize_attrs(struct perf_header *self, + event__handler_t process, + struct perf_session *session) +{ + struct perf_header_attr *attr; + int i, err = 0; + + for (i = 0; i < self->attrs; i++) { + attr = self->attr[i]; + + err = event__synthesize_attr(&attr->attr, attr->ids, attr->id, + process, session); + if (err) { + pr_debug("failed to create perf header attribute\n"); + return err; + } + } + + return err; +} + +int event__process_attr(event_t *self, struct perf_session *session) +{ + struct perf_header_attr *attr; + unsigned int i, ids, n_ids; + + attr = perf_header_attr__new(&self->attr.attr); + if (attr == NULL) + return -ENOMEM; + + ids = self->header.size; + ids -= (void *)&self->attr.id - (void *)self; + n_ids = ids / sizeof(u64); + + for (i = 0; i < n_ids; i++) { + if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) { + perf_header_attr__delete(attr); + return -ENOMEM; + } + } + + if (perf_header__add_attr(&session->header, attr) < 0) { + perf_header_attr__delete(attr); + return -ENOMEM; + } + + perf_session__update_sample_type(session); + + return 0; +} diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 6562ece..e916ac5 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -95,4 +95,12 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, const char *name, bool is_kallsyms); int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); +int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, + event__handler_t process, + struct perf_session *session); +int event__synthesize_attrs(struct perf_header *self, + event__handler_t process, + struct perf_session *session); +int event__process_attr(event_t *self, struct perf_session *session); + #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 2c1277c..bc81864 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -200,6 +200,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->throttle = process_event_stub; if (handler->unthrottle == NULL) handler->unthrottle = process_event_stub; + if (handler->attr == NULL) + handler->attr = process_event_stub; } static const char *event__name[] = { @@ -213,6 +215,7 @@ static const char *event__name[] = { [PERF_RECORD_FORK] = "FORK", [PERF_RECORD_READ] = "READ", [PERF_RECORD_SAMPLE] = "SAMPLE", + [PERF_RECORD_HEADER_ATTR] = "ATTR", }; unsigned long event__total[PERF_RECORD_HEADER_MAX]; @@ -279,6 +282,26 @@ static void event__read_swap(event_t *self) self->read.id = bswap_64(self->read.id); } +static void event__attr_swap(event_t *self) +{ + size_t size; + + self->attr.attr.type = bswap_32(self->attr.attr.type); + self->attr.attr.size = bswap_32(self->attr.attr.size); + self->attr.attr.config = bswap_64(self->attr.attr.config); + self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); + self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); + self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); + self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); + self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); + self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); + self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); + + size = self->header.size; + size -= (void *)&self->attr.id - (void *)self; + mem_bswap_64(self->attr.id, size); +} + typedef void (*event__swap_op)(event_t *self); static event__swap_op event__swap_ops[] = { @@ -289,6 +312,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_LOST] = event__all64_swap, [PERF_RECORD_READ] = event__read_swap, [PERF_RECORD_SAMPLE] = event__all64_swap, + [PERF_RECORD_HEADER_ATTR] = event__attr_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -329,6 +353,8 @@ static int perf_session__process_event(struct perf_session *self, return ops->throttle(event, self); case PERF_RECORD_UNTHROTTLE: return ops->unthrottle(event, self); + case PERF_RECORD_HEADER_ATTR: + return ops->attr(event, self); default: self->unknown_events++; return -1; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5f78911..45a1374 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -44,7 +44,8 @@ struct perf_event_ops { lost, read, throttle, - unthrottle; + unthrottle, + attr; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force); -- cgit v0.10.2 From cd19a035f3b63fee6dcbdb5371c4b22276f7dc8c Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:20 -0500 Subject: perf: Convert perf event types into event type events Bypasses the event type perf header code and replaces it with a synthesized event and processing function that accomplishes the same thing, used when reading/writing perf data to/from a pipe. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-7-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 289d9cf..c4c1322 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -592,6 +592,13 @@ static int __cmd_record(int argc, const char **argv) pr_err("Couldn't synthesize attrs.\n"); return err; } + + err = event__synthesize_event_types(process_synthesized_event, + session); + if (err < 0) { + pr_err("Couldn't synthesize event_types.\n"); + return err; + } } err = event__synthesize_kernel_mmap(process_synthesized_event, diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f0486ce..e59d012 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -268,6 +268,7 @@ static struct perf_event_ops event_ops = { .lost = event__process_lost, .read = process_read_event, .attr = event__process_attr, + .event_type = event__process_event_type, }; extern volatile int session_done; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e30eac6..eb884a7 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -105,6 +105,7 @@ static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, .attr = event__process_attr, + .event_type = event__process_event_type, }; extern volatile int session_done; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b4fbf25..c720fe0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -85,6 +85,7 @@ struct build_id_event { enum perf_header_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_ATTR = 64, + PERF_RECORD_HEADER_EVENT_TYPE = 65, PERF_RECORD_HEADER_MAX }; @@ -94,6 +95,18 @@ struct attr_event { u64 id[]; }; +#define MAX_EVENT_NAME 64 + +struct perf_trace_event_type { + u64 event_id; + char name[MAX_EVENT_NAME]; +}; + +struct event_type_event { + struct perf_event_header header; + struct perf_trace_event_type event_type; +}; + typedef union event_union { struct perf_event_header header; struct ip_event ip; @@ -104,6 +117,7 @@ typedef union event_union { struct read_event read; struct sample_event sample; struct attr_event attr; + struct event_type_event event_type; } event_t; struct events_stats { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e361739..4463799 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -99,13 +99,6 @@ int perf_header__add_attr(struct perf_header *self, return 0; } -#define MAX_EVENT_NAME 64 - -struct perf_trace_event_type { - u64 event_id; - char name[MAX_EVENT_NAME]; -}; - static int event_count; static struct perf_trace_event_type *events; @@ -886,3 +879,58 @@ int event__process_attr(event_t *self, struct perf_session *session) return 0; } + +int event__synthesize_event_type(u64 event_id, char *name, + event__handler_t process, + struct perf_session *session) +{ + event_t ev; + size_t size = 0; + int err = 0; + + memset(&ev, 0, sizeof(ev)); + + ev.event_type.event_type.event_id = event_id; + memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME); + strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); + + ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; + size = strlen(name); + size = ALIGN(size, sizeof(u64)); + ev.event_type.header.size = sizeof(ev.event_type) - + (sizeof(ev.event_type.event_type.name) - size); + + err = process(&ev, session); + + return err; +} + +int event__synthesize_event_types(event__handler_t process, + struct perf_session *session) +{ + struct perf_trace_event_type *type; + int i, err = 0; + + for (i = 0; i < event_count; i++) { + type = &events[i]; + + err = event__synthesize_event_type(type->event_id, type->name, + process, session); + if (err) { + pr_debug("failed to create perf header event type\n"); + return err; + } + } + + return err; +} + +int event__process_event_type(event_t *self, + struct perf_session *session __unused) +{ + if (perf_header__push_event(self->event_type.event_type.event_id, + self->event_type.event_type.name) < 0) + return -ENOMEM; + + return 0; +} diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index e916ac5..afeb618 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -103,4 +103,13 @@ int event__synthesize_attrs(struct perf_header *self, struct perf_session *session); int event__process_attr(event_t *self, struct perf_session *session); +int event__synthesize_event_type(u64 event_id, char *name, + event__handler_t process, + struct perf_session *session); +int event__synthesize_event_types(event__handler_t process, + struct perf_session *session); +int event__process_event_type(event_t *self, + struct perf_session *session); + + #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index bc81864..96c4629 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -202,6 +202,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->unthrottle = process_event_stub; if (handler->attr == NULL) handler->attr = process_event_stub; + if (handler->event_type == NULL) + handler->event_type = process_event_stub; } static const char *event__name[] = { @@ -216,6 +218,7 @@ static const char *event__name[] = { [PERF_RECORD_READ] = "READ", [PERF_RECORD_SAMPLE] = "SAMPLE", [PERF_RECORD_HEADER_ATTR] = "ATTR", + [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", }; unsigned long event__total[PERF_RECORD_HEADER_MAX]; @@ -302,6 +305,12 @@ static void event__attr_swap(event_t *self) mem_bswap_64(self->attr.id, size); } +static void event__event_type_swap(event_t *self) +{ + self->event_type.event_type.event_id = + bswap_64(self->event_type.event_type.event_id); +} + typedef void (*event__swap_op)(event_t *self); static event__swap_op event__swap_ops[] = { @@ -313,6 +322,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_READ] = event__read_swap, [PERF_RECORD_SAMPLE] = event__all64_swap, [PERF_RECORD_HEADER_ATTR] = event__attr_swap, + [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -355,6 +365,8 @@ static int perf_session__process_event(struct perf_session *self, return ops->unthrottle(event, self); case PERF_RECORD_HEADER_ATTR: return ops->attr(event, self); + case PERF_RECORD_HEADER_EVENT_TYPE: + return ops->event_type(event, self); default: self->unknown_events++; return -1; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 45a1374..0dac1f4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -45,7 +45,8 @@ struct perf_event_ops { read, throttle, unthrottle, - attr; + attr, + event_type; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force); -- cgit v0.10.2 From 9215545e99d8c0b27323df2de504f4294bf5e407 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:21 -0500 Subject: perf: Convert perf tracing data into a tracing_data event Bypasses the tracing_data perf header code and replaces it with a synthesized event and processing function that accomplishes the same thing, used when reading/writing perf data to/from a pipe. The tracing data is pretty large, and this patch doesn't attempt to break it down into component events. The tracing_data event itself doesn't actually contain the tracing data, rather it arranges for the event processing code to skip over it after it's read, using the skip return value added to the event processing loop in a previous patch. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-8-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index c4c1322..3775abe 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -104,6 +104,11 @@ static void mmap_write_tail(struct mmap_data *md, unsigned long tail) pc->data_tail = tail; } +static void advance_output(size_t size) +{ + bytes_written += size; +} + static void write_output(void *buf, size_t size) { while (size) { @@ -599,6 +604,17 @@ static int __cmd_record(int argc, const char **argv) pr_err("Couldn't synthesize event_types.\n"); return err; } + + err = event__synthesize_tracing_data(output, attrs, + nr_counters, + process_synthesized_event, + session); + if (err <= 0) { + pr_err("Couldn't record tracing data.\n"); + return err; + } + + advance_output(err); } err = event__synthesize_kernel_mmap(process_synthesized_event, diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e59d012..76f03a7 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -269,6 +269,7 @@ static struct perf_event_ops event_ops = { .read = process_read_event, .attr = event__process_attr, .event_type = event__process_event_type, + .tracing_data = event__process_tracing_data, }; extern volatile int session_done; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index eb884a7..1509744 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -106,6 +106,7 @@ static struct perf_event_ops event_ops = { .comm = event__process_comm, .attr = event__process_attr, .event_type = event__process_event_type, + .tracing_data = event__process_tracing_data, }; extern volatile int session_done; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c720fe0..b896a17 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -86,6 +86,7 @@ struct build_id_event { enum perf_header_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_ATTR = 64, PERF_RECORD_HEADER_EVENT_TYPE = 65, + PERF_RECORD_HEADER_TRACING_DATA = 66, PERF_RECORD_HEADER_MAX }; @@ -107,6 +108,11 @@ struct event_type_event { struct perf_trace_event_type event_type; }; +struct tracing_data_event { + struct perf_event_header header; + u32 size; +}; + typedef union event_union { struct perf_event_header header; struct ip_event ip; @@ -118,6 +124,7 @@ typedef union event_union { struct sample_event sample; struct attr_event attr; struct event_type_event event_type; + struct tracing_data_event tracing_data; } event_t; struct events_stats { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4463799..c6874ec 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -934,3 +934,55 @@ int event__process_event_type(event_t *self, return 0; } + +int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, + int nb_events, + event__handler_t process, + struct perf_session *session __unused) +{ + event_t ev; + ssize_t size = 0, aligned_size = 0, padding; + int err = 0; + + memset(&ev, 0, sizeof(ev)); + + ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; + size = read_tracing_data_size(fd, pattrs, nb_events); + if (size <= 0) + return size; + aligned_size = ALIGN(size, sizeof(u64)); + padding = aligned_size - size; + ev.tracing_data.header.size = sizeof(ev.tracing_data); + ev.tracing_data.size = aligned_size; + + process(&ev, session); + + err = read_tracing_data(fd, pattrs, nb_events); + write_padded(fd, NULL, 0, padding); + + return aligned_size; +} + +int event__process_tracing_data(event_t *self, + struct perf_session *session) +{ + ssize_t size_read, padding, size = self->tracing_data.size; + off_t offset = lseek(session->fd, 0, SEEK_CUR); + char buf[BUFSIZ]; + + /* setup for reading amidst mmap */ + lseek(session->fd, offset + sizeof(struct tracing_data_event), + SEEK_SET); + + size_read = trace_report(session->fd); + + padding = ALIGN(size_read, sizeof(u64)) - size_read; + + if (read(session->fd, buf, padding) < 0) + die("reading input file"); + + if (size_read + padding != size) + die("tracing data size mismatch"); + + return size_read + padding; +} diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index afeb618..3ed3d98 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -111,5 +111,11 @@ int event__synthesize_event_types(event__handler_t process, int event__process_event_type(event_t *self, struct perf_session *session); +int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, + int nb_events, + event__handler_t process, + struct perf_session *session); +int event__process_tracing_data(event_t *self, + struct perf_session *session); #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 96c4629..1516c40 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -204,6 +204,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->attr = process_event_stub; if (handler->event_type == NULL) handler->event_type = process_event_stub; + if (handler->tracing_data == NULL) + handler->tracing_data = process_event_stub; } static const char *event__name[] = { @@ -219,6 +221,7 @@ static const char *event__name[] = { [PERF_RECORD_SAMPLE] = "SAMPLE", [PERF_RECORD_HEADER_ATTR] = "ATTR", [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", + [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", }; unsigned long event__total[PERF_RECORD_HEADER_MAX]; @@ -311,6 +314,11 @@ static void event__event_type_swap(event_t *self) bswap_64(self->event_type.event_type.event_id); } +static void event__tracing_data_swap(event_t *self) +{ + self->tracing_data.size = bswap_32(self->tracing_data.size); +} + typedef void (*event__swap_op)(event_t *self); static event__swap_op event__swap_ops[] = { @@ -323,6 +331,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_SAMPLE] = event__all64_swap, [PERF_RECORD_HEADER_ATTR] = event__attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, + [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -367,6 +376,10 @@ static int perf_session__process_event(struct perf_session *self, return ops->attr(event, self); case PERF_RECORD_HEADER_EVENT_TYPE: return ops->event_type(event, self); + case PERF_RECORD_HEADER_TRACING_DATA: + /* setup for reading amidst mmap */ + lseek(self->fd, offset + head, SEEK_SET); + return ops->tracing_data(event, self); default: self->unknown_events++; return -1; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0dac1f4..0739ebb 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -46,7 +46,8 @@ struct perf_event_ops { throttle, unthrottle, attr, - event_type; + event_type, + tracing_data; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 5ea8973..30cd9b5 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -154,10 +154,17 @@ static void put_tracing_file(char *file) free(file); } +static ssize_t calc_data_size; + static ssize_t write_or_die(const void *buf, size_t len) { int ret; + if (calc_data_size) { + calc_data_size += len; + return len; + } + ret = write(output_fd, buf, len); if (ret < 0) die("writing to '%s'", output_file); @@ -526,3 +533,20 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) return 0; } + +ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, + int nb_events) +{ + ssize_t size; + int err = 0; + + calc_data_size = 1; + err = read_tracing_data(fd, pattrs, nb_events); + size = calc_data_size - 1; + calc_data_size = 0; + + if (err < 0) + return err; + + return size; +} diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 7cd1193..44889c9 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -50,14 +50,37 @@ static int long_size; static unsigned long page_size; +static ssize_t calc_data_size; + +static int do_read(int fd, void *buf, int size) +{ + int rsize = size; + + while (size) { + int ret = read(fd, buf, size); + + if (ret <= 0) + return -1; + + size -= ret; + buf += ret; + } + + return rsize; +} + static int read_or_die(void *data, int size) { int r; - r = read(input_fd, data, size); - if (r != size) + r = do_read(input_fd, data, size); + if (r <= 0) die("reading input file (size expected=%d received=%d)", size, r); + + if (calc_data_size) + calc_data_size += r; + return r; } @@ -82,56 +105,28 @@ static char *read_string(void) char buf[BUFSIZ]; char *str = NULL; int size = 0; - int i; off_t r; + char c; for (;;) { - r = read(input_fd, buf, BUFSIZ); + r = read(input_fd, &c, 1); if (r < 0) die("reading input file"); if (!r) die("no data"); - for (i = 0; i < r; i++) { - if (!buf[i]) - break; - } - if (i < r) - break; + buf[size++] = c; - if (str) { - size += BUFSIZ; - str = realloc(str, size); - if (!str) - die("malloc of size %d", size); - memcpy(str + (size - BUFSIZ), buf, BUFSIZ); - } else { - size = BUFSIZ; - str = malloc_or_die(size); - memcpy(str, buf, size); - } + if (!c) + break; } - /* trailing \0: */ - i++; - - /* move the file descriptor to the end of the string */ - r = lseek(input_fd, -(r - i), SEEK_CUR); - if (r == (off_t)-1) - die("lseek"); - - if (str) { - size += i; - str = realloc(str, size); - if (!str) - die("malloc of size %d", size); - memcpy(str + (size - i), buf, i); - } else { - size = i; - str = malloc_or_die(i); - memcpy(str, buf, i); - } + if (calc_data_size) + calc_data_size += size; + + str = malloc_or_die(size); + memcpy(str, buf, size); return str; } @@ -459,7 +454,7 @@ struct record *trace_read_data(int cpu) return data; } -void trace_report(int fd) +ssize_t trace_report(int fd) { char buf[BUFSIZ]; char test[] = { 23, 8, 68 }; @@ -467,6 +462,9 @@ void trace_report(int fd) int show_version = 0; int show_funcs = 0; int show_printk = 0; + ssize_t size; + + calc_data_size = 1; input_fd = fd; @@ -499,14 +497,17 @@ void trace_report(int fd) read_proc_kallsyms(); read_ftrace_printk(); + size = calc_data_size - 1; + calc_data_size = 0; + if (show_funcs) { print_funcs(); - return; + return size; } if (show_printk) { print_printk(); - return; + return size; } - return; + return size; } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 81f2fd2..1f45d46 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -163,7 +163,7 @@ struct record *trace_read_data(int cpu); void parse_set_info(int nr_cpus, int long_sz); -void trace_report(int fd); +ssize_t trace_report(int fd); void *malloc_or_die(unsigned int size); @@ -259,6 +259,8 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); unsigned long long eval_flag(const char *flag); int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); +ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, + int nb_events); /* taken from kernel/trace/trace.h */ enum trace_flag_type { -- cgit v0.10.2 From c7929e4727e8ff2d6fc8327188820e3b1c2f1dc3 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:22 -0500 Subject: perf: Convert perf header build_ids into build_id events Bypasses the build_id perf header code and replaces it with a synthesized event and processing function that accomplishes the same thing, used when reading/writing perf data to/from a pipe. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-9-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 3775abe..0bde31b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -426,10 +426,19 @@ static int process_buildids(void) static void atexit_header(void) { - session->header.data_size += bytes_written; + if (!pipe_output) { + session->header.data_size += bytes_written; - process_buildids(); - perf_header__write(&session->header, output, true); + process_buildids(); + perf_header__write(&session->header, output, true); + } else { + int err; + + err = event__synthesize_build_ids(process_synthesized_event, + session); + if (err < 0) + pr_err("Couldn't synthesize build ids.\n"); + } } static int __cmd_record(int argc, const char **argv) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 76f03a7..7da5fb3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -270,6 +270,7 @@ static struct perf_event_ops event_ops = { .attr = event__process_attr, .event_type = event__process_event_type, .tracing_data = event__process_tracing_data, + .build_id = event__process_build_id, }; extern volatile int session_done; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1509744..1ee1e30 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -107,6 +107,7 @@ static struct perf_event_ops event_ops = { .attr = event__process_attr, .event_type = event__process_event_type, .tracing_data = event__process_tracing_data, + .build_id = event__process_build_id, }; extern volatile int session_done; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b896a17..e5740ea 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -87,6 +87,7 @@ enum perf_header_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_ATTR = 64, PERF_RECORD_HEADER_EVENT_TYPE = 65, PERF_RECORD_HEADER_TRACING_DATA = 66, + PERF_RECORD_HEADER_BUILD_ID = 67, PERF_RECORD_HEADER_MAX }; @@ -125,6 +126,7 @@ typedef union event_union { struct attr_event attr; struct event_type_event event_type; struct tracing_data_event tracing_data; + struct build_id_event build_id; } event_t; struct events_stats { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c6874ec..628173b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -986,3 +986,93 @@ int event__process_tracing_data(event_t *self, return size_read + padding; } + +int event__synthesize_build_id(struct dso *pos, u16 misc, + event__handler_t process, + struct perf_session *session) +{ + event_t ev; + size_t len; + int err = 0; + + if (!pos->hit) + return err; + + memset(&ev, 0, sizeof(ev)); + + len = pos->long_name_len + 1; + len = ALIGN(len, NAME_ALIGN); + memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); + ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; + ev.build_id.header.misc = misc; + ev.build_id.header.size = sizeof(ev.build_id) + len; + memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); + + err = process(&ev, session); + + return err; +} + +static int __event_synthesize_build_ids(struct list_head *head, u16 misc, + event__handler_t process, + struct perf_session *session) +{ + struct dso *pos; + + dsos__for_each_with_build_id(pos, head) { + int err; + if (!pos->hit) + continue; + + err = event__synthesize_build_id(pos, misc, process, session); + if (err < 0) + return err; + } + + return 0; +} + +int event__synthesize_build_ids(event__handler_t process, + struct perf_session *session) +{ + int err; + + if (!dsos__read_build_ids(true)) + return 0; + + err = __event_synthesize_build_ids(&dsos__kernel, + PERF_RECORD_MISC_KERNEL, + process, session); + if (err == 0) + err = __event_synthesize_build_ids(&dsos__user, + PERF_RECORD_MISC_USER, + process, session); + + if (err < 0) { + pr_debug("failed to synthesize build ids\n"); + return err; + } + + dsos__cache_build_ids(); + + return 0; +} + +int event__process_build_id(event_t *self, + struct perf_session *session __unused) +{ + struct list_head *head = &dsos__user; + struct dso *dso; + + if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL) + head = &dsos__kernel; + + dso = __dsos__findnew(head, self->build_id.filename); + if (dso != NULL) { + dso__set_build_id(dso, &self->build_id.build_id); + if (head == &dsos__kernel && self->build_id.filename[0] == '[') + dso->kernel = 1; + } + + return 0; +} diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 3ed3d98..4214e23 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -118,4 +118,11 @@ int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, int event__process_tracing_data(event_t *self, struct perf_session *session); +int event__synthesize_build_id(struct dso *pos, u16 misc, + event__handler_t process, + struct perf_session *session); +int event__synthesize_build_ids(event__handler_t process, + struct perf_session *session); +int event__process_build_id(event_t *self, struct perf_session *session); + #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1516c40..0fdf3eb 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -206,6 +206,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->event_type = process_event_stub; if (handler->tracing_data == NULL) handler->tracing_data = process_event_stub; + if (handler->build_id == NULL) + handler->build_id = process_event_stub; } static const char *event__name[] = { @@ -222,6 +224,7 @@ static const char *event__name[] = { [PERF_RECORD_HEADER_ATTR] = "ATTR", [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", + [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", }; unsigned long event__total[PERF_RECORD_HEADER_MAX]; @@ -332,6 +335,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_HEADER_ATTR] = event__attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, + [PERF_RECORD_HEADER_BUILD_ID] = NULL, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -380,6 +384,8 @@ static int perf_session__process_event(struct perf_session *self, /* setup for reading amidst mmap */ lseek(self->fd, offset + head, SEEK_SET); return ops->tracing_data(event, self); + case PERF_RECORD_HEADER_BUILD_ID: + return ops->build_id(event, self); default: self->unknown_events++; return -1; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0739ebb..0ac14d4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -47,7 +47,8 @@ struct perf_event_ops { unthrottle, attr, event_type, - tracing_data; + tracing_data, + build_id; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force); -- cgit v0.10.2 From 47902f3611b392209e2a412bf7ec02dca95e666d Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:23 -0500 Subject: perf trace/scripting: Add rwtop and sctop scripts A couple of scripts, one in Python and the other in Perl, that demonstrate 'live mode' tracing. For each, the output of the perf event stream is fed continuously to the script, which continuously aggregates the data and reports the current results every 3 seconds, or at the optionally specified interval. After the current results are displayed, the aggregations are cleared and the cycle begins anew. To run the scripts, simply pipe the output of the 'perf trace record' step as input to the corresponding 'perf trace report' step, using '-' as the filename to -o and -i: $ perf trace record sctop -o - | perf trace report sctop -i - Also adds clear_term() utility functions to the Util.pm and Util.py utility modules, for use by any script to clear the screen. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-10-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm index f869c48..d94b40c 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm @@ -15,6 +15,7 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs +clear_term ); our $VERSION = '0.01'; @@ -55,6 +56,11 @@ sub nsecs_str { return $str; } +sub clear_term +{ + print "\x1b[H\x1b[2J"; +} + 1; __END__ =head1 NAME diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record new file mode 100644 index 0000000..63976bf --- /dev/null +++ b/tools/perf/scripts/perl/bin/rwtop-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report new file mode 100644 index 0000000..93e698c --- /dev/null +++ b/tools/perf/scripts/perl/bin/rwtop-report @@ -0,0 +1,23 @@ +#!/bin/bash +# description: system-wide r/w top +# args: [interval] +n_args=0 +for i in "$@" +do + if expr match "$i" "-" > /dev/null ; then + break + fi + n_args=$(( $n_args + 1 )) +done +if [ "$n_args" -gt 1 ] ; then + echo "usage: rwtop-report [interval]" + exit +fi +if [ "$n_args" -gt 0 ] ; then + interval=$1 + shift +fi +perf trace $@ -s ~/libexec/perf-core/scripts/perl/rwtop.pl $interval + + + diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl new file mode 100644 index 0000000..ec2ab49 --- /dev/null +++ b/tools/perf/scripts/perl/rwtop.pl @@ -0,0 +1,177 @@ +#!/usr/bin/perl -w +# (c) 2010, Tom Zanussi +# Licensed under the terms of the GNU GPL License version 2 + +# read/write top +# +# Periodically displays system-wide r/w call activity, broken down by +# pid. If an [interval] arg is specified, the display will be +# refreshed every [interval] seconds. The default interval is 3 +# seconds. + +use 5.010000; +use strict; +use warnings; + +use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; +use lib "./Perf-Trace-Util/lib"; +use Perf::Trace::Core; +use Perf::Trace::Util; + +my $default_interval = 3; +my $nlines = 20; +my $print_thread; + +my %reads; +my %writes; + +my $interval = shift; +if (!$interval) { + $interval = $default_interval; +} + +sub syscalls::sys_exit_read +{ + my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, + $common_pid, $common_comm, + $nr, $ret) = @_; + + if ($ret > 0) { + $reads{$common_pid}{bytes_read} += $ret; + } else { + if (!defined ($reads{$common_pid}{bytes_read})) { + $reads{$common_pid}{bytes_read} = 0; + } + $reads{$common_pid}{errors}{$ret}++; + } +} + +sub syscalls::sys_enter_read +{ + my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, + $common_pid, $common_comm, + $nr, $fd, $buf, $count) = @_; + + $reads{$common_pid}{bytes_requested} += $count; + $reads{$common_pid}{total_reads}++; + $reads{$common_pid}{comm} = $common_comm; +} + +sub syscalls::sys_exit_write +{ + my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, + $common_pid, $common_comm, + $nr, $ret) = @_; + + if ($ret <= 0) { + $writes{$common_pid}{errors}{$ret}++; + } +} + +sub syscalls::sys_enter_write +{ + my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, + $common_pid, $common_comm, + $nr, $fd, $buf, $count) = @_; + + $writes{$common_pid}{bytes_written} += $count; + $writes{$common_pid}{total_writes}++; + $writes{$common_pid}{comm} = $common_comm; +} + +sub trace_begin +{ + $SIG{ALRM} = \&print_totals; + alarm 1; +} + +sub trace_end +{ + print_unhandled(); + print_totals(); +} + +sub print_totals +{ + my $count; + + $count = 0; + + clear_term(); + + printf("\nread counts by pid:\n\n"); + + printf("%6s %20s %10s %10s %10s\n", "pid", "comm", + "# reads", "bytes_req", "bytes_read"); + printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------", + "----------", "----------", "----------"); + + foreach my $pid (sort {$reads{$b}{bytes_read} <=> + $reads{$a}{bytes_read}} keys %reads) { + my $comm = $reads{$pid}{comm}; + my $total_reads = $reads{$pid}{total_reads}; + my $bytes_requested = $reads{$pid}{bytes_requested}; + my $bytes_read = $reads{$pid}{bytes_read}; + + printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, + $total_reads, $bytes_requested, $bytes_read); + + if (++$count == $nlines) { + last; + } + } + + $count = 0; + + printf("\nwrite counts by pid:\n\n"); + + printf("%6s %20s %10s %13s\n", "pid", "comm", + "# writes", "bytes_written"); + printf("%6s %-20s %10s %13s\n", "------", "--------------------", + "----------", "-------------"); + + foreach my $pid (sort {$writes{$b}{bytes_written} <=> + $writes{$a}{bytes_written}} keys %writes) { + my $comm = $writes{$pid}{comm}; + my $total_writes = $writes{$pid}{total_writes}; + my $bytes_written = $writes{$pid}{bytes_written}; + + printf("%6s %-20s %10s %13s\n", $pid, $comm, + $total_writes, $bytes_written); + + if (++$count == $nlines) { + last; + } + } + + %reads = (); + %writes = (); + alarm $interval; +} + +my %unhandled; + +sub print_unhandled +{ + if ((scalar keys %unhandled) == 0) { + return; + } + + print "\nunhandled events:\n\n"; + + printf("%-40s %10s\n", "event", "count"); + printf("%-40s %10s\n", "----------------------------------------", + "-----------"); + + foreach my $event_name (keys %unhandled) { + printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); + } +} + +sub trace_unhandled +{ + my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, + $common_pid, $common_comm) = @_; + + $unhandled{$event_name}++; +} diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 83e9143..9689bc0 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -23,3 +23,6 @@ def nsecs_nsecs(nsecs): def nsecs_str(nsecs): str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), return str + +def clear_term(): + print("\x1b[H\x1b[2J") diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/scripts/python/bin/sctop-record new file mode 100644 index 0000000..27ccffa --- /dev/null +++ b/tools/perf/scripts/python/bin/sctop-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/scripts/python/bin/sctop-report new file mode 100644 index 0000000..b01c842 --- /dev/null +++ b/tools/perf/scripts/python/bin/sctop-report @@ -0,0 +1,24 @@ +#!/bin/bash +# description: syscall top +# args: [comm] [interval] +n_args=0 +for i in "$@" +do + if expr match "$i" "-" > /dev/null ; then + break + fi + n_args=$(( $n_args + 1 )) +done +if [ "$n_args" -gt 2 ] ; then + echo "usage: sctop-report [comm] [interval]" + exit +fi +if [ "$n_args" -gt 1 ] ; then + comm=$1 + interval=$2 + shift 2 +elif [ "$n_args" -gt 0 ] ; then + interval=$1 + shift +fi +perf trace $@ -s ~/libexec/perf-core/scripts/python/sctop.py $comm $interval diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py new file mode 100644 index 0000000..6cafad4 --- /dev/null +++ b/tools/perf/scripts/python/sctop.py @@ -0,0 +1,78 @@ +# system call top +# (c) 2010, Tom Zanussi +# Licensed under the terms of the GNU GPL License version 2 +# +# Periodically displays system-wide system call totals, broken down by +# syscall. If a [comm] arg is specified, only syscalls called by +# [comm] are displayed. If an [interval] arg is specified, the display +# will be refreshed every [interval] seconds. The default interval is +# 3 seconds. + +import thread +import time +import os +import sys + +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * +from Util import * + +usage = "perf trace -s syscall-counts.py [comm] [interval]\n"; + +for_comm = None +default_interval = 3 +interval = default_interval + +if len(sys.argv) > 3: + sys.exit(usage) + +if len(sys.argv) > 2: + for_comm = sys.argv[1] + interval = int(sys.argv[2]) +elif len(sys.argv) > 1: + try: + interval = int(sys.argv[1]) + except ValueError: + for_comm = sys.argv[1] + interval = default_interval + +syscalls = autodict() + +def trace_begin(): + thread.start_new_thread(print_syscall_totals, (interval,)) + pass + +def raw_syscalls__sys_enter(event_name, context, common_cpu, + common_secs, common_nsecs, common_pid, common_comm, + id, args): + if for_comm is not None: + if common_comm != for_comm: + return + try: + syscalls[id] += 1 + except TypeError: + syscalls[id] = 1 + +def print_syscall_totals(interval): + while 1: + clear_term() + if for_comm is not None: + print "\nsyscall events for %s:\n\n" % (for_comm), + else: + print "\nsyscall events:\n\n", + + print "%-40s %10s\n" % ("event", "count"), + print "%-40s %10s\n" % ("----------------------------------------", \ + "----------"), + + for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ + reverse = True): + try: + print "%-40d %10d\n" % (id, val), + except TypeError: + pass + syscalls.clear() + time.sleep(interval) -- cgit v0.10.2 From 00b21a01935892a2b97613f10300434998f45093 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:24 -0500 Subject: perf trace/scripting: Enable scripting shell scripts for live mode It should be possible to run any perf trace script in 'live mode'. This requires being able to pass in e.g. '-i -' or other args, which the current shell scripts aren't equipped to handle. In a few cases, there are required or optional args that also need special handling. This patch makes changes the current set of shell scripts as necessary. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-11-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record index f8885d3..6ad9b8f 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-record +++ b/tools/perf/scripts/perl/bin/failed-syscalls-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report index 8bfc660..f634608 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-report +++ b/tools/perf/scripts/perl/bin/failed-syscalls-report @@ -1,4 +1,10 @@ #!/bin/bash # description: system-wide failed syscalls # args: [comm] -perf trace -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $1 +if [ $# -gt 0 ] ; then + if ! expr match "$1" "-" ; then + comm=$1 + shift + fi +fi +perf trace $@ -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record index b25056e..a828679 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-record +++ b/tools/perf/scripts/perl/bin/rw-by-file-record @@ -1,2 +1,3 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write +perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ + diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report index eddb9cc..d83070b 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-report +++ b/tools/perf/scripts/perl/bin/rw-by-file-report @@ -1,7 +1,13 @@ #!/bin/bash # description: r/w activity for a program, by file # args: -perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1 +if [ $# -lt 1 ] ; then + echo "usage: rw-by-file " + exit +fi +comm=$1 +shift +perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $comm diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record index 8903979..63976bf 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-record +++ b/tools/perf/scripts/perl/bin/rw-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write +perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report index 7f44c25..7ef4698 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-report +++ b/tools/perf/scripts/perl/bin/rw-by-pid-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide r/w activity -perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl +perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record index 6abedda..9c0cf58 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-record +++ b/tools/perf/scripts/perl/bin/wakeup-latency-record @@ -1,5 +1,5 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup +perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup $@ diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report index fce3adc..a0d898f 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-report +++ b/tools/perf/scripts/perl/bin/wakeup-latency-report @@ -1,6 +1,6 @@ #!/bin/bash # description: system-wide min/max/avg wakeup latency -perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl +perf trace $@ -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record index fce6637..c2a1a94 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-record +++ b/tools/perf/scripts/perl/bin/workqueue-stats-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion +perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report index 71cfbd1..3508113 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ b/tools/perf/scripts/perl/bin/workqueue-stats-report @@ -1,6 +1,6 @@ #!/bin/bash # description: workqueue stats (ins/exe/create/destroy) -perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl +perf trace $@ -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record index f8885d3..6ad9b8f 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report index 1e0c0a8..8c128ef 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report @@ -1,4 +1,10 @@ #!/bin/bash # description: system-wide failed syscalls, by pid # args: [comm] -perf trace -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $1 +if [ $# -gt 0 ] ; then + if ! expr match "$1" "-" ; then + comm=$1 + shift + fi +fi +perf trace $@ -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $comm diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record index 45a8c50..27ccffa 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report index f8044d1..c53362e 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report @@ -1,4 +1,10 @@ #!/bin/bash # description: system-wide syscall counts, by pid # args: [comm] -perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $1 +if [ $# -gt 0 ] ; then + if ! expr match "$1" "-" ; then + comm=$1 + shift + fi +fi +perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $comm diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/perf/scripts/python/bin/syscall-counts-record index 45a8c50..27ccffa 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-record +++ b/tools/perf/scripts/python/bin/syscall-counts-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter +perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report index a366aa61..8c21552 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-report +++ b/tools/perf/scripts/python/bin/syscall-counts-report @@ -1,4 +1,10 @@ #!/bin/bash # description: system-wide syscall counts # args: [comm] -perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py $1 +if [ $# -gt 0 ] ; then + if ! expr match "$1" "-" ; then + comm=$1 + shift + fi +fi +perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts.py $comm -- cgit v0.10.2 From a0cccc2e8e9fb16cbed3a117b30e3fbac3092ee3 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Thu, 1 Apr 2010 23:59:25 -0500 Subject: perf trace: Invoke live mode automatically if record/report not specified Currently, live mode is invoked by explicitly invoking the record and report sides and connecting them with a pipe e.g. $ perf trace record rwtop -o - | perf trace report rwtop 5 -i - In terms of usability, it's not that bad, but it does require the user to type and remember more than necessary. This patch allows the user to accomplish the same thing without specifying the separate record/report steps or the pipe. So the same command as above can be accomplished more simply as: $ perf trace rwtop 5 Notice that the '-i -' and '-o -' aren't required in this case - they're added internally, and that any extra arguments are passed along to the report script (but not to the record script). The overall effect is that any of the scripts listed in 'perf trace -l' can now be used directly in live mode, with the expected arguments, by simply specifying the script and args to 'perf trace'. Signed-off-by: Tom Zanussi Acked-by: Thomas Gleixner Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: k-keiichi@bx.jp.nec.com Cc: acme@ghostprotocols.net LKML-Reference: <1270184365-8281-12-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1ee1e30..2eefb33 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -561,6 +561,65 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) suffix = REPORT_SUFFIX; } + if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) { + char *record_script_path, *report_script_path; + int live_pipe[2]; + pid_t pid; + + record_script_path = get_script_path(argv[1], RECORD_SUFFIX); + if (!record_script_path) { + fprintf(stderr, "record script not found\n"); + return -1; + } + + report_script_path = get_script_path(argv[1], REPORT_SUFFIX); + if (!report_script_path) { + fprintf(stderr, "report script not found\n"); + return -1; + } + + if (pipe(live_pipe) < 0) { + perror("failed to create pipe"); + exit(-1); + } + + pid = fork(); + if (pid < 0) { + perror("failed to fork"); + exit(-1); + } + + if (!pid) { + dup2(live_pipe[1], 1); + close(live_pipe[0]); + + __argv = malloc(5 * sizeof(const char *)); + __argv[0] = "/bin/sh"; + __argv[1] = record_script_path; + __argv[2] = "-o"; + __argv[3] = "-"; + __argv[4] = NULL; + + execvp("/bin/sh", (char **)__argv); + exit(-1); + } + + dup2(live_pipe[0], 0); + close(live_pipe[1]); + + __argv = malloc((argc + 3) * sizeof(const char *)); + __argv[0] = "/bin/sh"; + __argv[1] = report_script_path; + for (i = 2; i < argc; i++) + __argv[i] = argv[i]; + __argv[i++] = "-i"; + __argv[i++] = "-"; + __argv[i++] = NULL; + + execvp("/bin/sh", (char **)__argv); + exit(-1); + } + if (suffix) { script_path = get_script_path(argv[2], suffix); if (!script_path) { -- cgit v0.10.2 From 76e1d9047e4edefb8ada20aa90d5762306082bd6 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 5 Apr 2010 15:35:57 +0200 Subject: perf: Store active software events in a hashlist Each time a software event triggers, we need to walk through the entire list of events from the current cpu and task contexts to retrieve a running perf event that matches. We also need to check a matching perf event is actually counting. This walk is wasteful and makes the event fast path scaling down with a growing number of events running on the same contexts. To solve this, we store the running perf events in a hashlist to get an immediate access to them against their type:event_id when they trigger. v2: - Fix SWEVENT_HLIST_SIZE definition (and re-learn some basic maths along the way) - Only allocate hlist for online cpus, but keep track of the refcount on offline possible cpus too, so that we allocate it if needed when it becomes online. - Drop the kref use as it's not adapted to our tricks anymore. v3: - Fix bad refcount check (address instead of value). Thanks to Eric Dumazet who spotted this. - While exiting cpu, move the hlist release out of the IPI path to lock the hlist mutex sanely. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 6e96cc8..bf896d0 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -589,6 +589,14 @@ enum perf_group_flag { PERF_GROUP_SOFTWARE = 0x1, }; +#define SWEVENT_HLIST_BITS 8 +#define SWEVENT_HLIST_SIZE (1 << SWEVENT_HLIST_BITS) + +struct swevent_hlist { + struct hlist_head heads[SWEVENT_HLIST_SIZE]; + struct rcu_head rcu_head; +}; + /** * struct perf_event - performance event kernel representation: */ @@ -597,6 +605,7 @@ struct perf_event { struct list_head group_entry; struct list_head event_entry; struct list_head sibling_list; + struct hlist_node hlist_entry; int nr_siblings; int group_flags; struct perf_event *group_leader; @@ -744,6 +753,9 @@ struct perf_cpu_context { int active_oncpu; int max_pertask; int exclusive; + struct swevent_hlist *swevent_hlist; + struct mutex hlist_mutex; + int hlist_refcount; /* * Recursion avoidance: diff --git a/kernel/perf_event.c b/kernel/perf_event.c index fcf42dcd..9efdfe5 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -3966,36 +3967,6 @@ static void perf_swevent_add(struct perf_event *event, u64 nr, perf_swevent_overflow(event, 0, nmi, data, regs); } -static int perf_swevent_is_counting(struct perf_event *event) -{ - /* - * The event is active, we're good! - */ - if (event->state == PERF_EVENT_STATE_ACTIVE) - return 1; - - /* - * The event is off/error, not counting. - */ - if (event->state != PERF_EVENT_STATE_INACTIVE) - return 0; - - /* - * The event is inactive, if the context is active - * we're part of a group that didn't make it on the 'pmu', - * not counting. - */ - if (event->ctx->is_active) - return 0; - - /* - * We're inactive and the context is too, this means the - * task is scheduled out, we're counting events that happen - * to us, like migration events. - */ - return 1; -} - static int perf_tp_event_match(struct perf_event *event, struct perf_sample_data *data); @@ -4019,12 +3990,6 @@ static int perf_swevent_match(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { - if (event->cpu != -1 && event->cpu != smp_processor_id()) - return 0; - - if (!perf_swevent_is_counting(event)) - return 0; - if (event->attr.type != type) return 0; @@ -4041,18 +4006,53 @@ static int perf_swevent_match(struct perf_event *event, return 1; } -static void perf_swevent_ctx_event(struct perf_event_context *ctx, - enum perf_type_id type, - u32 event_id, u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) +static inline u64 swevent_hash(u64 type, u32 event_id) +{ + u64 val = event_id | (type << 32); + + return hash_64(val, SWEVENT_HLIST_BITS); +} + +static struct hlist_head * +find_swevent_head(struct perf_cpu_context *ctx, u64 type, u32 event_id) +{ + u64 hash; + struct swevent_hlist *hlist; + + hash = swevent_hash(type, event_id); + + hlist = rcu_dereference(ctx->swevent_hlist); + if (!hlist) + return NULL; + + return &hlist->heads[hash]; +} + +static void do_perf_sw_event(enum perf_type_id type, u32 event_id, + u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) { + struct perf_cpu_context *cpuctx; struct perf_event *event; + struct hlist_node *node; + struct hlist_head *head; - list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + cpuctx = &__get_cpu_var(perf_cpu_context); + + rcu_read_lock(); + + head = find_swevent_head(cpuctx, type, event_id); + + if (!head) + goto end; + + hlist_for_each_entry_rcu(event, node, head, hlist_entry) { if (perf_swevent_match(event, type, event_id, data, regs)) perf_swevent_add(event, nr, nmi, data, regs); } +end: + rcu_read_unlock(); } int perf_swevent_get_recursion_context(void) @@ -4090,27 +4090,6 @@ void perf_swevent_put_recursion_context(int rctx) } EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context); -static void do_perf_sw_event(enum perf_type_id type, u32 event_id, - u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct perf_cpu_context *cpuctx; - struct perf_event_context *ctx; - - cpuctx = &__get_cpu_var(perf_cpu_context); - rcu_read_lock(); - perf_swevent_ctx_event(&cpuctx->ctx, type, event_id, - nr, nmi, data, regs); - /* - * doesn't really matter which of the child contexts the - * events ends up in. - */ - ctx = rcu_dereference(current->perf_event_ctxp); - if (ctx) - perf_swevent_ctx_event(ctx, type, event_id, nr, nmi, data, regs); - rcu_read_unlock(); -} void __perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) @@ -4136,16 +4115,28 @@ static void perf_swevent_read(struct perf_event *event) static int perf_swevent_enable(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; + struct perf_cpu_context *cpuctx; + struct hlist_head *head; + + cpuctx = &__get_cpu_var(perf_cpu_context); if (hwc->sample_period) { hwc->last_period = hwc->sample_period; perf_swevent_set_period(event); } + + head = find_swevent_head(cpuctx, event->attr.type, event->attr.config); + if (WARN_ON_ONCE(!head)) + return -EINVAL; + + hlist_add_head_rcu(&event->hlist_entry, head); + return 0; } static void perf_swevent_disable(struct perf_event *event) { + hlist_del_rcu(&event->hlist_entry); } static const struct pmu perf_ops_generic = { @@ -4359,13 +4350,115 @@ static int perf_tp_event_match(struct perf_event *event, return 0; } +static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) +{ + struct swevent_hlist *hlist; + + hlist = container_of(rcu_head, struct swevent_hlist, rcu_head); + kfree(hlist); +} + +static void swevent_hlist_release(struct perf_cpu_context *cpuctx) +{ + struct swevent_hlist *hlist; + + if (!cpuctx->swevent_hlist) + return; + + hlist = cpuctx->swevent_hlist; + rcu_assign_pointer(cpuctx->swevent_hlist, NULL); + call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu); +} + +static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) +{ + struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); + + mutex_lock(&cpuctx->hlist_mutex); + + if (!--cpuctx->hlist_refcount) + swevent_hlist_release(cpuctx); + + mutex_unlock(&cpuctx->hlist_mutex); +} + +static void swevent_hlist_put(struct perf_event *event) +{ + int cpu; + + if (event->cpu != -1) { + swevent_hlist_put_cpu(event, event->cpu); + return; + } + + for_each_possible_cpu(cpu) + swevent_hlist_put_cpu(event, cpu); +} + +static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) +{ + struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); + int err = 0; + + mutex_lock(&cpuctx->hlist_mutex); + + if (!cpuctx->swevent_hlist && cpu_online(cpu)) { + struct swevent_hlist *hlist; + + hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); + if (!hlist) { + err = -ENOMEM; + goto exit; + } + rcu_assign_pointer(cpuctx->swevent_hlist, hlist); + } + cpuctx->hlist_refcount++; + exit: + mutex_unlock(&cpuctx->hlist_mutex); + + return err; +} + +static int swevent_hlist_get(struct perf_event *event) +{ + int err; + int cpu, failed_cpu; + + if (event->cpu != -1) + return swevent_hlist_get_cpu(event, event->cpu); + + get_online_cpus(); + for_each_possible_cpu(cpu) { + err = swevent_hlist_get_cpu(event, cpu); + if (err) { + failed_cpu = cpu; + goto fail; + } + } + put_online_cpus(); + + return 0; + fail: + for_each_possible_cpu(cpu) { + if (cpu == failed_cpu) + break; + swevent_hlist_put_cpu(event, cpu); + } + + put_online_cpus(); + return err; +} + static void tp_perf_event_destroy(struct perf_event *event) { perf_trace_disable(event->attr.config); + swevent_hlist_put(event); } static const struct pmu *tp_perf_event_init(struct perf_event *event) { + int err; + /* * Raw tracepoint data is a severe data leak, only allow root to * have these. @@ -4379,6 +4472,11 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event) return NULL; event->destroy = tp_perf_event_destroy; + err = swevent_hlist_get(event); + if (err) { + perf_trace_disable(event->attr.config); + return ERR_PTR(err); + } return &perf_ops_generic; } @@ -4479,6 +4577,7 @@ static void sw_perf_event_destroy(struct perf_event *event) WARN_ON(event->parent); atomic_dec(&perf_swevent_enabled[event_id]); + swevent_hlist_put(event); } static const struct pmu *sw_perf_event_init(struct perf_event *event) @@ -4517,6 +4616,12 @@ static const struct pmu *sw_perf_event_init(struct perf_event *event) case PERF_COUNT_SW_ALIGNMENT_FAULTS: case PERF_COUNT_SW_EMULATION_FAULTS: if (!event->parent) { + int err; + + err = swevent_hlist_get(event); + if (err) + return ERR_PTR(err); + atomic_inc(&perf_swevent_enabled[event_id]); event->destroy = sw_perf_event_destroy; } @@ -5389,6 +5494,7 @@ static void __init perf_event_init_all_cpus(void) for_each_possible_cpu(cpu) { cpuctx = &per_cpu(perf_cpu_context, cpu); + mutex_init(&cpuctx->hlist_mutex); __perf_event_init_context(&cpuctx->ctx, NULL); } } @@ -5402,6 +5508,16 @@ static void __cpuinit perf_event_init_cpu(int cpu) spin_lock(&perf_resource_lock); cpuctx->max_pertask = perf_max_events - perf_reserved_percpu; spin_unlock(&perf_resource_lock); + + mutex_lock(&cpuctx->hlist_mutex); + if (cpuctx->hlist_refcount > 0) { + struct swevent_hlist *hlist; + + hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); + WARN_ON_ONCE(!hlist); + rcu_assign_pointer(cpuctx->swevent_hlist, hlist); + } + mutex_unlock(&cpuctx->hlist_mutex); } #ifdef CONFIG_HOTPLUG_CPU @@ -5421,6 +5537,10 @@ static void perf_event_exit_cpu(int cpu) struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); struct perf_event_context *ctx = &cpuctx->ctx; + mutex_lock(&cpuctx->hlist_mutex); + swevent_hlist_release(cpuctx); + mutex_unlock(&cpuctx->hlist_mutex); + mutex_lock(&ctx->mutex); smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); mutex_unlock(&ctx->mutex); -- cgit v0.10.2 From df8290bf7ea6b3051e2f315579a6e829309ec1ed Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 9 Apr 2010 00:28:14 +0200 Subject: perf: Make clock software events consistent with general exclusion rules The cpu/task clock events implement their own version of exclusion on top of exclude_user and exclude_kernel. The result is that when the event triggered in the kernel but we have exclude_kernel set, we try to rewind using task_pt_regs. There are two side effects of this: - we call task_pt_regs even on kernel threads, which doesn't give us the desired result. - if the event occured in the kernel, we shouldn't rewind to the user context. We want to actually ignore the event. get_irq_regs() will always give us the right interrupted context, so use its result and submit it to perf_exclude_context() that knows when an event must be ignored. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 9efdfe5..095101d 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4164,15 +4164,8 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; regs = get_irq_regs(); - /* - * In case we exclude kernel IPs or are somehow not in interrupt - * context, provide the next best thing, the user IP. - */ - if ((event->attr.exclude_kernel || !regs) && - !event->attr.exclude_user) - regs = task_pt_regs(current); - if (regs) { + if (regs && !perf_exclude_event(event, regs)) { if (!(event->attr.exclude_idle && current->pid == 0)) if (perf_event_overflow(event, 0, &data, regs)) ret = HRTIMER_NORESTART; -- cgit v0.10.2 From fcd1498405c2c88ac632e7c3c3fce3213d9196db Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 19:11:29 +0200 Subject: perf tools: Fix accidentally preprocessed snprintf callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct sort_entry has a callback named snprintf that turns an entry into a string result. But there are glibc versions that implement snprintf through a macro. The following expression is then going to get the snprintf call preprocessed: ent->snprintf(...) to finally end up in a build error: util/hist.c: Dans la fonction «hist_entry__snprintf» : util/hist.c:539: erreur: «struct sort_entry» has no member named «__builtin___snprintf_chk» To fix this, prepend struct sort_entry callbacks with an "se_" prefix. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 18cf8b3..9c2b874 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { - cmp = se->cmp(left, right); + cmp = se->se_cmp(left, right); if (cmp) break; } @@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) list_for_each_entry(se, &hist_entry__sort_list, list) { int64_t (*f)(struct hist_entry *, struct hist_entry *); - f = se->collapse ?: se->cmp; + f = se->se_collapse ?: se->se_cmp; cmp = f(left, right); if (cmp) @@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self, continue; ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->snprintf(self, s + ret, size - ret, - se->width ? *se->width : 0); + ret += se->se_snprintf(self, s + ret, size - ret, + se->se_width ? *se->se_width : 0); } return ret; @@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->width ? *se->width : 0; + left_margin = se->se_width ? *se->se_width : 0; left_margin -= thread__comm_len(self->thread); } @@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, if (se->elide) continue; if (sep) { - fprintf(fp, "%c%s", *sep, se->header); + fprintf(fp, "%c%s", *sep, se->se_header); continue; } - width = strlen(se->header); - if (se->width) { + width = strlen(se->se_header); + if (se->se_width) { if (symbol_conf.col_width_list_str) { if (col_width) { - *se->width = atoi(col_width); + *se->se_width = atoi(col_width); col_width = strchr(col_width, ','); if (col_width) ++col_width; } } - width = *se->width = max(*se->width, width); + width = *se->se_width = max(*se->se_width, width); } - fprintf(fp, " %*s", width, se->header); + fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, continue; fprintf(fp, " "); - if (se->width) - width = *se->width; + if (se->se_width) + width = *se->se_width; else - width = strlen(se->header); + width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9d24d4b..da30b30 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); struct sort_entry sort_thread = { - .header = "Command: Pid", - .cmp = sort__thread_cmp, - .snprintf = hist_entry__thread_snprintf, - .width = &threads__col_width, + .se_header = "Command: Pid", + .se_cmp = sort__thread_cmp, + .se_snprintf = hist_entry__thread_snprintf, + .se_width = &threads__col_width, }; struct sort_entry sort_comm = { - .header = "Command", - .cmp = sort__comm_cmp, - .collapse = sort__comm_collapse, - .snprintf = hist_entry__comm_snprintf, - .width = &comms__col_width, + .se_header = "Command", + .se_cmp = sort__comm_cmp, + .se_collapse = sort__comm_collapse, + .se_snprintf = hist_entry__comm_snprintf, + .se_width = &comms__col_width, }; struct sort_entry sort_dso = { - .header = "Shared Object", - .cmp = sort__dso_cmp, - .snprintf = hist_entry__dso_snprintf, - .width = &dsos__col_width, + .se_header = "Shared Object", + .se_cmp = sort__dso_cmp, + .se_snprintf = hist_entry__dso_snprintf, + .se_width = &dsos__col_width, }; struct sort_entry sort_sym = { - .header = "Symbol", - .cmp = sort__sym_cmp, - .snprintf = hist_entry__sym_snprintf, + .se_header = "Symbol", + .se_cmp = sort__sym_cmp, + .se_snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { - .header = "Parent symbol", - .cmp = sort__parent_cmp, - .snprintf = hist_entry__parent_snprintf, - .width = &parent_symbol__col_width, + .se_header = "Parent symbol", + .se_cmp = sort__parent_cmp, + .se_snprintf = hist_entry__parent_snprintf, + .se_width = &parent_symbol__col_width, }; struct sort_dimension { @@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; - if (sd->entry->collapse) + if (sd->entry->se_collapse) sort__need_collapse = 1; if (sd->entry == &sort_parent) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6d7b4be..1d857aa 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -78,13 +78,13 @@ enum sort_type { struct sort_entry { struct list_head list; - const char *header; + const char *se_header; - int64_t (*cmp)(struct hist_entry *, struct hist_entry *); - int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - int (*snprintf)(struct hist_entry *self, char *bf, size_t size, - unsigned int width); - unsigned int *width; + int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); + int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); + int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); + unsigned int *se_width; bool elide; }; -- cgit v0.10.2 From 48481938b02471d505296d7557ed296eb093d496 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:16:53 -0400 Subject: perf probe: Support argument name Set given names to event arguments. The syntax is same as kprobe-tracer, you can add 'NAME=' right before each argument. e.g. ./perf probe vfs_read foo=file then, 'foo' is set to the argument name as below. ./perf probe -l probe:vfs_read (on vfs_read@linux-2.6-tip/fs/read_write.c with foo) Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171653.3790.74624.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index bb671b3..e36ed4d 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -79,7 +79,15 @@ Probe points are defined by following syntax. 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. -'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). +'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT). + +PROBE ARGUMENT +-------------- +Each probe argument follows below syntax. + + [NAME=]LOCALVAR|$retval|%REG|@SYMBOL + +'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). LINE SYNTAX ----------- diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index bfc47ff..daf4668 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -142,9 +142,9 @@ static const struct option options[] = { OPT_CALLBACK('a', "add", NULL, #ifdef DWARF_SUPPORT "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" - " [ARG ...]", + " [[NAME=]ARG ...]", #else - "[EVENT=]FUNC[+OFF|%return] [ARG ...]", + "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]", #endif "probe point definition, where\n" "\t\tGROUP:\tGroup name (optional)\n" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3fc0be7..ab6f53d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) /* Parse perf-probe event argument */ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) { - const char *tmp; + char *tmp; struct perf_probe_arg_field **fieldp; pr_debug("parsing arg: %s into ", str); + tmp = strchr(str, '='); + if (tmp) { + arg->name = xstrndup(str, tmp - str); + str = tmp + 1; + } + tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ - arg->name = xstrdup(str); - pr_debug("%s\n", arg->name); + arg->var = xstrdup(str); + pr_debug("%s\n", arg->var); return; } /* Structure fields */ - arg->name = xstrndup(str, tmp - str); - pr_debug("%s, ", arg->name); + arg->var = xstrndup(str, tmp - str); + pr_debug("%s, ", arg->var); fieldp = &arg->field; do { @@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); for (i = 0; i < pev->nargs; i++) { parse_perf_probe_arg(argv[i + 1], &pev->args[i]); - if (is_c_varname(pev->args[i].name) && pev->point.retprobe) + if (is_c_varname(pev->args[i].var) && pev->point.retprobe) semantic_error("You can't specify local variable for" " kretprobe"); } @@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) return true; for (i = 0; i < pev->nargs; i++) - if (is_c_varname(pev->args[i].name)) + if (is_c_varname(pev->args[i].var)) return true; return false; @@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) int ret; char *tmp = buf; - ret = e_snprintf(tmp, len, "%s", pa->name); + if (pa->name && pa->var) + ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); + else + ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); if (ret <= 0) goto error; tmp += ret; @@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev) for (i = 0; i < pev->nargs; i++) { if (pev->args[i].name) free(pev->args[i].name); + if (pev->args[i].var) + free(pev->args[i].var); field = pev->args[i].field; while (field) { next = field->next; @@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (tev->nargs) { tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); - for (i = 0; i < tev->nargs; i++) - tev->args[i].value = xstrdup(pev->args[i].name); + for (i = 0; i < tev->nargs; i++) { + if (pev->args[i].name) + tev->args[i].name = xstrdup(pev->args[i].name); + tev->args[i].value = xstrdup(pev->args[i].var); + } } /* Currently just checking function name from symbol map */ diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 9d99fc2..10411f5 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -55,6 +55,7 @@ struct perf_probe_arg_field { /* Perf probe probing argument */ struct perf_probe_arg { char *name; /* Argument name */ + char *var; /* Variable name */ struct perf_probe_arg_field *field; /* Structure fields */ }; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index a851377..105e95c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -484,35 +484,40 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) convert_location(expr, pf); if (pf->pvar->field) - convert_variable_fields(vr_die, pf->pvar->name, + convert_variable_fields(vr_die, pf->pvar->var, pf->pvar->field, &pf->tvar->ref); /* *expr will be cached in libdw. Don't free it. */ return ; error: /* TODO: Support const_value */ die("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.", pf->pvar->name); + " Perhaps, it has been optimized out.", pf->pvar->var); } /* Find a variable in a subprogram die */ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; - char buf[128]; + char buf[32]; - /* TODO: Support struct members and arrays */ - if (!is_c_varname(pf->pvar->name)) { + /* TODO: Support arrays */ + if (pf->pvar->name) + pf->tvar->name = xstrdup(pf->pvar->name); + else { + synthesize_perf_probe_arg(pf->pvar, buf, 32); + pf->tvar->name = xstrdup(buf); + } + + if (!is_c_varname(pf->pvar->var)) { /* Copy raw parameters */ - pf->tvar->value = xstrdup(pf->pvar->name); + pf->tvar->value = xstrdup(pf->pvar->var); } else { - synthesize_perf_probe_arg(pf->pvar, buf, 128); - pf->tvar->name = xstrdup(buf); pr_debug("Searching '%s' variable in context.\n", - pf->pvar->name); + pf->pvar->var); /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->pvar->name, &vr_die)) + if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) die("Failed to find '%s' in this function.", - pf->pvar->name); + pf->pvar->var); convert_variable(&vr_die, pf); } } -- cgit v0.10.2 From df0faf4be02996135bc3a06b4f34360449c78084 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:00 -0400 Subject: perf probe: Use the last field name as the argument name Set the last field name to the argument name when the argument is refering a data-structure member. e.g. ./perf probe --add 'vfs_read file->f_mode' Add new event: probe:vfs_read (on vfs_read with f_mode=file->f_mode) This probe records file->f_mode, but the argument name becomes "f_mode". This enables perf-trace command to parse trace event format correctly. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171700.3790.72961.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index e36ed4d..441324f2 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -87,7 +87,7 @@ Each probe argument follows below syntax. [NAME=]LOCALVAR|$retval|%REG|@SYMBOL -'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). +'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) LINE SYNTAX ----------- diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ab6f53d..19de8b7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -481,6 +481,10 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) } while (tmp); (*fieldp)->name = xstrdup(str); pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); + + /* If no name is specified, set the last field name */ + if (!arg->name) + arg->name = xstrdup((*fieldp)->name); } /* Parse perf-probe event command */ -- cgit v0.10.2 From 93ccae7a2227466a0d071fe52c51319f2f34c365 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:08 -0400 Subject: tracing/kprobes: Support basic types on dynamic events Support basic types of integer (u8, u16, u32, u64, s8, s16, s32, s64) in kprobe tracer. With this patch, users can specify above basic types on each arguments after ':'. If omitted, the argument type is set as unsigned long (u32 or u64, arch-dependent). e.g. echo 'p account_system_time+0 hardirq_offset=%si:s32' > kprobe_events adds a probe recording hardirq_offset in signed-32bits value on the entry of account_system_time. Cc: Ingo Molnar Cc: Steven Rostedt Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171708.3790.18599.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt index a9100b2..ec94748 100644 --- a/Documentation/trace/kprobetrace.txt +++ b/Documentation/trace/kprobetrace.txt @@ -40,7 +40,9 @@ Synopsis of kprobe_events $stack : Fetch stack address. $retval : Fetch return value.(*) +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) - NAME=FETCHARG: Set NAME as the argument name of FETCHARG. + NAME=FETCHARG : Set NAME as the argument name of FETCHARG. + FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types + (u8/u16/u32/u64/s8/s16/s32/s64) are supported. (*) only for return probe. (**) this is useful for fetching a field of data structures. diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index bec2c97..3ebdb6b 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -102,29 +102,17 @@ struct syscall_trace_exit { long ret; }; -struct kprobe_trace_entry { +struct kprobe_trace_entry_head { struct trace_entry ent; unsigned long ip; - int nargs; - unsigned long args[]; }; -#define SIZEOF_KPROBE_TRACE_ENTRY(n) \ - (offsetof(struct kprobe_trace_entry, args) + \ - (sizeof(unsigned long) * (n))) - -struct kretprobe_trace_entry { +struct kretprobe_trace_entry_head { struct trace_entry ent; unsigned long func; unsigned long ret_ip; - int nargs; - unsigned long args[]; }; -#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \ - (offsetof(struct kretprobe_trace_entry, args) + \ - (sizeof(unsigned long) * (n))) - /* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are: diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 1251e36..a751432 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "trace.h" #include "trace_output.h" @@ -40,7 +42,6 @@ /* Reserved field names */ #define FIELD_STRING_IP "__probe_ip" -#define FIELD_STRING_NARGS "__probe_nargs" #define FIELD_STRING_RETIP "__probe_ret_ip" #define FIELD_STRING_FUNC "__probe_func" @@ -52,56 +53,102 @@ const char *reserved_field_names[] = { "common_tgid", "common_lock_depth", FIELD_STRING_IP, - FIELD_STRING_NARGS, FIELD_STRING_RETIP, FIELD_STRING_FUNC, }; -struct fetch_func { - unsigned long (*func)(struct pt_regs *, void *); +/* Printing function type */ +typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *); +#define PRINT_TYPE_FUNC_NAME(type) print_type_##type +#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type + +/* Printing in basic type function template */ +#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \ +static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \ + const char *name, void *data)\ +{ \ + return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\ +} \ +static const char PRINT_TYPE_FMT_NAME(type)[] = fmt; + +DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int) +DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int) +DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long) +DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long) +DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int) +DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int) +DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long) +DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long) + +/* Data fetch function type */ +typedef void (*fetch_func_t)(struct pt_regs *, void *, void *); + +struct fetch_param { + fetch_func_t fn; void *data; }; -static __kprobes unsigned long call_fetch(struct fetch_func *f, - struct pt_regs *regs) +static __kprobes void call_fetch(struct fetch_param *fprm, + struct pt_regs *regs, void *dest) { - return f->func(regs, f->data); + return fprm->fn(regs, fprm->data, dest); } -/* fetch handlers */ -static __kprobes unsigned long fetch_register(struct pt_regs *regs, - void *offset) -{ - return regs_get_register(regs, (unsigned int)((unsigned long)offset)); +#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type +/* + * Define macro for basic types - we don't need to define s* types, because + * we have to care only about bitwidth at recording time. + */ +#define DEFINE_BASIC_FETCH_FUNCS(kind) \ +DEFINE_FETCH_##kind(u8) \ +DEFINE_FETCH_##kind(u16) \ +DEFINE_FETCH_##kind(u32) \ +DEFINE_FETCH_##kind(u64) + +#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \ + ((FETCH_FUNC_NAME(kind, u8) == fn) || \ + (FETCH_FUNC_NAME(kind, u16) == fn) || \ + (FETCH_FUNC_NAME(kind, u32) == fn) || \ + (FETCH_FUNC_NAME(kind, u64) == fn)) + +/* Data fetch function templates */ +#define DEFINE_FETCH_reg(type) \ +static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \ + void *offset, void *dest) \ +{ \ + *(type *)dest = (type)regs_get_register(regs, \ + (unsigned int)((unsigned long)offset)); \ } - -static __kprobes unsigned long fetch_stack(struct pt_regs *regs, - void *num) -{ - return regs_get_kernel_stack_nth(regs, - (unsigned int)((unsigned long)num)); +DEFINE_BASIC_FETCH_FUNCS(reg) + +#define DEFINE_FETCH_stack(type) \ +static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\ + void *offset, void *dest) \ +{ \ + *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \ + (unsigned int)((unsigned long)offset)); \ } +DEFINE_BASIC_FETCH_FUNCS(stack) -static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr) -{ - unsigned long retval; - - if (probe_kernel_address(addr, retval)) - return 0; - return retval; +#define DEFINE_FETCH_retval(type) \ +static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\ + void *dummy, void *dest) \ +{ \ + *(type *)dest = (type)regs_return_value(regs); \ } - -static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs, - void *dummy) -{ - return regs_return_value(regs); -} - -static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs, - void *dummy) -{ - return kernel_stack_pointer(regs); +DEFINE_BASIC_FETCH_FUNCS(retval) + +#define DEFINE_FETCH_memory(type) \ +static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\ + void *addr, void *dest) \ +{ \ + type retval; \ + if (probe_kernel_address(addr, retval)) \ + *(type *)dest = 0; \ + else \ + *(type *)dest = retval; \ } +DEFINE_BASIC_FETCH_FUNCS(memory) /* Memory fetching by symbol */ struct symbol_cache { @@ -145,51 +192,126 @@ static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset) return sc; } -static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data) -{ - struct symbol_cache *sc = data; - - if (sc->addr) - return fetch_memory(regs, (void *)sc->addr); - else - return 0; +#define DEFINE_FETCH_symbol(type) \ +static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\ + void *data, void *dest) \ +{ \ + struct symbol_cache *sc = data; \ + if (sc->addr) \ + fetch_memory_##type(regs, (void *)sc->addr, dest); \ + else \ + *(type *)dest = 0; \ } +DEFINE_BASIC_FETCH_FUNCS(symbol) -/* Special indirect memory access interface */ -struct indirect_fetch_data { - struct fetch_func orig; +/* Dereference memory access function */ +struct deref_fetch_param { + struct fetch_param orig; long offset; }; -static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data) -{ - struct indirect_fetch_data *ind = data; - unsigned long addr; - - addr = call_fetch(&ind->orig, regs); - if (addr) { - addr += ind->offset; - return fetch_memory(regs, (void *)addr); - } else - return 0; +#define DEFINE_FETCH_deref(type) \ +static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\ + void *data, void *dest) \ +{ \ + struct deref_fetch_param *dprm = data; \ + unsigned long addr; \ + call_fetch(&dprm->orig, regs, &addr); \ + if (addr) { \ + addr += dprm->offset; \ + fetch_memory_##type(regs, (void *)addr, dest); \ + } else \ + *(type *)dest = 0; \ } +DEFINE_BASIC_FETCH_FUNCS(deref) -static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data) +static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) { - if (data->orig.func == fetch_indirect) - free_indirect_fetch_data(data->orig.data); - else if (data->orig.func == fetch_symbol) + if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn)) + free_deref_fetch_param(data->orig.data); + else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn)) free_symbol_cache(data->orig.data); kfree(data); } +/* Default (unsigned long) fetch type */ +#define __DEFAULT_FETCH_TYPE(t) u##t +#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) +#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) +#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) + +#define ASSIGN_FETCH_FUNC(kind, type) \ + .kind = FETCH_FUNC_NAME(kind, type) + +#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ + {.name = #ptype, \ + .size = sizeof(ftype), \ + .is_signed = sign, \ + .print = PRINT_TYPE_FUNC_NAME(ptype), \ + .fmt = PRINT_TYPE_FMT_NAME(ptype), \ +ASSIGN_FETCH_FUNC(reg, ftype), \ +ASSIGN_FETCH_FUNC(stack, ftype), \ +ASSIGN_FETCH_FUNC(retval, ftype), \ +ASSIGN_FETCH_FUNC(memory, ftype), \ +ASSIGN_FETCH_FUNC(symbol, ftype), \ +ASSIGN_FETCH_FUNC(deref, ftype), \ + } + +/* Fetch type information table */ +static const struct fetch_type { + const char *name; /* Name of type */ + size_t size; /* Byte size of type */ + int is_signed; /* Signed flag */ + print_type_func_t print; /* Print functions */ + const char *fmt; /* Fromat string */ + /* Fetch functions */ + fetch_func_t reg; + fetch_func_t stack; + fetch_func_t retval; + fetch_func_t memory; + fetch_func_t symbol; + fetch_func_t deref; +} fetch_type_table[] = { + ASSIGN_FETCH_TYPE(u8, u8, 0), + ASSIGN_FETCH_TYPE(u16, u16, 0), + ASSIGN_FETCH_TYPE(u32, u32, 0), + ASSIGN_FETCH_TYPE(u64, u64, 0), + ASSIGN_FETCH_TYPE(s8, u8, 1), + ASSIGN_FETCH_TYPE(s16, u16, 1), + ASSIGN_FETCH_TYPE(s32, u32, 1), + ASSIGN_FETCH_TYPE(s64, u64, 1), +}; + +static const struct fetch_type *find_fetch_type(const char *type) +{ + int i; + + if (!type) + type = DEFAULT_FETCH_TYPE_STR; + + for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) + if (strcmp(type, fetch_type_table[i].name) == 0) + return &fetch_type_table[i]; + return NULL; +} + +/* Special function : only accept unsigned long */ +static __kprobes void fetch_stack_address(struct pt_regs *regs, + void *dummy, void *dest) +{ + *(unsigned long *)dest = kernel_stack_pointer(regs); +} + /** * Kprobe event core functions */ struct probe_arg { - struct fetch_func fetch; - const char *name; + struct fetch_param fetch; + unsigned int offset; /* Offset from argument entry */ + const char *name; /* Name of this argument */ + const char *comm; /* Command of this argument */ + const struct fetch_type *type; /* Type of this argument */ }; /* Flags for trace_probe */ @@ -204,6 +326,7 @@ struct trace_probe { const char *symbol; /* symbol name */ struct ftrace_event_call call; struct trace_event event; + ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[]; }; @@ -212,6 +335,7 @@ struct trace_probe { (offsetof(struct trace_probe, args) + \ (sizeof(struct probe_arg) * (n))) + static __kprobes int probe_is_return(struct trace_probe *tp) { return tp->rp.handler != NULL; @@ -222,49 +346,6 @@ static __kprobes const char *probe_symbol(struct trace_probe *tp) return tp->symbol ? tp->symbol : "unknown"; } -static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff) -{ - int ret = -EINVAL; - - if (ff->func == fetch_register) { - const char *name; - name = regs_query_register_name((unsigned int)((long)ff->data)); - ret = snprintf(buf, n, "%%%s", name); - } else if (ff->func == fetch_stack) - ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data); - else if (ff->func == fetch_memory) - ret = snprintf(buf, n, "@0x%p", ff->data); - else if (ff->func == fetch_symbol) { - struct symbol_cache *sc = ff->data; - if (sc->offset) - ret = snprintf(buf, n, "@%s%+ld", sc->symbol, - sc->offset); - else - ret = snprintf(buf, n, "@%s", sc->symbol); - } else if (ff->func == fetch_retvalue) - ret = snprintf(buf, n, "$retval"); - else if (ff->func == fetch_stack_address) - ret = snprintf(buf, n, "$stack"); - else if (ff->func == fetch_indirect) { - struct indirect_fetch_data *id = ff->data; - size_t l = 0; - ret = snprintf(buf, n, "%+ld(", id->offset); - if (ret >= n) - goto end; - l += ret; - ret = probe_arg_string(buf + l, n - l, &id->orig); - if (ret < 0) - goto end; - l += ret; - ret = snprintf(buf + l, n - l, ")"); - ret += l; - } -end: - if (ret >= n) - return -ENOSPC; - return ret; -} - static int register_probe_event(struct trace_probe *tp); static void unregister_probe_event(struct trace_probe *tp); @@ -347,11 +428,12 @@ error: static void free_probe_arg(struct probe_arg *arg) { - if (arg->fetch.func == fetch_symbol) + if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn)) + free_deref_fetch_param(arg->fetch.data); + else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn)) free_symbol_cache(arg->fetch.data); - else if (arg->fetch.func == fetch_indirect) - free_indirect_fetch_data(arg->fetch.data); kfree(arg->name); + kfree(arg->comm); } static void free_trace_probe(struct trace_probe *tp) @@ -457,28 +539,30 @@ static int split_symbol_offset(char *symbol, unsigned long *offset) #define PARAM_MAX_ARGS 16 #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) -static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return) +static int parse_probe_vars(char *arg, const struct fetch_type *t, + struct fetch_param *f, int is_return) { int ret = 0; unsigned long param; if (strcmp(arg, "retval") == 0) { - if (is_return) { - ff->func = fetch_retvalue; - ff->data = NULL; - } else + if (is_return) + f->fn = t->retval; + else ret = -EINVAL; } else if (strncmp(arg, "stack", 5) == 0) { if (arg[5] == '\0') { - ff->func = fetch_stack_address; - ff->data = NULL; + if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0) + f->fn = fetch_stack_address; + else + ret = -EINVAL; } else if (isdigit(arg[5])) { ret = strict_strtoul(arg + 5, 10, ¶m); if (ret || param > PARAM_MAX_STACK) ret = -EINVAL; else { - ff->func = fetch_stack; - ff->data = (void *)param; + f->fn = t->stack; + f->data = (void *)param; } } else ret = -EINVAL; @@ -488,7 +572,8 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return) } /* Recursive argument parser */ -static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) +static int __parse_probe_arg(char *arg, const struct fetch_type *t, + struct fetch_param *f, int is_return) { int ret = 0; unsigned long param; @@ -497,13 +582,13 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) switch (arg[0]) { case '$': - ret = parse_probe_vars(arg + 1, ff, is_return); + ret = parse_probe_vars(arg + 1, t, f, is_return); break; case '%': /* named register */ ret = regs_query_register_offset(arg + 1); if (ret >= 0) { - ff->func = fetch_register; - ff->data = (void *)(unsigned long)ret; + f->fn = t->reg; + f->data = (void *)(unsigned long)ret; ret = 0; } break; @@ -512,26 +597,22 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) ret = strict_strtoul(arg + 1, 0, ¶m); if (ret) break; - ff->func = fetch_memory; - ff->data = (void *)param; + f->fn = t->memory; + f->data = (void *)param; } else { ret = split_symbol_offset(arg + 1, &offset); if (ret) break; - ff->data = alloc_symbol_cache(arg + 1, offset); - if (ff->data) - ff->func = fetch_symbol; - else - ret = -EINVAL; + f->data = alloc_symbol_cache(arg + 1, offset); + if (f->data) + f->fn = t->symbol; } break; - case '+': /* indirect memory */ + case '+': /* deref memory */ case '-': tmp = strchr(arg, '('); - if (!tmp) { - ret = -EINVAL; + if (!tmp) break; - } *tmp = '\0'; ret = strict_strtol(arg + 1, 0, &offset); if (ret) @@ -541,38 +622,58 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) arg = tmp + 1; tmp = strrchr(arg, ')'); if (tmp) { - struct indirect_fetch_data *id; + struct deref_fetch_param *dprm; + const struct fetch_type *t2 = find_fetch_type(NULL); *tmp = '\0'; - id = kzalloc(sizeof(struct indirect_fetch_data), - GFP_KERNEL); - if (!id) + dprm = kzalloc(sizeof(struct deref_fetch_param), + GFP_KERNEL); + if (!dprm) return -ENOMEM; - id->offset = offset; - ret = __parse_probe_arg(arg, &id->orig, is_return); + dprm->offset = offset; + ret = __parse_probe_arg(arg, t2, &dprm->orig, + is_return); if (ret) - kfree(id); + kfree(dprm); else { - ff->func = fetch_indirect; - ff->data = (void *)id; + f->fn = t->deref; + f->data = (void *)dprm; } - } else - ret = -EINVAL; + } break; - default: - /* TODO: support custom handler */ - ret = -EINVAL; } + if (!ret && !f->fn) + ret = -EINVAL; return ret; } /* String length checking wrapper */ -static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return) +static int parse_probe_arg(char *arg, struct trace_probe *tp, + struct probe_arg *parg, int is_return) { + const char *t; + if (strlen(arg) > MAX_ARGSTR_LEN) { pr_info("Argument is too long.: %s\n", arg); return -ENOSPC; } - return __parse_probe_arg(arg, ff, is_return); + parg->comm = kstrdup(arg, GFP_KERNEL); + if (!parg->comm) { + pr_info("Failed to allocate memory for command '%s'.\n", arg); + return -ENOMEM; + } + t = strchr(parg->comm, ':'); + if (t) { + arg[t - parg->comm] = '\0'; + t++; + } + parg->type = find_fetch_type(t); + if (!parg->type) { + pr_info("Unsupported type: %s\n", t); + return -EINVAL; + } + parg->offset = tp->size; + tp->size += parg->type->size; + return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); } /* Return 1 if name is reserved or already used by another argument */ @@ -602,15 +703,18 @@ static int create_trace_probe(int argc, char **argv) * @ADDR : fetch memory at ADDR (ADDR should be in kernel) * @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol) * %REG : fetch register REG - * Indirect memory fetch: + * Dereferencing memory fetch: * +|-offs(ARG) : fetch memory at ARG +|- offs address. * Alias name of args: * NAME=FETCHARG : set NAME as alias of FETCHARG. + * Type of args: + * FETCHARG:TYPE : use TYPE instead of unsigned long. */ struct trace_probe *tp; int i, ret = 0; int is_return = 0, is_delete = 0; - char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL; + char *symbol = NULL, *event = NULL, *group = NULL; + char *arg, *tmp; unsigned long offset = 0; void *addr = NULL; char buf[MAX_EVENT_NAME_LEN]; @@ -723,13 +827,6 @@ static int create_trace_probe(int argc, char **argv) else arg = argv[i]; - if (conflict_field_name(argv[i], tp->args, i)) { - pr_info("Argument%d name '%s' conflicts with " - "another field.\n", i, argv[i]); - ret = -EINVAL; - goto error; - } - tp->args[i].name = kstrdup(argv[i], GFP_KERNEL); if (!tp->args[i].name) { pr_info("Failed to allocate argument%d name '%s'.\n", @@ -737,9 +834,19 @@ static int create_trace_probe(int argc, char **argv) ret = -ENOMEM; goto error; } + tmp = strchr(tp->args[i].name, ':'); + if (tmp) + *tmp = '_'; /* convert : to _ */ + + if (conflict_field_name(tp->args[i].name, tp->args, i)) { + pr_info("Argument%d name '%s' conflicts with " + "another field.\n", i, argv[i]); + ret = -EINVAL; + goto error; + } /* Parse fetch argument */ - ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return); + ret = parse_probe_arg(arg, tp, &tp->args[i], is_return); if (ret) { pr_info("Parse error at argument%d. (%d)\n", i, ret); kfree(tp->args[i].name); @@ -794,8 +901,7 @@ static void probes_seq_stop(struct seq_file *m, void *v) static int probes_seq_show(struct seq_file *m, void *v) { struct trace_probe *tp = v; - int i, ret; - char buf[MAX_ARGSTR_LEN + 1]; + int i; seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p'); seq_printf(m, ":%s/%s", tp->call.system, tp->call.name); @@ -807,15 +913,10 @@ static int probes_seq_show(struct seq_file *m, void *v) else seq_printf(m, " %s", probe_symbol(tp)); - for (i = 0; i < tp->nr_args; i++) { - ret = probe_arg_string(buf, MAX_ARGSTR_LEN, &tp->args[i].fetch); - if (ret < 0) { - pr_warning("Argument%d decoding error(%d).\n", i, ret); - return ret; - } - seq_printf(m, " %s=%s", tp->args[i].name, buf); - } + for (i = 0; i < tp->nr_args; i++) + seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm); seq_printf(m, "\n"); + return 0; } @@ -945,9 +1046,10 @@ static const struct file_operations kprobe_profile_ops = { static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) { struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); - struct kprobe_trace_entry *entry; + struct kprobe_trace_entry_head *entry; struct ring_buffer_event *event; struct ring_buffer *buffer; + u8 *data; int size, i, pc; unsigned long irq_flags; struct ftrace_event_call *call = &tp->call; @@ -957,7 +1059,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) local_save_flags(irq_flags); pc = preempt_count(); - size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args); + size = sizeof(*entry) + tp->size; event = trace_current_buffer_lock_reserve(&buffer, call->id, size, irq_flags, pc); @@ -965,10 +1067,10 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs) return; entry = ring_buffer_event_data(event); - entry->nargs = tp->nr_args; entry->ip = (unsigned long)kp->addr; + data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - entry->args[i] = call_fetch(&tp->args[i].fetch, regs); + call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); if (!filter_current_check_discard(buffer, call, entry, event)) trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); @@ -979,9 +1081,10 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, struct pt_regs *regs) { struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); - struct kretprobe_trace_entry *entry; + struct kretprobe_trace_entry_head *entry; struct ring_buffer_event *event; struct ring_buffer *buffer; + u8 *data; int size, i, pc; unsigned long irq_flags; struct ftrace_event_call *call = &tp->call; @@ -989,7 +1092,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, local_save_flags(irq_flags); pc = preempt_count(); - size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args); + size = sizeof(*entry) + tp->size; event = trace_current_buffer_lock_reserve(&buffer, call->id, size, irq_flags, pc); @@ -997,11 +1100,11 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, return; entry = ring_buffer_event_data(event); - entry->nargs = tp->nr_args; entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; + data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - entry->args[i] = call_fetch(&tp->args[i].fetch, regs); + call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); if (!filter_current_check_discard(buffer, call, entry, event)) trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc); @@ -1011,13 +1114,14 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri, enum print_line_t print_kprobe_event(struct trace_iterator *iter, int flags) { - struct kprobe_trace_entry *field; + struct kprobe_trace_entry_head *field; struct trace_seq *s = &iter->seq; struct trace_event *event; struct trace_probe *tp; + u8 *data; int i; - field = (struct kprobe_trace_entry *)iter->ent; + field = (struct kprobe_trace_entry_head *)iter->ent; event = ftrace_find_event(field->ent.type); tp = container_of(event, struct trace_probe, event); @@ -1030,9 +1134,10 @@ print_kprobe_event(struct trace_iterator *iter, int flags) if (!trace_seq_puts(s, ")")) goto partial; - for (i = 0; i < field->nargs; i++) - if (!trace_seq_printf(s, " %s=%lx", - tp->args[i].name, field->args[i])) + data = (u8 *)&field[1]; + for (i = 0; i < tp->nr_args; i++) + if (!tp->args[i].type->print(s, tp->args[i].name, + data + tp->args[i].offset)) goto partial; if (!trace_seq_puts(s, "\n")) @@ -1046,13 +1151,14 @@ partial: enum print_line_t print_kretprobe_event(struct trace_iterator *iter, int flags) { - struct kretprobe_trace_entry *field; + struct kretprobe_trace_entry_head *field; struct trace_seq *s = &iter->seq; struct trace_event *event; struct trace_probe *tp; + u8 *data; int i; - field = (struct kretprobe_trace_entry *)iter->ent; + field = (struct kretprobe_trace_entry_head *)iter->ent; event = ftrace_find_event(field->ent.type); tp = container_of(event, struct trace_probe, event); @@ -1071,9 +1177,10 @@ print_kretprobe_event(struct trace_iterator *iter, int flags) if (!trace_seq_puts(s, ")")) goto partial; - for (i = 0; i < field->nargs; i++) - if (!trace_seq_printf(s, " %s=%lx", - tp->args[i].name, field->args[i])) + data = (u8 *)&field[1]; + for (i = 0; i < tp->nr_args; i++) + if (!tp->args[i].type->print(s, tp->args[i].name, + data + tp->args[i].offset)) goto partial; if (!trace_seq_puts(s, "\n")) @@ -1129,29 +1236,43 @@ static int probe_event_raw_init(struct ftrace_event_call *event_call) static int kprobe_event_define_fields(struct ftrace_event_call *event_call) { int ret, i; - struct kprobe_trace_entry field; + struct kprobe_trace_entry_head field; struct trace_probe *tp = (struct trace_probe *)event_call->data; DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); - DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1); /* Set argument names as fields */ - for (i = 0; i < tp->nr_args; i++) - DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0); + for (i = 0; i < tp->nr_args; i++) { + ret = trace_define_field(event_call, tp->args[i].type->name, + tp->args[i].name, + sizeof(field) + tp->args[i].offset, + tp->args[i].type->size, + tp->args[i].type->is_signed, + FILTER_OTHER); + if (ret) + return ret; + } return 0; } static int kretprobe_event_define_fields(struct ftrace_event_call *event_call) { int ret, i; - struct kretprobe_trace_entry field; + struct kretprobe_trace_entry_head field; struct trace_probe *tp = (struct trace_probe *)event_call->data; DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0); DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0); - DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1); /* Set argument names as fields */ - for (i = 0; i < tp->nr_args; i++) - DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0); + for (i = 0; i < tp->nr_args; i++) { + ret = trace_define_field(event_call, tp->args[i].type->name, + tp->args[i].name, + sizeof(field) + tp->args[i].offset, + tp->args[i].type->size, + tp->args[i].type->is_signed, + FILTER_OTHER); + if (ret) + return ret; + } return 0; } @@ -1176,8 +1297,8 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len) pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); for (i = 0; i < tp->nr_args; i++) { - pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%%lx", - tp->args[i].name); + pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", + tp->args[i].name, tp->args[i].type->fmt); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); @@ -1219,12 +1340,13 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, { struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); struct ftrace_event_call *call = &tp->call; - struct kprobe_trace_entry *entry; + struct kprobe_trace_entry_head *entry; + u8 *data; int size, __size, i; unsigned long irq_flags; int rctx; - __size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args); + __size = sizeof(*entry) + tp->size; size = ALIGN(__size + sizeof(u32), sizeof(u64)); size -= sizeof(u32); if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, @@ -1235,10 +1357,10 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, if (!entry) return; - entry->nargs = tp->nr_args; entry->ip = (unsigned long)kp->addr; + data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - entry->args[i] = call_fetch(&tp->args[i].fetch, regs); + call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs); } @@ -1249,12 +1371,13 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, { struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); struct ftrace_event_call *call = &tp->call; - struct kretprobe_trace_entry *entry; + struct kretprobe_trace_entry_head *entry; + u8 *data; int size, __size, i; unsigned long irq_flags; int rctx; - __size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args); + __size = sizeof(*entry) + tp->size; size = ALIGN(__size + sizeof(u32), sizeof(u64)); size -= sizeof(u32); if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, @@ -1265,11 +1388,11 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, if (!entry) return; - entry->nargs = tp->nr_args; entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; + data = (u8 *)&entry[1]; for (i = 0; i < tp->nr_args; i++) - entry->args[i] = call_fetch(&tp->args[i].fetch, regs); + call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset); perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, irq_flags, regs); -- cgit v0.10.2 From 4984912eb23113a4007940cd09c8351c0623ea5f Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:15 -0400 Subject: perf probe: Query basic types from debuginfo Query the basic type information (byte-size and signed-flag) from debuginfo and pass that to kprobe-tracer. This is especially useful for tracing the members of data structure, because each member has different byte-size on the memory. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171715.3790.23730.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 19de8b7..05ca4a9 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, buf += ret; buflen -= ret; } + /* Print argument type */ + if (arg->type) { + ret = e_snprintf(buf, buflen, ":%s", arg->type); + if (ret <= 0) + return ret; + buf += ret; + } return buf - tmp; } @@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev) free(tev->args[i].name); if (tev->args[i].value) free(tev->args[i].value); + if (tev->args[i].type) + free(tev->args[i].type); ref = tev->args[i].ref; while (ref) { next = ref->next; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 10411f5..a393a3f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref { struct kprobe_trace_arg { char *name; /* Argument name */ char *value; /* Base value */ + char *type; /* Type name */ struct kprobe_trace_arg_ref *ref; /* Referencing offset */ }; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 105e95c..ebeb413 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -84,6 +84,9 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = { #define arch_regs_table x86_32_regs_table #endif +/* Kprobe tracer basic type is up to u64 */ +#define MAX_BASIC_TYPE_BITS 64 + /* Return architecture dependent register string (for kprobe-tracer) */ static const char *get_arch_regstr(unsigned int n) { @@ -230,6 +233,31 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) return die_mem; } +static bool die_is_signed_type(Dwarf_Die *tp_die) +{ + Dwarf_Attribute attr; + Dwarf_Word ret; + + if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || + dwarf_formudata(&attr, &ret) != 0) + return false; + + return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || + ret == DW_ATE_signed_fixed); +} + +static int die_get_byte_size(Dwarf_Die *tp_die) +{ + Dwarf_Attribute attr; + Dwarf_Word ret; + + if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || + dwarf_formudata(&attr, &ret) != 0) + return 0; + + return (int)ret; +} + /* Return values for die_find callbacks */ enum { DIE_FIND_CB_FOUND = 0, /* End of Search */ @@ -406,13 +434,42 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) } } +static void convert_variable_type(Dwarf_Die *vr_die, + struct kprobe_trace_arg *targ) +{ + Dwarf_Die type; + char buf[16]; + int ret; + + if (die_get_real_type(vr_die, &type) == NULL) + die("Failed to get a type information of %s.", + dwarf_diename(vr_die)); + + ret = die_get_byte_size(&type) * 8; + if (ret) { + /* Check the bitwidth */ + if (ret > MAX_BASIC_TYPE_BITS) { + pr_warning(" Warning: %s exceeds max-bitwidth." + " Cut down to %d bits.\n", + dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + ret = MAX_BASIC_TYPE_BITS; + } + + ret = snprintf(buf, 16, "%c%d", + die_is_signed_type(&type) ? 's' : 'u', ret); + if (ret < 0 || ret >= 16) + die("Failed to convert variable type."); + targ->type = xstrdup(buf); + } +} + static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, - struct kprobe_trace_arg_ref **ref_ptr) + struct kprobe_trace_arg_ref **ref_ptr, + Dwarf_Die *die_mem) { struct kprobe_trace_arg_ref *ref = *ref_ptr; Dwarf_Attribute attr; - Dwarf_Die member; Dwarf_Die type; Dwarf_Word offs; @@ -450,26 +507,27 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, die("Structure on a register is not supported yet."); } - if (die_find_member(&type, field->name, &member) == NULL) + if (die_find_member(&type, field->name, die_mem) == NULL) die("%s(tyep:%s) has no member %s.", varname, dwarf_diename(&type), field->name); /* Get the offset of the field */ - if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || + if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL || dwarf_formudata(&attr, &offs) != 0) die("Failed to get the offset of %s.", field->name); ref->offset += (long)offs; /* Converting next field */ if (field->next) - convert_variable_fields(&member, field->name, field->next, - &ref); + convert_variable_fields(die_mem, field->name, field->next, + &ref, die_mem); } /* Show a variables in kprobe event format */ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; + Dwarf_Die die_mem; Dwarf_Op *expr; size_t nexpr; int ret; @@ -483,9 +541,13 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) convert_location(expr, pf); - if (pf->pvar->field) + if (pf->pvar->field) { convert_variable_fields(vr_die, pf->pvar->var, - pf->pvar->field, &pf->tvar->ref); + pf->pvar->field, &pf->tvar->ref, + &die_mem); + vr_die = &die_mem; + } + convert_variable_type(vr_die, pf->tvar); /* *expr will be cached in libdw. Don't free it. */ return ; error: -- cgit v0.10.2 From 11a1ca3554b377d2a8a318a3cbf8ce10a7a2a8e4 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:22 -0400 Subject: perf probe: Support basic type casting Add basic type casting for arguments to perf probe. This allows users to specify the actual type of arguments. Of course, if user sets invalid types, kprobe-tracer rejects that. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171722.3790.50372.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 441324f2..63c25d3 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -85,9 +85,10 @@ PROBE ARGUMENT -------------- Each probe argument follows below syntax. - [NAME=]LOCALVAR|$retval|%REG|@SYMBOL + [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) +'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. LINE SYNTAX ----------- diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 05ca4a9..bef2805 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -435,7 +435,7 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) } /* Parse perf-probe event argument */ -static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) +static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) { char *tmp; struct perf_probe_arg_field **fieldp; @@ -445,9 +445,17 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) tmp = strchr(str, '='); if (tmp) { arg->name = xstrndup(str, tmp - str); + pr_debug("name:%s ", arg->name); str = tmp + 1; } + tmp = strchr(str, ':'); + if (tmp) { /* Type setting */ + *tmp = '\0'; + arg->type = xstrdup(tmp + 1); + pr_debug("type:%s ", arg->type); + } + tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ @@ -603,6 +611,15 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) len -= ret; field = field->next; } + + if (pa->type) { + ret = e_snprintf(tmp, len, ":%s", pa->type); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + } + return tmp - buf; error: die("Failed to synthesize perf probe argument: %s", strerror(-ret)); @@ -825,6 +842,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pev->args[i].name); if (pev->args[i].var) free(pev->args[i].var); + if (pev->args[i].type) + free(pev->args[i].type); field = pev->args[i].field; while (field) { next = field->next; @@ -1145,6 +1164,8 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (pev->args[i].name) tev->args[i].name = xstrdup(pev->args[i].name); tev->args[i].value = xstrdup(pev->args[i].var); + if (pev->args[i].type) + tev->args[i].type = xstrdup(pev->args[i].type); } } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index a393a3f..ff2f26b 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -57,6 +57,7 @@ struct perf_probe_arg_field { struct perf_probe_arg { char *name; /* Argument name */ char *var; /* Variable name */ + char *type; /* Type name */ struct perf_probe_arg_field *field; /* Structure fields */ }; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ebeb413..ab47673 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -547,7 +547,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) &die_mem); vr_die = &die_mem; } - convert_variable_type(vr_die, pf->tvar); + if (pf->pvar->type) + pf->tvar->type = xstrdup(pf->pvar->type); + else + convert_variable_type(vr_die, pf->tvar); /* *expr will be cached in libdw. Don't free it. */ return ; error: @@ -560,13 +563,16 @@ error: static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; - char buf[32]; + char buf[32], *ptr; /* TODO: Support arrays */ if (pf->pvar->name) pf->tvar->name = xstrdup(pf->pvar->name); else { synthesize_perf_probe_arg(pf->pvar, buf, 32); + ptr = strchr(buf, ':'); /* Change type separator to _ */ + if (ptr) + *ptr = '_'; pf->tvar->name = xstrdup(buf); } -- cgit v0.10.2 From a34a985499895a46a4dacff727d0fbc63cdc75e8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:29 -0400 Subject: perf probe: Support DW_OP_call_frame_cfa in debuginfo When building kernel without CONFIG_FRAME_POINTER, gcc uses CFA (canonical frame address) for frame base. With this patch, perf probe just gets CFI (call frame information) from debuginfo and search corresponding CFA from the CFI. IOW, this allows perf probe works correctly on the kernel without CONFIG_FRAME_POINTER. ./perf probe -fn sched_slice:12 lw.weight Fatal: DW_OP 156 is not supported. (^^^ DW_OP_call_frame_cfa) ./perf probe -fn sched_slice:12 lw.weight Add new event: probe:sched_slice (on sched_slice:12 with weight=lw.weight) Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171728.3790.98217.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab47673..1f45285 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -398,7 +398,6 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) const char *regs; struct kprobe_trace_arg *tvar = pf->tvar; - /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { if (pf->fb_ops == NULL) @@ -629,11 +628,17 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Get the frame base attribute/ops */ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) + if (ret <= 0 || nops == 0) { pf->fb_ops = NULL; + } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && + pf->cfi != NULL) { + Dwarf_Frame *frame; + ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame); + DIE_IF(ret != 0); + dwarf_frame_cfa(frame, &pf->fb_ops, &nops); + } /* Find each argument */ - /* TODO: use dwarf_cfi_addrframe */ tev->nargs = pf->pev->nargs; tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); for (i = 0; i < pf->pev->nargs; i++) { @@ -842,6 +847,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, if (!dbg) return -ENOENT; + /* Get the call frame information from this dwarf */ + pf.cfi = dwarf_getcfi(dbg); + off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 2a27132..310ce89 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -42,6 +42,7 @@ struct probe_finder { struct list_head lcache; /* Line cache for lazy match */ /* For variable searching */ + Dwarf_CFI *cfi; /* Call Frame Information */ Dwarf_Op *fb_ops; /* Frame base attribute */ struct perf_probe_arg *pvar; /* Current target variable */ struct kprobe_trace_arg *tvar; /* Current result variable */ -- cgit v0.10.2 From b55a87ade3839c33ab94768a0b5955734073f987 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:35 -0400 Subject: perf probe: Remove die() from probe-finder code Remove die() and DIE_IF() code from util/probe-finder.c since these 'sudden death' in utility functions make reusing it from other code (especially tui/gui) difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171735.3790.88853.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bef2805..7893b32 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -151,10 +151,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, /* Error path */ if (need_dwarf) { - if (ntevs == -ENOENT) + if (ntevs == -EBADF) pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); + die("Failed to analyze debuginfo."); } pr_debug("An error occurred in debuginfo analysis." " Try to use symbols.\n"); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1f45285..54daa91 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -196,19 +196,7 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { const char *name; name = dwarf_diename(dw_die); - DIE_IF(name == NULL); - return strcmp(tname, name); -} - -/* Get entry pc(or low pc, 1st entry of ranges) of the die */ -static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) -{ - Dwarf_Addr epc; - int ret; - - ret = dwarf_entrypc(dw_die, &epc); - DIE_IF(ret == -1); - return epc; + return name ? strcmp(tname, name) : -1; } /* Get type die, but skip qualifiers and typedef */ @@ -390,7 +378,7 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, */ /* Show a location */ -static void convert_location(Dwarf_Op *op, struct probe_finder *pf) +static int convert_location(Dwarf_Op *op, struct probe_finder *pf) { unsigned int regn; Dwarf_Word offs = 0; @@ -400,8 +388,11 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { - if (pf->fb_ops == NULL) - die("The attribute of frame base is not supported.\n"); + if (pf->fb_ops == NULL) { + pr_warning("The attribute of frame base is not " + "supported.\n"); + return -ENOTSUP; + } ref = true; offs = op->number; op = &pf->fb_ops[0]; @@ -419,50 +410,63 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) ref = true; } else if (op->atom == DW_OP_regx) { regn = op->number; - } else - die("DW_OP %d is not supported.", op->atom); + } else { + pr_warning("DW_OP %x is not supported.\n", op->atom); + return -ENOTSUP; + } regs = get_arch_regstr(regn); - if (!regs) - die("%u exceeds max register number.", regn); + if (!regs) { + pr_warning("%u exceeds max register number.\n", regn); + return -ERANGE; + } tvar->value = xstrdup(regs); if (ref) { tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); tvar->ref->offset = (long)offs; } + return 0; } -static void convert_variable_type(Dwarf_Die *vr_die, - struct kprobe_trace_arg *targ) +static int convert_variable_type(Dwarf_Die *vr_die, + struct kprobe_trace_arg *targ) { Dwarf_Die type; char buf[16]; int ret; - if (die_get_real_type(vr_die, &type) == NULL) - die("Failed to get a type information of %s.", - dwarf_diename(vr_die)); + if (die_get_real_type(vr_die, &type) == NULL) { + pr_warning("Failed to get a type information of %s.\n", + dwarf_diename(vr_die)); + return -ENOENT; + } ret = die_get_byte_size(&type) * 8; if (ret) { /* Check the bitwidth */ if (ret > MAX_BASIC_TYPE_BITS) { - pr_warning(" Warning: %s exceeds max-bitwidth." - " Cut down to %d bits.\n", - dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + pr_info("%s exceeds max-bitwidth." + " Cut down to %d bits.\n", + dwarf_diename(&type), MAX_BASIC_TYPE_BITS); ret = MAX_BASIC_TYPE_BITS; } ret = snprintf(buf, 16, "%c%d", die_is_signed_type(&type) ? 's' : 'u', ret); - if (ret < 0 || ret >= 16) - die("Failed to convert variable type."); + if (ret < 0 || ret >= 16) { + if (ret >= 16) + ret = -E2BIG; + pr_warning("Failed to convert variable type: %s\n", + strerror(-ret)); + return ret; + } targ->type = xstrdup(buf); } + return 0; } -static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, +static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, struct kprobe_trace_arg_ref **ref_ptr, Dwarf_Die *die_mem) @@ -473,21 +477,28 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, Dwarf_Word offs; pr_debug("converting %s in %s\n", field->name, varname); - if (die_get_real_type(vr_die, &type) == NULL) - die("Failed to get a type information of %s.", varname); + if (die_get_real_type(vr_die, &type) == NULL) { + pr_warning("Failed to get the type of %s.\n", varname); + return -ENOENT; + } /* Check the pointer and dereference */ if (dwarf_tag(&type) == DW_TAG_pointer_type) { - if (!field->ref) - die("Semantic error: %s must be referred by '->'", - field->name); + if (!field->ref) { + pr_err("Semantic error: %s must be referred by '->'\n", + field->name); + return -EINVAL; + } /* Get the type pointed by this pointer */ - if (die_get_real_type(&type, &type) == NULL) - die("Failed to get a type information of %s.", varname); - + if (die_get_real_type(&type, &type) == NULL) { + pr_warning("Failed to get the type of %s.\n", varname); + return -ENOENT; + } /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) - die("%s is not a data structure.", varname); + if (dwarf_tag(&type) != DW_TAG_structure_type) { + pr_warning("%s is not a data structure.\n", varname); + return -EINVAL; + } ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); if (*ref_ptr) @@ -496,34 +507,46 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, *ref_ptr = ref; } else { /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) - die("%s is not a data structure.", varname); - - if (field->ref) - die("Semantic error: %s must be referred by '.'", - field->name); - if (!ref) - die("Structure on a register is not supported yet."); + if (dwarf_tag(&type) != DW_TAG_structure_type) { + pr_warning("%s is not a data structure.\n", varname); + return -EINVAL; + } + if (field->ref) { + pr_err("Semantic error: %s must be referred by '.'\n", + field->name); + return -EINVAL; + } + if (!ref) { + pr_warning("Structure on a register is not " + "supported yet.\n"); + return -ENOTSUP; + } } - if (die_find_member(&type, field->name, die_mem) == NULL) - die("%s(tyep:%s) has no member %s.", varname, - dwarf_diename(&type), field->name); + if (die_find_member(&type, field->name, die_mem) == NULL) { + pr_warning("%s(tyep:%s) has no member %s.\n", varname, + dwarf_diename(&type), field->name); + return -EINVAL; + } /* Get the offset of the field */ if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL || - dwarf_formudata(&attr, &offs) != 0) - die("Failed to get the offset of %s.", field->name); + dwarf_formudata(&attr, &offs) != 0) { + pr_warning("Failed to get the offset of %s.\n", field->name); + return -ENOENT; + } ref->offset += (long)offs; /* Converting next field */ if (field->next) - convert_variable_fields(die_mem, field->name, field->next, - &ref, die_mem); + return convert_variable_fields(die_mem, field->name, + field->next, &ref, die_mem); + else + return 0; } /* Show a variables in kprobe event format */ -static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) +static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; Dwarf_Die die_mem; @@ -538,28 +561,30 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) if (ret <= 0 || nexpr == 0) goto error; - convert_location(expr, pf); - - if (pf->pvar->field) { - convert_variable_fields(vr_die, pf->pvar->var, - pf->pvar->field, &pf->tvar->ref, - &die_mem); + ret = convert_location(expr, pf); + if (ret == 0 && pf->pvar->field) { + ret = convert_variable_fields(vr_die, pf->pvar->var, + pf->pvar->field, &pf->tvar->ref, + &die_mem); vr_die = &die_mem; } - if (pf->pvar->type) - pf->tvar->type = xstrdup(pf->pvar->type); - else - convert_variable_type(vr_die, pf->tvar); + if (ret == 0) { + if (pf->pvar->type) + pf->tvar->type = xstrdup(pf->pvar->type); + else + ret = convert_variable_type(vr_die, pf->tvar); + } /* *expr will be cached in libdw. Don't free it. */ - return ; + return ret; error: /* TODO: Support const_value */ - die("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.", pf->pvar->var); + pr_err("Failed to find the location of %s at this address.\n" + " Perhaps, it has been optimized out.\n", pf->pvar->var); + return -ENOENT; } /* Find a variable in a subprogram die */ -static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) +static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; char buf[32], *ptr; @@ -578,19 +603,22 @@ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) if (!is_c_varname(pf->pvar->var)) { /* Copy raw parameters */ pf->tvar->value = xstrdup(pf->pvar->var); - } else { - pr_debug("Searching '%s' variable in context.\n", - pf->pvar->var); - /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) - die("Failed to find '%s' in this function.", - pf->pvar->var); - convert_variable(&vr_die, pf); + return 0; } + + pr_debug("Searching '%s' variable in context.\n", + pf->pvar->var); + /* Search child die for local variables and parameters. */ + if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { + pr_warning("Failed to find '%s' in this function.\n", + pf->pvar->var); + return -ENOENT; + } + return convert_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { struct kprobe_trace_event *tev; Dwarf_Addr eaddr; @@ -600,22 +628,31 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Attribute fb_attr; size_t nops; - if (pf->ntevs == MAX_PROBES) - die("Too many( > %d) probe point found.\n", MAX_PROBES); + if (pf->ntevs == MAX_PROBES) { + pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES); + return -ERANGE; + } tev = &pf->tevs[pf->ntevs++]; /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { sp_die = die_find_real_subprogram(&pf->cu_die, pf->addr, &die_mem); - if (!sp_die) - die("Probe point is not found in subprograms."); + if (!sp_die) { + pr_warning("Failed to find probe point in any " + "functions.\n"); + return -ENOENT; + } } /* Copy the name of probe point */ name = dwarf_diename(sp_die); if (name) { - dwarf_entrypc(sp_die, &eaddr); + if (dwarf_entrypc(sp_die, &eaddr) != 0) { + pr_warning("Failed to get entry pc of %s\n", + dwarf_diename(sp_die)); + return -ENOENT; + } tev->point.symbol = xstrdup(name); tev->point.offset = (unsigned long)(pf->addr - eaddr); } else @@ -633,9 +670,12 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && pf->cfi != NULL) { Dwarf_Frame *frame; - ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame); - DIE_IF(ret != 0); - dwarf_frame_cfa(frame, &pf->fb_ops, &nops); + if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || + dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { + pr_warning("Failed to get CFA on 0x%jx\n", + (uintmax_t)pf->addr); + return -ENOENT; + } } /* Find each argument */ @@ -644,45 +684,53 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; - find_variable(sp_die, pf); + ret = find_variable(sp_die, pf); + if (ret != 0) + return ret; } /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; + return 0; } /* Find probe point from its line number */ -static void find_probe_point_by_line(struct probe_finder *pf) +static int find_probe_point_by_line(struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno; - int ret; + int ret = 0; - ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); + if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } - for (i = 0; i < nlines; i++) { + for (i = 0; i < nlines && ret == 0; i++) { line = dwarf_onesrcline(lines, i); - dwarf_lineno(line, &lineno); - if (lineno != pf->lno) + if (dwarf_lineno(line, &lineno) != 0 || + lineno != pf->lno) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); + if (dwarf_lineaddr(line, &addr) != 0) { + pr_warning("Failed to get the address of the line.\n"); + return -ENOENT; + } pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", (int)i, lineno, (uintmax_t)addr); pf->addr = addr; - convert_probe_point(NULL, pf); + ret = convert_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } + return ret; } /* Find lines which match lazy pattern */ @@ -690,15 +738,27 @@ static int find_lazy_match_lines(struct list_head *head, const char *fname, const char *pat) { char *fbuf, *p1, *p2; - int fd, line, nlines = 0; + int fd, ret, line, nlines = 0; struct stat st; fd = open(fname, O_RDONLY); - if (fd < 0) - die("failed to open %s", fname); - DIE_IF(fstat(fd, &st) < 0); + if (fd < 0) { + pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); + return fd; + } + + ret = fstat(fd, &st); + if (ret < 0) { + pr_warning("Failed to get the size of %s: %s\n", + fname, strerror(errno)); + return ret; + } fbuf = xmalloc(st.st_size + 2); - DIE_IF(read(fd, fbuf, st.st_size) < 0); + ret = read(fd, fbuf, st.st_size); + if (ret < 0) { + pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); + return ret; + } close(fd); fbuf[st.st_size] = '\n'; /* Dummy line */ fbuf[st.st_size + 1] = '\0'; @@ -718,7 +778,7 @@ static int find_lazy_match_lines(struct list_head *head, } /* Find probe points from lazy pattern */ -static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) +static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; @@ -726,31 +786,40 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Addr addr; Dwarf_Die die_mem; int lineno; - int ret; + int ret = 0; if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, pf->pev->point.lazy_line); - if (ret <= 0) - die("No matched lines found in %s.", pf->fname); + if (ret == 0) { + pr_debug("No matched lines found in %s.\n", pf->fname); + return 0; + } else if (ret < 0) + return ret; } - ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); - for (i = 0; i < nlines; i++) { + if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } + + for (i = 0; i < nlines && ret >= 0; i++) { line = dwarf_onesrcline(lines, i); - dwarf_lineno(line, &lineno); - if (!line_list__has_line(&pf->lcache, lineno)) + if (dwarf_lineno(line, &lineno) != 0 || + !line_list__has_line(&pf->lcache, lineno)) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); + if (dwarf_lineaddr(line, &addr) != 0) { + pr_debug("Failed to get the address of line %d.\n", + lineno); + continue; + } if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (!dwarf_haspc(sp_die, addr)) @@ -764,27 +833,42 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - convert_probe_point(sp_die, pf); + ret = convert_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ + return ret; } +/* Callback parameter with return value */ +struct dwarf_callback_param { + void *data; + int retval; +}; + static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { - struct probe_finder *pf = (struct probe_finder *)data; + struct dwarf_callback_param *param = data; + struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; + Dwarf_Addr addr; if (pp->lazy_line) - find_probe_point_lazy(in_die, pf); + param->retval = find_probe_point_lazy(in_die, pf); else { /* Get probe address */ - pf->addr = die_get_entrypc(in_die); + if (dwarf_entrypc(in_die, &addr) != 0) { + pr_warning("Failed to get entry pc of %s.\n", + dwarf_diename(in_die)); + param->retval = -ENOENT; + return DWARF_CB_ABORT; + } + pf->addr = addr; pf->addr += pp->offset; pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - convert_probe_point(in_die, pf); + param->retval = convert_probe_point(in_die, pf); } return DWARF_CB_OK; @@ -793,39 +877,53 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { - struct probe_finder *pf = (struct probe_finder *)data; + struct dwarf_callback_param *param = data; + struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || die_compare_name(sp_die, pp->function) != 0) - return 0; + return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; - find_probe_point_by_line(pf); + param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) - find_probe_point_lazy(sp_die, pf); + param->retval = find_probe_point_lazy(sp_die, pf); else { - pf->addr = die_get_entrypc(sp_die); + if (dwarf_entrypc(sp_die, &pf->addr) != 0) { + pr_warning("Failed to get entry pc of %s.\n", + dwarf_diename(sp_die)); + param->retval = -ENOENT; + return DWARF_CB_ABORT; + } pf->addr += pp->offset; /* TODO: Check the address in this function */ - convert_probe_point(sp_die, pf); + param->retval = convert_probe_point(sp_die, pf); } - } else + } else { + struct dwarf_callback_param _param = {.data = (void *)pf, + .retval = 0}; /* Inlined function: search instances */ - dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); + dwarf_func_inline_instances(sp_die, probe_point_inline_cb, + &_param); + param->retval = _param.retval; + } - return 1; /* Exit; no same symbol in this CU. */ + return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } -static void find_probe_point_by_func(struct probe_finder *pf) +static int find_probe_point_by_func(struct probe_finder *pf) { - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); + struct dwarf_callback_param _param = {.data = (void *)pf, + .retval = 0}; + dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + return _param.retval; } /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ @@ -838,14 +936,18 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; + int ret = 0; pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); *tevs = pf.tevs; pf.ntevs = 0; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -ENOENT; + if (!dbg) { + pr_warning("No dwarf info found in the vmlinux - " + "please rebuild with CONFIG_DEBUG_INFO=y.\n"); + return -EBADF; + } /* Get the call frame information from this dwarf */ pf.cfi = dwarf_getcfi(dbg); @@ -853,7 +955,8 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ - while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { + while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && + ret >= 0) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); if (!diep) @@ -867,12 +970,12 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, if (!pp->file || pf.fname) { if (pp->function) - find_probe_point_by_func(&pf); + ret = find_probe_point_by_func(&pf); else if (pp->lazy_line) - find_probe_point_lazy(NULL, &pf); + ret = find_probe_point_lazy(NULL, &pf); else { pf.lno = pp->line; - find_probe_point_by_line(&pf); + ret = find_probe_point_by_line(&pf); } } off = noff; @@ -880,7 +983,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, line_list__free(&pf.lcache); dwarf_end(dbg); - return pf.ntevs; + return (ret < 0) ? ret : pf.ntevs; } /* Reverse search */ @@ -893,10 +996,11 @@ int find_perf_probe_point(int fd, unsigned long addr, Dwarf_Addr laddr, eaddr; const char *tmp; int lineno, ret = 0; + bool found = false; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) - return -ENOENT; + return -EBADF; /* Find cu die */ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { @@ -907,82 +1011,87 @@ int find_perf_probe_point(int fd, unsigned long addr, /* Find a corresponding line */ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); if (line) { - dwarf_lineaddr(line, &laddr); - if ((Dwarf_Addr)addr == laddr) { - dwarf_lineno(line, &lineno); - ppt->line = lineno; - + if (dwarf_lineaddr(line, &laddr) == 0 && + (Dwarf_Addr)addr == laddr && + dwarf_lineno(line, &lineno) == 0) { tmp = dwarf_linesrc(line, NULL, NULL); - DIE_IF(!tmp); - ppt->file = xstrdup(tmp); - ret = 1; + if (tmp) { + ppt->line = lineno; + ppt->file = xstrdup(tmp); + found = true; + } } } /* Find a corresponding function */ if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { tmp = dwarf_diename(&spdie); - if (!tmp) + if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) goto end; - dwarf_entrypc(&spdie, &eaddr); - if (!lineno) { - /* We don't have a line number, let's use offset */ - ppt->function = xstrdup(tmp); - ppt->offset = addr - (unsigned long)eaddr; - ret = 1; - goto end; - } - if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { - /* addr in an inline function */ - tmp = dwarf_diename(&indie); - if (!tmp) - goto end; - dwarf_decl_line(&indie, &lineno); - } else { - if (eaddr == addr) /* No offset: function entry */ - lineno = ppt->line; - else - dwarf_decl_line(&spdie, &lineno); + if (ppt->line) { + if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, + &indie)) { + /* addr in an inline function */ + tmp = dwarf_diename(&indie); + if (!tmp) + goto end; + ret = dwarf_decl_line(&indie, &lineno); + } else { + if (eaddr == addr) { /* Function entry */ + lineno = ppt->line; + ret = 0; + } else + ret = dwarf_decl_line(&spdie, &lineno); + } + if (ret == 0) { + /* Make a relative line number */ + ppt->line -= lineno; + goto found; + } } + /* We don't have a line number, let's use offset */ + ppt->offset = addr - (unsigned long)eaddr; +found: ppt->function = xstrdup(tmp); - ppt->line -= lineno; /* Make a relative line number */ + found = true; } end: dwarf_end(dbg); + if (ret >= 0) + ret = found ? 1 : 0; return ret; } /* Find line range from its line number */ -static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) +static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno; - int ret; const char *src; Dwarf_Die die_mem; line_list__init(&lf->lr->line_list); - ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); + if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); - ret = dwarf_lineno(line, &lineno); - DIE_IF(ret != 0); - if (lf->lno_s > lineno || lf->lno_e < lineno) + if (dwarf_lineno(line, &lineno) != 0 || + (lf->lno_s > lineno || lf->lno_e < lineno)) continue; if (sp_die) { /* Address filtering 1: does sp_die include addr? */ - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); - if (!dwarf_haspc(sp_die, addr)) + if (dwarf_lineaddr(line, &addr) != 0 || + !dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ @@ -1007,18 +1116,22 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) free(lf->lr->path); lf->lr->path = NULL; } + return lf->found; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { - find_line_range_by_line(in_die, (struct line_finder *)data); + struct dwarf_callback_param *param = data; + + param->retval = find_line_range_by_line(in_die, param->data); return DWARF_CB_ABORT; /* No need to find other instances */ } /* Search function from function name */ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { - struct line_finder *lf = (struct line_finder *)data; + struct dwarf_callback_param *param = data; + struct line_finder *lf = param->data; struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && @@ -1033,38 +1146,47 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) lf->lno_e = lr->offset + lr->end; lr->start = lf->lno_s; lr->end = lf->lno_e; - if (dwarf_func_inline(sp_die)) + if (dwarf_func_inline(sp_die)) { + struct dwarf_callback_param _param; + _param.data = (void *)lf; + _param.retval = 0; dwarf_func_inline_instances(sp_die, - line_range_inline_cb, lf); - else - find_line_range_by_line(sp_die, lf); - return 1; + line_range_inline_cb, + &_param); + param->retval = _param.retval; + } else + param->retval = find_line_range_by_line(sp_die, lf); + return DWARF_CB_ABORT; } - return 0; + return DWARF_CB_OK; } -static void find_line_range_by_func(struct line_finder *lf) +static int find_line_range_by_func(struct line_finder *lf) { - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); + struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; + dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + return param.retval; } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; - int ret; + int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -ENOENT; + if (!dbg) { + pr_warning("No dwarf info found in the vmlinux - " + "please rebuild with CONFIG_DEBUG_INFO=y.\n"); + return -EBADF; + } /* Loop on CUs (Compilation Unit) */ - while (!lf.found) { - ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); - if (ret != 0) + while (!lf.found && ret >= 0) { + if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ @@ -1080,20 +1202,21 @@ int find_line_range(int fd, struct line_range *lr) if (!lr->file || lf.fname) { if (lr->function) - find_line_range_by_func(&lf); + ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; if (!lr->end) lf.lno_e = INT_MAX; else lf.lno_e = lr->end; - find_line_range_by_line(NULL, &lf); + ret = find_line_range_by_line(NULL, &lf); } } off = noff; } pr_debug("path: %lx\n", (unsigned long)lr->path); dwarf_end(dbg); - return lf.found; + + return (ret < 0) ? ret : lf.found; } -- cgit v0.10.2 From 146a143948ed9e8b248c0ec59937f3e9e1bbc7e5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:42 -0400 Subject: perf probe: Remove die() from probe-event code Remove die() and DIE_IF() code from util/probe-event.c since these 'sudden death' in utility functions make reusing it from other code (especially tui/gui) difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171742.3790.33650.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index daf4668..64bc11a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -59,23 +59,25 @@ static struct { /* Parse an event definition. Note that any error must die. */ -static void parse_probe_event(const char *str) +static int parse_probe_event(const char *str) { struct perf_probe_event *pev = ¶ms.events[params.nevents]; + int ret; pr_debug("probe-definition(%d): %s\n", params.nevents, str); if (++params.nevents == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); /* Parse a perf-probe command into event */ - parse_perf_probe_command(str, pev); - + ret = parse_perf_probe_command(str, pev); pr_debug("%d arguments\n", pev->nargs); + + return ret; } -static void parse_probe_event_argv(int argc, const char **argv) +static int parse_probe_event_argv(int argc, const char **argv) { - int i, len; + int i, len, ret; char *buf; /* Bind up rest arguments */ @@ -86,16 +88,18 @@ static void parse_probe_event_argv(int argc, const char **argv) len = 0; for (i = 0; i < argc; i++) len += sprintf(&buf[len], "%s ", argv[i]); - parse_probe_event(buf); + ret = parse_probe_event(buf); free(buf); + return ret; } static int opt_add_probe_event(const struct option *opt __used, const char *str, int unset __used) { if (str) - parse_probe_event(str); - return 0; + return parse_probe_event(str); + else + return 0; } static int opt_del_probe_event(const struct option *opt __used, @@ -113,11 +117,14 @@ static int opt_del_probe_event(const struct option *opt __used, static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { + int ret = 0; + if (str) - parse_line_range_desc(str, ¶ms.line_range); + ret = parse_line_range_desc(str, ¶ms.line_range); INIT_LIST_HEAD(¶ms.line_range.line_list); params.show_lines = true; - return 0; + + return ret; } #endif @@ -178,6 +185,8 @@ static const struct option options[] = { int cmd_probe(int argc, const char **argv, const char *prefix __used) { + int ret; + argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (argc > 0) { @@ -185,7 +194,11 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) pr_warning(" Error: '-' is not supported.\n"); usage_with_options(probe_usage, options); } - parse_probe_event_argv(argc, argv); + ret = parse_probe_event_argv(argc, argv); + if (ret < 0) { + pr_err(" Error: Parse Error. (%d)\n", ret); + return ret; + } } if ((!params.nevents && !params.dellist && !params.list_events && @@ -197,16 +210,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (params.list_events) { if (params.nevents != 0 || params.dellist) { - pr_warning(" Error: Don't use --list with" - " --add/--del.\n"); + pr_err(" Error: Don't use --list with --add/--del.\n"); usage_with_options(probe_usage, options); } if (params.show_lines) { - pr_warning(" Error: Don't use --list with --line.\n"); + pr_err(" Error: Don't use --list with --line.\n"); usage_with_options(probe_usage, options); } - show_perf_probe_events(); - return 0; + ret = show_perf_probe_events(); + if (ret < 0) + pr_err(" Error: Failed to show event list. (%d)\n", + ret); + return ret; } #ifdef DWARF_SUPPORT @@ -217,19 +232,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) usage_with_options(probe_usage, options); } - show_line_range(¶ms.line_range); - return 0; + ret = show_line_range(¶ms.line_range); + if (ret < 0) + pr_err(" Error: Failed to show lines. (%d)\n", ret); + return ret; } #endif if (params.dellist) { - del_perf_probe_events(params.dellist); + ret = del_perf_probe_events(params.dellist); strlist__delete(params.dellist); - if (params.nevents == 0) - return 0; + if (ret < 0) { + pr_err(" Error: Failed to delete events. (%d)\n", ret); + return ret; + } } - add_perf_probe_events(params.events, params.nevents, params.force_add); + if (params.nevents) { + ret = add_perf_probe_events(params.events, params.nevents, + params.force_add); + if (ret < 0) { + pr_err(" Error: Failed to add events. (%d)\n", ret); + return ret; + } + } return 0; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7893b32..bd68f7b 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -53,7 +53,7 @@ bool probe_event_dry_run; /* Dry run flag */ -#define semantic_error(msg ...) die("Semantic error :" msg) +#define semantic_error(msg ...) pr_err("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ static int e_snprintf(char *str, size_t size, const char *format, ...) @@ -76,19 +76,30 @@ static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; /* Initialize symbol maps and path of vmlinux */ -static void init_vmlinux(void) +static int init_vmlinux(void) { + int ret; + symbol_conf.sort_by_name = true; if (symbol_conf.vmlinux_name == NULL) symbol_conf.try_vmlinux_path = true; else pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); - if (symbol__init() < 0) - die("Failed to init symbol map."); + ret = symbol__init(); + if (ret < 0) { + pr_debug("Failed to init symbol map.\n"); + goto out; + } map_groups__init(&kmap_groups); - if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0) - die("Failed to create kernel maps."); + ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); + if (ret < 0) + pr_debug("Failed to create kernel maps.\n"); + +out: + if (ret < 0) + pr_warning("Failed to init vmlinux path.\n"); + return ret; } #ifdef DWARF_SUPPORT @@ -102,24 +113,32 @@ static int open_vmlinux(void) return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } -static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, - struct perf_probe_point *pp) +/* Convert trace point to probe point with debuginfo */ +static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) { struct symbol *sym; - int fd, ret = 0; + int fd, ret = -ENOENT; sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], tp->symbol, NULL); if (sym) { fd = open_vmlinux(); - ret = find_perf_probe_point(fd, sym->start + tp->offset, pp); - close(fd); + if (fd >= 0) { + ret = find_perf_probe_point(fd, + sym->start + tp->offset, pp); + close(fd); + } } if (ret <= 0) { + pr_debug("Failed to find corresponding probes from " + "debuginfo. Use kprobe event information.\n"); pp->function = xstrdup(tp->symbol); pp->offset = tp->offset; } pp->retprobe = tp->retprobe; + + return 0; } /* Try to find perf_probe_event with debuginfo */ @@ -131,9 +150,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, fd = open_vmlinux(); if (fd < 0) { - if (need_dwarf) - die("Could not open debuginfo file."); - + if (need_dwarf) { + pr_warning("Failed to open debuginfo file.\n"); + return fd; + } pr_debug("Could not open vmlinux. Try to use symbols.\n"); return 0; } @@ -142,30 +162,32 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, ntevs = find_kprobe_trace_events(fd, pev, tevs); close(fd); - if (ntevs > 0) /* Succeeded to find trace events */ + if (ntevs > 0) { /* Succeeded to find trace events */ + pr_debug("find %d kprobe_trace_events.\n", ntevs); return ntevs; + } - if (ntevs == 0) /* No error but failed to find probe point. */ - die("Probe point '%s' not found. - probe not added.", - synthesize_perf_probe_point(&pev->point)); - - /* Error path */ + if (ntevs == 0) { /* No error but failed to find probe point. */ + pr_warning("Probe point '%s' not found.\n", + synthesize_perf_probe_point(&pev->point)); + return -ENOENT; + } + /* Error path : ntevs < 0 */ if (need_dwarf) { if (ntevs == -EBADF) pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Failed to analyze debuginfo."); + return ntevs; } pr_debug("An error occurred in debuginfo analysis." " Try to use symbols.\n"); return 0; - } #define LINEBUF_SIZE 256 #define NR_ADDITIONAL_LINES 2 -static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) { char buf[LINEBUF_SIZE]; const char *color = PERF_COLOR_BLUE; @@ -190,19 +212,22 @@ static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) color_fprintf(stdout, color, "%s", buf); } } - return; + + return 0; error: if (feof(fp)) - die("Source file is shorter than expected."); + pr_warning("Source file is shorter than expected.\n"); else - die("File read error: %s", strerror(errno)); + pr_warning("File read error: %s\n", strerror(errno)); + + return -1; } /* * Show line-range always requires debuginfo to find source file and * line number. */ -void show_line_range(struct line_range *lr) +int show_line_range(struct line_range *lr) { unsigned int l = 1; struct line_node *ln; @@ -210,14 +235,25 @@ void show_line_range(struct line_range *lr) int fd, ret; /* Search a line range */ - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; + fd = open_vmlinux(); - if (fd < 0) - die("Could not open debuginfo file."); + if (fd < 0) { + pr_warning("Failed to open debuginfo file.\n"); + return fd; + } + ret = find_line_range(fd, lr); - if (ret <= 0) - die("Source line is not found.\n"); close(fd); + if (ret == 0) { + pr_warning("Specified source line is not found.\n"); + return -ENOENT; + } else if (ret < 0) { + pr_warning("Debuginfo analysis failed. (%d)\n", ret); + return ret; + } setup_pager(); @@ -228,52 +264,68 @@ void show_line_range(struct line_range *lr) fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); fp = fopen(lr->path, "r"); - if (fp == NULL) - die("Failed to open %s: %s", lr->path, strerror(errno)); + if (fp == NULL) { + pr_warning("Failed to open %s: %s\n", lr->path, + strerror(errno)); + return -errno; + } /* Skip to starting line number */ - while (l < lr->start) - show_one_line(fp, l++, true, false); + while (l < lr->start && ret >= 0) + ret = show_one_line(fp, l++, true, false); + if (ret < 0) + goto end; list_for_each_entry(ln, &lr->line_list, list) { - while (ln->line > l) - show_one_line(fp, (l++) - lr->offset, false, false); - show_one_line(fp, (l++) - lr->offset, false, true); + while (ln->line > l && ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, + false, false); + if (ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, + false, true); + if (ret < 0) + goto end; } if (lr->end == INT_MAX) lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp)) - show_one_line(fp, (l++) - lr->offset, false, false); - + while (l < lr->end && !feof(fp) && ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, false, false); +end: fclose(fp); + return ret; } #else /* !DWARF_SUPPORT */ -static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, +static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct perf_probe_point *pp) { pp->function = xstrdup(tp->symbol); pp->offset = tp->offset; pp->retprobe = tp->retprobe; + + return 0; } static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs __unused) { - if (perf_probe_event_need_dwarf(pev)) - die("Debuginfo-analysis is not supported"); + if (perf_probe_event_need_dwarf(pev)) { + pr_warning("Debuginfo-analysis is not supported.\n"); + return -ENOSYS; + } return 0; } -void show_line_range(struct line_range *lr __unused) +int show_line_range(struct line_range *lr __unused) { - die("Debuginfo-analysis is not supported"); + pr_warning("Debuginfo-analysis is not supported.\n"); + return -ENOSYS; } #endif -void parse_line_range_desc(const char *arg, struct line_range *lr) +int parse_line_range_desc(const char *arg, struct line_range *lr) { const char *ptr; char *tmp; @@ -293,12 +345,16 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) else lr->end = 0; pr_debug("Line range is %u to %u\n", lr->start, lr->end); - if (lr->end && lr->start > lr->end) + if (lr->end && lr->start > lr->end) { semantic_error("Start line must be smaller" - " than end line."); - if (*tmp != '\0') - semantic_error("Tailing with invalid character '%d'.", + " than end line.\n"); + return -EINVAL; + } + if (*tmp != '\0') { + semantic_error("Tailing with invalid character '%d'.\n", *tmp); + return -EINVAL; + } tmp = xstrndup(arg, (ptr - arg)); } else tmp = xstrdup(arg); @@ -307,6 +363,8 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) lr->file = tmp; else lr->function = tmp; + + return 0; } /* Check the name is good for event/group */ @@ -322,7 +380,7 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) +static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; @@ -339,12 +397,15 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) if (ptr && *ptr == '=') { /* Event name */ *ptr = '\0'; tmp = ptr + 1; - ptr = strchr(arg, ':'); - if (ptr) /* Group name is not supported yet. */ - semantic_error("Group name is not supported yet."); - if (!check_event_name(arg)) + if (strchr(arg, ':')) { + semantic_error("Group name is not supported yet.\n"); + return -ENOTSUP; + } + if (!check_event_name(arg)) { semantic_error("%s is bad for event name -it must " - "follow C symbol-naming rule.", arg); + "follow C symbol-naming rule.\n", arg); + return -EINVAL; + } pev->event = xstrdup(arg); pev->group = NULL; arg = tmp; @@ -378,64 +439,89 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) switch (c) { case ':': /* Line number */ pp->line = strtoul(arg, &tmp, 0); - if (*tmp != '\0') + if (*tmp != '\0') { semantic_error("There is non-digit char" - " in line number."); + " in line number.\n"); + return -EINVAL; + } break; case '+': /* Byte offset from a symbol */ pp->offset = strtoul(arg, &tmp, 0); - if (*tmp != '\0') + if (*tmp != '\0') { semantic_error("There is non-digit character" - " in offset."); + " in offset.\n"); + return -EINVAL; + } break; case '@': /* File name */ - if (pp->file) - semantic_error("SRC@SRC is not allowed."); + if (pp->file) { + semantic_error("SRC@SRC is not allowed.\n"); + return -EINVAL; + } pp->file = xstrdup(arg); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { pp->retprobe = 1; - } else /* Others not supported yet */ - semantic_error("%%%s is not supported.", arg); + } else { /* Others not supported yet */ + semantic_error("%%%s is not supported.\n", arg); + return -ENOTSUP; + } break; - default: - DIE_IF("Program has a bug."); + default: /* Buggy case */ + pr_err("This program has a bug at %s:%d.\n", + __FILE__, __LINE__); + return -ENOTSUP; break; } } /* Exclusion check */ - if (pp->lazy_line && pp->line) + if (pp->lazy_line && pp->line) { semantic_error("Lazy pattern can't be used with line number."); + return -EINVAL; + } - if (pp->lazy_line && pp->offset) + if (pp->lazy_line && pp->offset) { semantic_error("Lazy pattern can't be used with offset."); + return -EINVAL; + } - if (pp->line && pp->offset) + if (pp->line && pp->offset) { semantic_error("Offset can't be used with line number."); + return -EINVAL; + } - if (!pp->line && !pp->lazy_line && pp->file && !pp->function) + if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { semantic_error("File always requires line number or " "lazy pattern."); + return -EINVAL; + } - if (pp->offset && !pp->function) + if (pp->offset && !pp->function) { semantic_error("Offset requires an entry function."); + return -EINVAL; + } - if (pp->retprobe && !pp->function) + if (pp->retprobe && !pp->function) { semantic_error("Return probe requires an entry function."); + return -EINVAL; + } - if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) + if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); + return -EINVAL; + } pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); + return 0; } /* Parse perf-probe event argument */ -static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) +static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) { char *tmp; struct perf_probe_arg_field **fieldp; @@ -461,7 +547,7 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) /* A variable, register, symbol or special value */ arg->var = xstrdup(str); pr_debug("%s\n", arg->var); - return; + return 0; } /* Structure fields */ @@ -477,8 +563,10 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) } else if (tmp[1] == '>') { str = tmp + 2; (*fieldp)->ref = true; - } else - semantic_error("Argument parse error: %s", str); + } else { + semantic_error("Argument parse error: %s\n", str); + return -EINVAL; + } tmp = strpbrk(str, "-."); if (tmp) { @@ -493,34 +581,47 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) /* If no name is specified, set the last field name */ if (!arg->name) arg->name = xstrdup((*fieldp)->name); + + return 0; } /* Parse perf-probe event command */ -void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) +int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { char **argv; - int argc, i; + int argc, i, ret = 0; argv = argv_split(cmd, &argc); - if (!argv) - die("argv_split failed."); - if (argc > MAX_PROBE_ARGS + 1) - semantic_error("Too many arguments"); - + if (!argv) { + pr_debug("Failed to split arguments.\n"); + return -ENOMEM; + } + if (argc - 1 > MAX_PROBE_ARGS) { + semantic_error("Too many probe arguments (%d).\n", argc - 1); + ret = -ERANGE; + goto out; + } /* Parse probe point */ - parse_perf_probe_point(argv[0], pev); + ret = parse_perf_probe_point(argv[0], pev); + if (ret < 0) + goto out; /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); - for (i = 0; i < pev->nargs; i++) { - parse_perf_probe_arg(argv[i + 1], &pev->args[i]); - if (is_c_varname(pev->args[i].var) && pev->point.retprobe) + for (i = 0; i < pev->nargs && ret >= 0; i++) { + ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); + if (ret >= 0 && + is_c_varname(pev->args[i].var) && pev->point.retprobe) { semantic_error("You can't specify local variable for" - " kretprobe"); + " kretprobe.\n"); + ret = -EINVAL; + } } - +out: argv_free(argv); + + return ret; } /* Return true if this perf_probe_event requires debuginfo */ @@ -539,7 +640,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) } /* Parse kprobe_events event into struct probe_point */ -void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) +int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) { struct kprobe_trace_point *tp = &tev->point; char pr; @@ -549,17 +650,25 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) pr_debug("Parsing kprobe_events: %s\n", cmd); argv = argv_split(cmd, &argc); - if (!argv) - die("argv_split failed."); - if (argc < 2) - semantic_error("Too less arguments."); + if (!argv) { + pr_debug("Failed to split arguments.\n"); + return -ENOMEM; + } + if (argc < 2) { + semantic_error("Too few probe arguments.\n"); + ret = -ERANGE; + goto out; + } /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", &pr, (float *)(void *)&tev->group, (float *)(void *)&tev->event); - if (ret != 3) - semantic_error("Failed to parse event name: %s", argv[0]); + if (ret != 3) { + semantic_error("Failed to parse event name: %s\n", argv[0]); + ret = -EINVAL; + goto out; + } pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); tp->retprobe = (pr == 'r'); @@ -582,8 +691,10 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) /* TODO: parse regs and offset */ tev->args[i].value = xstrdup(p); } - + ret = 0; +out: argv_free(argv); + return ret; } /* Compose only probe arg */ @@ -622,7 +733,9 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) return tmp - buf; error: - die("Failed to synthesize perf probe argument: %s", strerror(-ret)); + pr_debug("Failed to synthesize perf probe argument: %s", + strerror(-ret)); + return ret; } /* Compose only probe point (not argument) */ @@ -666,7 +779,10 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) return buf; error: - die("Failed to synthesize perf probe point: %s", strerror(-ret)); + pr_debug("Failed to synthesize perf probe point: %s", + strerror(-ret)); + free(buf); + return NULL; } #if 0 @@ -796,29 +912,37 @@ error: return NULL; } -void convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev) +int convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev) { char buf[64]; - int i; + int i, ret; /* Convert event/group name */ pev->event = xstrdup(tev->event); pev->group = xstrdup(tev->group); /* Convert trace_point to probe_point */ - convert_to_perf_probe_point(&tev->point, &pev->point); + ret = convert_to_perf_probe_point(&tev->point, &pev->point); + if (ret < 0) + return ret; /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); - for (i = 0; i < tev->nargs; i++) + for (i = 0; i < tev->nargs && ret >= 0; i++) if (tev->args[i].name) pev->args[i].name = xstrdup(tev->args[i].name); else { - synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); + ret = synthesize_kprobe_trace_arg(&tev->args[i], + buf, 64); pev->args[i].name = xstrdup(buf); } + + if (ret < 0) + clear_perf_probe_event(pev); + + return ret; } void clear_perf_probe_event(struct perf_probe_event *pev) @@ -894,21 +1018,20 @@ static int open_kprobe_events(bool readwrite) int ret; ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); - if (ret < 0) - die("Failed to make kprobe_events path."); - - if (readwrite && !probe_event_dry_run) - ret = open(buf, O_RDWR, O_APPEND); - else - ret = open(buf, O_RDONLY, 0); + if (ret >= 0) { + if (readwrite && !probe_event_dry_run) + ret = open(buf, O_RDWR, O_APPEND); + else + ret = open(buf, O_RDONLY, 0); + } if (ret < 0) { if (errno == ENOENT) - die("kprobe_events file does not exist -" - " please rebuild with CONFIG_KPROBE_EVENT."); + pr_warning("kprobe_events file does not exist - please" + " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); else - die("Could not open kprobe_events file: %s", - strerror(errno)); + pr_warning("Failed to open kprobe_events file: %s\n", + strerror(errno)); } return ret; } @@ -934,8 +1057,11 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd) if (p[idx] == '\n') p[idx] = '\0'; ret = strlist__add(sl, buf); - if (ret < 0) - die("strlist__add failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("strlist__add failed: %s\n", strerror(-ret)); + strlist__delete(sl); + return NULL; + } } fclose(fp); @@ -943,7 +1069,7 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd) } /* Show an event */ -static void show_perf_probe_event(struct perf_probe_event *pev) +static int show_perf_probe_event(struct perf_probe_event *pev) { int i, ret; char buf[128]; @@ -951,52 +1077,71 @@ static void show_perf_probe_event(struct perf_probe_event *pev) /* Synthesize only event probe point */ place = synthesize_perf_probe_point(&pev->point); + if (!place) + return -EINVAL; ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) - die("Failed to copy event: %s", strerror(-ret)); + return ret; + printf(" %-20s (on %s", buf, place); if (pev->nargs > 0) { printf(" with"); for (i = 0; i < pev->nargs; i++) { - synthesize_perf_probe_arg(&pev->args[i], buf, 128); + ret = synthesize_perf_probe_arg(&pev->args[i], + buf, 128); + if (ret < 0) + break; printf(" %s", buf); } } printf(")\n"); free(place); + return ret; } /* List up current perf-probe events */ -void show_perf_probe_events(void) +int show_perf_probe_events(void) { - int fd; + int fd, ret; struct kprobe_trace_event tev; struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; setup_pager(); - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); fd = open_kprobe_events(false); + if (fd < 0) + return fd; + rawlist = get_kprobe_trace_command_rawlist(fd); close(fd); + if (!rawlist) + return -ENOENT; strlist__for_each(ent, rawlist) { - parse_kprobe_trace_command(ent->s, &tev); - convert_to_perf_probe_event(&tev, &pev); - /* Show an event */ - show_perf_probe_event(&pev); + ret = parse_kprobe_trace_command(ent->s, &tev); + if (ret >= 0) { + ret = convert_to_perf_probe_event(&tev, &pev); + if (ret >= 0) + ret = show_perf_probe_event(&pev); + } clear_perf_probe_event(&pev); clear_kprobe_trace_event(&tev); + if (ret < 0) + break; } - strlist__delete(rawlist); + + return ret; } /* Get current perf-probe event names */ @@ -1006,88 +1151,118 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) struct strlist *sl, *rawlist; struct str_node *ent; struct kprobe_trace_event tev; + int ret = 0; memset(&tev, 0, sizeof(tev)); rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_kprobe_trace_command(ent->s, &tev); + ret = parse_kprobe_trace_command(ent->s, &tev); + if (ret < 0) + break; if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", tev.group, - tev.event) < 0) - die("Failed to copy group:event name."); - strlist__add(sl, buf); + ret = e_snprintf(buf, 128, "%s:%s", tev.group, + tev.event); + if (ret >= 0) + ret = strlist__add(sl, buf); } else - strlist__add(sl, tev.event); + ret = strlist__add(sl, tev.event); clear_kprobe_trace_event(&tev); + if (ret < 0) + break; } - strlist__delete(rawlist); + if (ret < 0) { + strlist__delete(sl); + return NULL; + } return sl; } -static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) +static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { int ret; char *buf = synthesize_kprobe_trace_command(tev); + if (!buf) { + pr_debug("Failed to synthesize kprobe trace event.\n"); + return -EINVAL; + } + pr_debug("Writing event: %s\n", buf); if (!probe_event_dry_run) { ret = write(fd, buf, strlen(buf)); if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + pr_warning("Failed to write event: %s\n", + strerror(errno)); } free(buf); + return ret; } -static void get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist, bool allow_suffix) +static int get_new_event_name(char *buf, size_t len, const char *base, + struct strlist *namelist, bool allow_suffix) { int i, ret; /* Try no suffix */ ret = e_snprintf(buf, len, "%s", base); - if (ret < 0) - die("snprintf() failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("snprintf() failed: %s\n", strerror(-ret)); + return ret; + } if (!strlist__has_entry(namelist, buf)) - return; + return 0; if (!allow_suffix) { pr_warning("Error: event \"%s\" already exists. " "(Use -f to force duplicates.)\n", base); - die("Can't add new event."); + return -EEXIST; } /* Try to add suffix */ for (i = 1; i < MAX_EVENT_INDEX; i++) { ret = e_snprintf(buf, len, "%s_%d", base, i); - if (ret < 0) - die("snprintf() failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("snprintf() failed: %s\n", strerror(-ret)); + return ret; + } if (!strlist__has_entry(namelist, buf)) break; } - if (i == MAX_EVENT_INDEX) - die("Too many events are on the same function."); + if (i == MAX_EVENT_INDEX) { + pr_warning("Too many events are on the same function.\n"); + ret = -ERANGE; + } + + return ret; } -static void __add_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event *tevs, - int ntevs, bool allow_suffix) +static int __add_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event *tevs, + int ntevs, bool allow_suffix) { - int i, fd; + int i, fd, ret; struct kprobe_trace_event *tev = NULL; char buf[64]; const char *event, *group; struct strlist *namelist; fd = open_kprobe_events(true); + if (fd < 0) + return fd; /* Get current event names */ namelist = get_kprobe_trace_event_names(fd, false); + if (!namelist) { + pr_debug("Failed to get current event list.\n"); + return -EIO; + } + ret = 0; printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); - for (i = 0; i < ntevs; i++) { + for (i = 0; i < ntevs && ret >= 0; i++) { tev = &tevs[i]; if (pev->event) event = pev->event; @@ -1102,12 +1277,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, group = PERFPROBE_GROUP; /* Get an unused new event name */ - get_new_event_name(buf, 64, event, namelist, allow_suffix); + ret = get_new_event_name(buf, 64, event, + namelist, allow_suffix); + if (ret < 0) + break; event = buf; tev->event = xstrdup(event); tev->group = xstrdup(group); - write_kprobe_trace_event(fd, tev); + ret = write_kprobe_trace_event(fd, tev); + if (ret < 0) + break; /* Add added event name to namelist */ strlist__add(namelist, event); @@ -1129,12 +1309,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, */ allow_suffix = true; } - /* 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", tev->group, tev->event); + + if (ret >= 0) { + /* 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 -aR sleep 1\n\n", tev->group, + tev->event); + } strlist__delete(namelist); close(fd); + return ret; } static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, @@ -1146,7 +1331,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, /* Convert perf_probe_event with debuginfo */ ntevs = try_to_find_kprobe_trace_events(pev, tevs); - if (ntevs > 0) + if (ntevs != 0) return ntevs; /* Allocate trace event buffer */ @@ -1172,10 +1357,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, /* Currently just checking function name from symbol map */ sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], tev->point.symbol, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - tev->point.symbol); - + if (!sym) { + pr_warning("Kernel symbol \'%s\' not found.\n", + tev->point.symbol); + return -ENOENT; + } return ntevs; } @@ -1185,93 +1371,128 @@ struct __event_package { int ntevs; }; -void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add) +int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add) { - int i; + int i, j, ret; struct __event_package *pkgs; pkgs = xzalloc(sizeof(struct __event_package) * npevs); /* Init vmlinux path */ - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; /* Loop 1: convert all events */ for (i = 0; i < npevs; i++) { pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ - pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, - &pkgs[i].tevs); + ret = convert_to_kprobe_trace_events(pkgs[i].pev, + &pkgs[i].tevs); + if (ret < 0) + goto end; + pkgs[i].ntevs = ret; } /* Loop 2: add all events */ + for (i = 0; i < npevs && ret >= 0; i++) + ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + pkgs[i].ntevs, force_add); +end: + /* Loop 3: cleanup trace events */ for (i = 0; i < npevs; i++) - __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, - pkgs[i].ntevs, force_add); - /* TODO: cleanup all trace events? */ + for (j = 0; j < pkgs[i].ntevs; j++) + clear_kprobe_trace_event(&pkgs[i].tevs[j]); + + return ret; } -static void __del_trace_kprobe_event(int fd, struct str_node *ent) +static int __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; int ret; /* Convert from perf-probe event to trace-kprobe event */ - if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) - die("Failed to copy event."); + ret = e_snprintf(buf, 128, "-:%s", ent->s); + if (ret < 0) + goto error; + p = strchr(buf + 2, ':'); - if (!p) - die("Internal error: %s should have ':' but not.", ent->s); + if (!p) { + pr_debug("Internal error: %s should have ':' but not.\n", + ent->s); + ret = -ENOTSUP; + goto error; + } *p = '/'; pr_debug("Writing event: %s\n", buf); ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + if (ret < 0) + goto error; + printf("Remove event: %s\n", ent->s); + return 0; +error: + pr_warning("Failed to delete event: %s\n", strerror(-ret)); + return ret; } -static void del_trace_kprobe_event(int fd, const char *group, - const char *event, struct strlist *namelist) +static int del_trace_kprobe_event(int fd, const char *group, + const char *event, struct strlist *namelist) { char buf[128]; struct str_node *ent, *n; - int found = 0; + int found = 0, ret = 0; - if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) - die("Failed to copy event."); + ret = e_snprintf(buf, 128, "%s:%s", group, event); + if (ret < 0) { + pr_err("Failed to copy event."); + return ret; + } if (strpbrk(buf, "*?")) { /* Glob-exp */ strlist__for_each_safe(ent, n, namelist) if (strglobmatch(ent->s, buf)) { found++; - __del_trace_kprobe_event(fd, ent); + ret = __del_trace_kprobe_event(fd, ent); + if (ret < 0) + break; strlist__remove(namelist, ent); } } else { ent = strlist__find(namelist, buf); if (ent) { found++; - __del_trace_kprobe_event(fd, ent); - strlist__remove(namelist, ent); + ret = __del_trace_kprobe_event(fd, ent); + if (ret >= 0) + strlist__remove(namelist, ent); } } - if (found == 0) - pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); + if (found == 0 && ret >= 0) + pr_info("Info: Event \"%s\" does not exist.\n", buf); + + return ret; } -void del_perf_probe_events(struct strlist *dellist) +int del_perf_probe_events(struct strlist *dellist) { - int fd; + int fd, ret = 0; const char *group, *event; char *p, *str; struct str_node *ent; struct strlist *namelist; fd = open_kprobe_events(true); + if (fd < 0) + return fd; + /* Get current event names */ namelist = get_kprobe_trace_event_names(fd, true); + if (namelist == NULL) + return -EINVAL; strlist__for_each(ent, dellist) { str = xstrdup(ent->s); @@ -1286,10 +1507,14 @@ void del_perf_probe_events(struct strlist *dellist) event = str; } pr_debug("Group: %s, Event: %s\n", group, event); - del_trace_kprobe_event(fd, group, event, namelist); + ret = del_trace_kprobe_event(fd, group, event, namelist); free(str); + if (ret < 0) + break; } strlist__delete(namelist); close(fd); + + return ret; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ff2f26b..ab54929 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -89,10 +89,10 @@ struct line_range { }; /* Command string to events */ -extern void parse_perf_probe_command(const char *cmd, - struct perf_probe_event *pev); -extern void parse_kprobe_trace_command(const char *cmd, - struct kprobe_trace_event *tev); +extern int parse_perf_probe_command(const char *cmd, + struct perf_probe_event *pev); +extern int parse_kprobe_trace_command(const char *cmd, + struct kprobe_trace_event *tev); /* Events to command string */ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); @@ -104,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); /* Convert from kprobe_trace_event to perf_probe_event */ -extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev); +extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev); /* Release event contents */ extern void clear_perf_probe_event(struct perf_probe_event *pev); extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); /* Command string to line-range */ -extern void parse_line_range_desc(const char *cmd, struct line_range *lr); +extern int parse_line_range_desc(const char *cmd, struct line_range *lr); -extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, - bool force_add); -extern void del_perf_probe_events(struct strlist *dellist); -extern void show_perf_probe_events(void); -extern void show_line_range(struct line_range *lr); +extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, + bool force_add); +extern int del_perf_probe_events(struct strlist *dellist); +extern int show_perf_probe_events(void); +extern int show_line_range(struct line_range *lr); /* Maximum index number of event-name postfix */ -- cgit v0.10.2 From e334016f1d7250a6523b3a44ccecfe23af6e2f57 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:49 -0400 Subject: perf probe: Remove xzalloc() from util/probe-{event, finder}.c Remove all xzalloc() calls from util/probe-{event,finder}.c since it may cause 'sudden death' in utility functions and it makes reusing it from other code difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171749.3790.33303.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bd68f7b..aacbf73 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -556,7 +556,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) fieldp = &arg->field; do { - *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); + *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); + if (*fieldp == NULL) + return -ENOMEM; if (*tmp == '.') { str = tmp + 1; (*fieldp)->ref = false; @@ -608,7 +610,11 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; - pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + if (pev->args == NULL) { + ret = -ENOMEM; + goto out; + } for (i = 0; i < pev->nargs && ret >= 0; i++) { ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); if (ret >= 0 && @@ -680,7 +686,11 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) tp->offset = 0; tev->nargs = argc - 2; - tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + if (tev->args == NULL) { + ret = -ENOMEM; + goto out; + } for (i = 0; i < tev->nargs; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ @@ -745,7 +755,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) char offs[32] = "", line[32] = "", file[32] = ""; int ret, len; - buf = xzalloc(MAX_CMDLEN); + buf = zalloc(MAX_CMDLEN); + if (buf == NULL) { + ret = -ENOMEM; + goto error; + } if (pp->offset) { ret = e_snprintf(offs, 32, "+%lu", pp->offset); if (ret <= 0) @@ -781,7 +795,8 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) error: pr_debug("Failed to synthesize perf probe point: %s", strerror(-ret)); - free(buf); + if (buf) + free(buf); return NULL; } @@ -890,7 +905,10 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) char *buf; int i, len, ret; - buf = xzalloc(MAX_CMDLEN); + buf = zalloc(MAX_CMDLEN); + if (buf == NULL) + return NULL; + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", tp->retprobe ? 'r' : 'p', tev->group, tev->event, @@ -929,7 +947,9 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; - pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + if (pev->args == NULL) + return -ENOMEM; for (i = 0; i < tev->nargs && ret >= 0; i++) if (tev->args[i].name) pev->args[i].name = xstrdup(tev->args[i].name); @@ -1326,25 +1346,31 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs) { struct symbol *sym; - int ntevs = 0, i; + int ret = 0, i; struct kprobe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ntevs = try_to_find_kprobe_trace_events(pev, tevs); - if (ntevs != 0) - return ntevs; + ret = try_to_find_kprobe_trace_events(pev, tevs); + if (ret != 0) + return ret; /* Allocate trace event buffer */ - ntevs = 1; - tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); + tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); + if (tev == NULL) + return -ENOMEM; /* Copy parameters */ tev->point.symbol = xstrdup(pev->point.function); tev->point.offset = pev->point.offset; tev->nargs = pev->nargs; if (tev->nargs) { - tev->args = xzalloc(sizeof(struct kprobe_trace_arg) - * tev->nargs); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + if (tev->args == NULL) { + free(tev); + *tevs = NULL; + return -ENOMEM; + } for (i = 0; i < tev->nargs; i++) { if (pev->args[i].name) tev->args[i].name = xstrdup(pev->args[i].name); @@ -1360,9 +1386,14 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", tev->point.symbol); + clear_kprobe_trace_event(tev); + free(tev); + *tevs = NULL; return -ENOENT; - } - return ntevs; + } else + ret = 1; + + return ret; } struct __event_package { @@ -1377,7 +1408,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int i, j, ret; struct __event_package *pkgs; - pkgs = xzalloc(sizeof(struct __event_package) * npevs); + pkgs = zalloc(sizeof(struct __event_package) * npevs); + if (pkgs == NULL) + return -ENOMEM; /* Init vmlinux path */ ret = init_vmlinux(); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 54daa91..ce1ac82 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -111,7 +111,7 @@ static int strtailcmp(const char *s1, const char *s2) /* Line number list operations */ /* Add a line to line number list */ -static void line_list__add_line(struct list_head *head, unsigned int line) +static int line_list__add_line(struct list_head *head, unsigned int line) { struct line_node *ln; struct list_head *p; @@ -122,16 +122,19 @@ static void line_list__add_line(struct list_head *head, unsigned int line) p = &ln->list; goto found; } else if (ln->line == line) /* Already exist */ - return ; + return 1; } /* List is empty, or the smallest entry */ p = head; found: pr_debug("line list: add a line %u\n", line); - ln = xzalloc(sizeof(struct line_node)); + ln = zalloc(sizeof(struct line_node)); + if (ln == NULL) + return -ENOMEM; ln->line = line; INIT_LIST_HEAD(&ln->list); list_add(&ln->list, p); + return 0; } /* Check if the line in line number list */ @@ -423,7 +426,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) tvar->value = xstrdup(regs); if (ref) { - tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (tvar->ref == NULL) + return -ENOMEM; tvar->ref->offset = (long)offs; } return 0; @@ -500,7 +505,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, return -EINVAL; } - ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (ref == NULL) + return -ENOMEM; if (*ref_ptr) (*ref_ptr)->next = ref; else @@ -680,7 +687,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Find each argument */ tev->nargs = pf->pev->nargs; - tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + if (tev->args == NULL) + return -ENOMEM; for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; @@ -938,7 +947,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, Dwarf *dbg; int ret = 0; - pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + if (pf.tevs == NULL) + return -ENOMEM; *tevs = pf.tevs; pf.ntevs = 0; -- cgit v0.10.2 From 02b95dadc8a1d2c302513e5fa24c492380d26e93 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:56 -0400 Subject: perf probe: Remove xstrdup()/xstrndup() from util/probe-{event, finder}.c Remove all xstr*dup() calls from util/probe-{event,finder}.c since it may cause 'sudden death' in utility functions and it makes reusing it from other code difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171756.3790.89607.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aacbf73..ca108b2 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -133,7 +133,9 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, if (ret <= 0) { pr_debug("Failed to find corresponding probes from " "debuginfo. Use kprobe event information.\n"); - pp->function = xstrdup(tp->symbol); + pp->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; pp->offset = tp->offset; } pp->retprobe = tp->retprobe; @@ -300,7 +302,9 @@ end: static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct perf_probe_point *pp) { - pp->function = xstrdup(tp->symbol); + pp->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; pp->offset = tp->offset; pp->retprobe = tp->retprobe; @@ -355,9 +359,12 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) *tmp); return -EINVAL; } - tmp = xstrndup(arg, (ptr - arg)); + tmp = strndup(arg, (ptr - arg)); } else - tmp = xstrdup(arg); + tmp = strdup(arg); + + if (tmp == NULL) + return -ENOMEM; if (strchr(tmp, '.')) lr->file = tmp; @@ -406,7 +413,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) "follow C symbol-naming rule.\n", arg); return -EINVAL; } - pev->event = xstrdup(arg); + pev->event = strdup(arg); + if (pev->event == NULL) + return -ENOMEM; pev->group = NULL; arg = tmp; } @@ -417,18 +426,24 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) *ptr++ = '\0'; } + tmp = strdup(arg); + if (tmp == NULL) + return -ENOMEM; + /* Check arg is function or file and copy it */ - if (strchr(arg, '.')) /* File */ - pp->file = xstrdup(arg); + if (strchr(tmp, '.')) /* File */ + pp->file = tmp; else /* Function */ - pp->function = xstrdup(arg); + pp->function = tmp; /* Parse other options */ while (ptr) { arg = ptr; c = nc; if (c == ';') { /* Lazy pattern must be the last part */ - pp->lazy_line = xstrdup(arg); + pp->lazy_line = strdup(arg); + if (pp->lazy_line == NULL) + return -ENOMEM; break; } ptr = strpbrk(arg, ";:+@%"); @@ -458,7 +473,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) semantic_error("SRC@SRC is not allowed.\n"); return -EINVAL; } - pp->file = xstrdup(arg); + pp->file = strdup(arg); + if (pp->file == NULL) + return -ENOMEM; break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { @@ -530,7 +547,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strchr(str, '='); if (tmp) { - arg->name = xstrndup(str, tmp - str); + arg->name = strndup(str, tmp - str); + if (arg->name == NULL) + return -ENOMEM; pr_debug("name:%s ", arg->name); str = tmp + 1; } @@ -538,20 +557,26 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strchr(str, ':'); if (tmp) { /* Type setting */ *tmp = '\0'; - arg->type = xstrdup(tmp + 1); + arg->type = strdup(tmp + 1); + if (arg->type == NULL) + return -ENOMEM; pr_debug("type:%s ", arg->type); } tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ - arg->var = xstrdup(str); + arg->var = strdup(str); + if (arg->var == NULL) + return -ENOMEM; pr_debug("%s\n", arg->var); return 0; } /* Structure fields */ - arg->var = xstrndup(str, tmp - str); + arg->var = strndup(str, tmp - str); + if (arg->var == NULL) + return -ENOMEM; pr_debug("%s, ", arg->var); fieldp = &arg->field; @@ -572,18 +597,24 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strpbrk(str, "-."); if (tmp) { - (*fieldp)->name = xstrndup(str, tmp - str); + (*fieldp)->name = strndup(str, tmp - str); + if ((*fieldp)->name == NULL) + return -ENOMEM; pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); fieldp = &(*fieldp)->next; } } while (tmp); - (*fieldp)->name = xstrdup(str); + (*fieldp)->name = strdup(str); + if ((*fieldp)->name == NULL) + return -ENOMEM; pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); /* If no name is specified, set the last field name */ - if (!arg->name) - arg->name = xstrdup((*fieldp)->name); - + if (!arg->name) { + arg->name = strdup((*fieldp)->name); + if (arg->name == NULL) + return -ENOMEM; + } return 0; } @@ -697,9 +728,13 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) *p++ = '\0'; else p = argv[i + 2]; - tev->args[i].name = xstrdup(argv[i + 2]); + tev->args[i].name = strdup(argv[i + 2]); /* TODO: parse regs and offset */ - tev->args[i].value = xstrdup(p); + tev->args[i].value = strdup(p); + if (tev->args[i].name == NULL || tev->args[i].value == NULL) { + ret = -ENOMEM; + goto out; + } } ret = 0; out: @@ -933,12 +968,14 @@ error: int convert_to_perf_probe_event(struct kprobe_trace_event *tev, struct perf_probe_event *pev) { - char buf[64]; + char buf[64] = ""; int i, ret; /* Convert event/group name */ - pev->event = xstrdup(tev->event); - pev->group = xstrdup(tev->group); + pev->event = strdup(tev->event); + pev->group = strdup(tev->group); + if (pev->event == NULL || pev->group == NULL) + return -ENOMEM; /* Convert trace_point to probe_point */ ret = convert_to_perf_probe_point(&tev->point, &pev->point); @@ -950,14 +987,17 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); if (pev->args == NULL) return -ENOMEM; - for (i = 0; i < tev->nargs && ret >= 0; i++) + for (i = 0; i < tev->nargs && ret >= 0; i++) { if (tev->args[i].name) - pev->args[i].name = xstrdup(tev->args[i].name); + pev->args[i].name = strdup(tev->args[i].name); else { ret = synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); - pev->args[i].name = xstrdup(buf); + pev->args[i].name = strdup(buf); } + if (pev->args[i].name == NULL && ret >= 0) + ret = -ENOMEM; + } if (ret < 0) clear_perf_probe_event(pev); @@ -1282,7 +1322,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, ret = 0; printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); - for (i = 0; i < ntevs && ret >= 0; i++) { + for (i = 0; i < ntevs; i++) { tev = &tevs[i]; if (pev->event) event = pev->event; @@ -1303,8 +1343,12 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, break; event = buf; - tev->event = xstrdup(event); - tev->group = xstrdup(group); + tev->event = strdup(event); + tev->group = strdup(group); + if (tev->event == NULL || tev->group == NULL) { + ret = -ENOMEM; + break; + } ret = write_kprobe_trace_event(fd, tev); if (ret < 0) break; @@ -1360,23 +1404,40 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, return -ENOMEM; /* Copy parameters */ - tev->point.symbol = xstrdup(pev->point.function); + tev->point.symbol = strdup(pev->point.function); + if (tev->point.symbol == NULL) { + ret = -ENOMEM; + goto error; + } tev->point.offset = pev->point.offset; tev->nargs = pev->nargs; if (tev->nargs) { tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); if (tev->args == NULL) { - free(tev); - *tevs = NULL; - return -ENOMEM; + ret = -ENOMEM; + goto error; } for (i = 0; i < tev->nargs; i++) { - if (pev->args[i].name) - tev->args[i].name = xstrdup(pev->args[i].name); - tev->args[i].value = xstrdup(pev->args[i].var); - if (pev->args[i].type) - tev->args[i].type = xstrdup(pev->args[i].type); + if (pev->args[i].name) { + tev->args[i].name = strdup(pev->args[i].name); + if (tev->args[i].name == NULL) { + ret = -ENOMEM; + goto error; + } + } + tev->args[i].value = strdup(pev->args[i].var); + if (tev->args[i].value == NULL) { + ret = -ENOMEM; + goto error; + } + if (pev->args[i].type) { + tev->args[i].type = strdup(pev->args[i].type); + if (tev->args[i].type == NULL) { + ret = -ENOMEM; + goto error; + } + } } } @@ -1386,13 +1447,15 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", tev->point.symbol); - clear_kprobe_trace_event(tev); - free(tev); - *tevs = NULL; - return -ENOENT; - } else - ret = 1; + ret = -ENOENT; + goto error; + } + return 1; +error: + clear_kprobe_trace_event(tev); + free(tev); + *tevs = NULL; return ret; } @@ -1528,7 +1591,11 @@ int del_perf_probe_events(struct strlist *dellist) return -EINVAL; strlist__for_each(ent, dellist) { - str = xstrdup(ent->s); + str = strdup(ent->s); + if (str == NULL) { + ret = -ENOMEM; + break; + } pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ce1ac82..e443e69 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -424,7 +424,10 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) return -ERANGE; } - tvar->value = xstrdup(regs); + tvar->value = strdup(regs); + if (tvar->value == NULL) + return -ENOMEM; + if (ref) { tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); if (tvar->ref == NULL) @@ -466,7 +469,9 @@ static int convert_variable_type(Dwarf_Die *vr_die, strerror(-ret)); return ret; } - targ->type = xstrdup(buf); + targ->type = strdup(buf); + if (targ->type == NULL) + return -ENOMEM; } return 0; } @@ -576,9 +581,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) vr_die = &die_mem; } if (ret == 0) { - if (pf->pvar->type) - pf->tvar->type = xstrdup(pf->pvar->type); - else + if (pf->pvar->type) { + pf->tvar->type = strdup(pf->pvar->type); + if (pf->tvar->type == NULL) + ret = -ENOMEM; + } else ret = convert_variable_type(vr_die, pf->tvar); } /* *expr will be cached in libdw. Don't free it. */ @@ -595,22 +602,30 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; char buf[32], *ptr; + int ret; /* TODO: Support arrays */ if (pf->pvar->name) - pf->tvar->name = xstrdup(pf->pvar->name); + pf->tvar->name = strdup(pf->pvar->name); else { - synthesize_perf_probe_arg(pf->pvar, buf, 32); + ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); + if (ret < 0) + return ret; ptr = strchr(buf, ':'); /* Change type separator to _ */ if (ptr) *ptr = '_'; - pf->tvar->name = xstrdup(buf); + pf->tvar->name = strdup(buf); } + if (pf->tvar->name == NULL) + return -ENOMEM; if (!is_c_varname(pf->pvar->var)) { /* Copy raw parameters */ - pf->tvar->value = xstrdup(pf->pvar->var); - return 0; + pf->tvar->value = strdup(pf->pvar->var); + if (pf->tvar->value == NULL) + return -ENOMEM; + else + return 0; } pr_debug("Searching '%s' variable in context.\n", @@ -660,7 +675,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) dwarf_diename(sp_die)); return -ENOENT; } - tev->point.symbol = xstrdup(name); + tev->point.symbol = strdup(name); + if (tev->point.symbol == NULL) + return -ENOMEM; tev->point.offset = (unsigned long)(pf->addr - eaddr); } else /* This function has no name. */ @@ -1028,7 +1045,11 @@ int find_perf_probe_point(int fd, unsigned long addr, tmp = dwarf_linesrc(line, NULL, NULL); if (tmp) { ppt->line = lineno; - ppt->file = xstrdup(tmp); + ppt->file = strdup(tmp); + if (ppt->file == NULL) { + ret = -ENOMEM; + goto end; + } found = true; } } @@ -1064,7 +1085,11 @@ int find_perf_probe_point(int fd, unsigned long addr, /* We don't have a line number, let's use offset */ ppt->offset = addr - (unsigned long)eaddr; found: - ppt->function = xstrdup(tmp); + ppt->function = strdup(tmp); + if (ppt->function == NULL) { + ret = -ENOMEM; + goto end; + } found = true; } @@ -1116,8 +1141,11 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) continue; /* Copy real path */ - if (!lf->lr->path) - lf->lr->path = xstrdup(src); + if (!lf->lr->path) { + lf->lr->path = strdup(src); + if (lf->lr->path == NULL) + return -ENOMEM; + } line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); } /* Update status */ -- cgit v0.10.2 From 7ca5989dd065cbc48a958666c273794686ea7525 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:28 -0400 Subject: perf probe: Fix to use correct debugfs path finder Instead of using debugfs_path, use debugfs_find_mountpoint() to find actual debugfs path. LKML-Reference: <20100414223928.14630.38326.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Reported-by: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 64bc11a..c1e5403 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -40,7 +40,6 @@ #include "util/debug.h" #include "util/debugfs.h" #include "util/parse-options.h" -#include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" #include "util/probe-event.h" @@ -205,9 +204,6 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) !params.show_lines)) usage_with_options(probe_usage, options); - if (debugfs_valid_mountpoint(debugfs_path) < 0) - die("Failed to find debugfs path."); - if (params.list_events) { if (params.nevents != 0 || params.dellist) { pr_err(" Error: Don't use --list with --add/--del.\n"); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ca108b2..1c4a20a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -42,8 +42,8 @@ #include "color.h" #include "symbol.h" #include "thread.h" +#include "debugfs.h" #include "trace-event.h" /* For __unused */ -#include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" #include "probe-finder.h" @@ -1075,10 +1075,18 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev) static int open_kprobe_events(bool readwrite) { char buf[PATH_MAX]; + const char *__debugfs; int ret; - ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); + __debugfs = debugfs_find_mountpoint(); + if (__debugfs == NULL) { + pr_warning("Debugfs is not mounted.\n"); + return -ENOENT; + } + + ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); if (ret >= 0) { + pr_debug("Opening %s write=%d\n", buf, readwrite); if (readwrite && !probe_event_dry_run) ret = open(buf, O_RDWR, O_APPEND); else -- cgit v0.10.2 From dd259c5db26ccda46409dbf6efc79d5a2b259e38 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:35 -0400 Subject: perf probe: Fix mis-estimation for shortening filename Fix mis-estimation size for making a short filename. Since the buffer size is 32 bytes and there are '@' prefix and '\0' termination, maximum shorten filename length should be 30. This means, before searching '/', it should be 31 bytes. LKML-Reference: <20100414223935.14630.11954.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c4a20a..6d43839 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -806,12 +806,12 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) goto error; } if (pp->file) { - len = strlen(pp->file) - 32; + len = strlen(pp->file) - 31; if (len < 0) len = 0; tmp = strchr(pp->file + len, '/'); if (!tmp) - tmp = pp->file + len - 1; + tmp = pp->file + len; ret = e_snprintf(file, 32, "@%s", tmp + 1); if (ret <= 0) goto error; -- cgit v0.10.2 From d3b63d7ae04879a817bac5c0bf09749f73629d32 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:42 -0400 Subject: perf probe: Fix a bug that --line range can be overflow Since line_finder.lno_s/e are signed int but line_range.start/end are unsigned int, it is possible to be overflow when converting line_range->start/end to line_finder->lno_s/e. This changes line_range.start/end and line_list.line to signed int and adds overflow checks when setting line_finder.lno_s/e. LKML-Reference: <20100414223942.14630.72730.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6d43839..954ca21 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -189,7 +189,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, #define LINEBUF_SIZE 256 #define NR_ADDITIONAL_LINES 2 -static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +static int show_one_line(FILE *fp, int l, bool skip, bool show_num) { char buf[LINEBUF_SIZE]; const char *color = PERF_COLOR_BLUE; @@ -198,7 +198,7 @@ static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) goto error; if (!skip) { if (show_num) - fprintf(stdout, "%7u %s", l, buf); + fprintf(stdout, "%7d %s", l, buf); else color_fprintf(stdout, color, " %s", buf); } @@ -231,7 +231,7 @@ error: */ int show_line_range(struct line_range *lr) { - unsigned int l = 1; + int l = 1; struct line_node *ln; FILE *fp; int fd, ret; @@ -340,16 +340,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) */ ptr = strchr(arg, ':'); if (ptr) { - lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); + lr->start = (int)strtoul(ptr + 1, &tmp, 0); if (*tmp == '+') - lr->end = lr->start + (unsigned int)strtoul(tmp + 1, - &tmp, 0); + lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); else if (*tmp == '-') - lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); + lr->end = (int)strtoul(tmp + 1, &tmp, 0); else - lr->end = 0; - pr_debug("Line range is %u to %u\n", lr->start, lr->end); - if (lr->end && lr->start > lr->end) { + lr->end = INT_MAX; + pr_debug("Line range is %d to %d\n", lr->start, lr->end); + if (lr->start > lr->end) { semantic_error("Start line must be smaller" " than end line.\n"); return -EINVAL; @@ -360,8 +359,10 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) return -EINVAL; } tmp = strndup(arg, (ptr - arg)); - } else + } else { tmp = strdup(arg); + lr->end = INT_MAX; + } if (tmp == NULL) return -ENOMEM; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ab54929..e7ff0d0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -74,15 +74,15 @@ struct perf_probe_event { /* Line number container */ struct line_node { struct list_head list; - unsigned int line; + int line; }; /* Line range */ struct line_range { char *file; /* File name */ char *function; /* Function name */ - unsigned int start; /* Start line number */ - unsigned int end; /* End line number */ + int start; /* Start line number */ + int end; /* End line number */ int offset; /* Start line offset */ char *path; /* Real path name */ struct list_head line_list; /* Visible lines */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e443e69..b4c9365 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -111,7 +111,7 @@ static int strtailcmp(const char *s1, const char *s2) /* Line number list operations */ /* Add a line to line number list */ -static int line_list__add_line(struct list_head *head, unsigned int line) +static int line_list__add_line(struct list_head *head, int line) { struct line_node *ln; struct list_head *p; @@ -138,7 +138,7 @@ found: } /* Check if the line in line number list */ -static int line_list__has_line(struct list_head *head, unsigned int line) +static int line_list__has_line(struct list_head *head, int line) { struct line_node *ln; @@ -1146,7 +1146,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) if (lf->lr->path == NULL) return -ENOMEM; } - line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); + line_list__add_line(&lf->lr->line_list, lineno); } /* Update status */ if (!list_empty(&lf->lr->line_list)) @@ -1179,10 +1179,12 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; - if (!lr->end) + if (lf->lno_s < 0) /* Overflow */ + lf->lno_s = INT_MAX; + lf->lno_e = lr->offset + lr->end; + if (lf->lno_e < 0) /* Overflow */ lf->lno_e = INT_MAX; - else - lf->lno_e = lr->offset + lr->end; + pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; if (dwarf_func_inline(sp_die)) { @@ -1244,10 +1246,7 @@ int find_line_range(int fd, struct line_range *lr) ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; - if (!lr->end) - lf.lno_e = INT_MAX; - else - lf.lno_e = lr->end; + lf.lno_e = lr->end; ret = find_line_range_by_line(NULL, &lf); } } -- cgit v0.10.2 From dda4ab34fe1905d3d590572b776dd92aa0866558 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:50 -0400 Subject: perf probe: Fix line range to show end line Line range should reject the range if the number of lines is 0 (e.g. "sched.c:1024+0"), and it should show the lines include the end of line number (e.g. "sched.c:1024-2048" should show 2048th line). LKML-Reference: <20100414223950.14630.42263.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 954ca21..5bf8ab0 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -290,7 +290,7 @@ int show_line_range(struct line_range *lr) if (lr->end == INT_MAX) lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp) && ret >= 0) + while (l <= lr->end && !feof(fp) && ret >= 0) ret = show_one_line(fp, (l++) - lr->offset, false, false); end: fclose(fp); @@ -341,9 +341,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) ptr = strchr(arg, ':'); if (ptr) { lr->start = (int)strtoul(ptr + 1, &tmp, 0); - if (*tmp == '+') + if (*tmp == '+') { lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); - else if (*tmp == '-') + lr->end--; /* + * Adjust the number of lines here. + * If the number of lines == 1, the + * the end of line should be equal to + * the start of line. + */ + } else if (*tmp == '-') lr->end = (int)strtoul(tmp + 1, &tmp, 0); else lr->end = INT_MAX; -- cgit v0.10.2 From de1439d8a521d22c3219fc007a570fcf944ac789 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 17:44:00 -0300 Subject: perf probe: Support DW_OP_plus_uconst in DW_AT_data_member_location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DW_OP_plus_uconst can be used for DW_AT_data_member_location. This patch adds DW_OP_plus_uconst support when getting structure member offset. Commiter note: Fixed up the size_t format specifier in one case: cc1: warnings being treated as errors util/probe-finder.c: In function ‘die_get_data_member_location’: util/probe-finder.c:270: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘size_t’ make: *** [/home/acme/git/build/perf/util/probe-finder.o] Error 1 LKML-Reference: <20100414223958.14630.5230.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index b4c9365..03b4691 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -249,6 +249,33 @@ static int die_get_byte_size(Dwarf_Die *tp_die) return (int)ret; } +/* Get data_member_location offset */ +static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) +{ + Dwarf_Attribute attr; + Dwarf_Op *expr; + size_t nexpr; + int ret; + + if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + return -ENOENT; + + if (dwarf_formudata(&attr, offs) != 0) { + /* DW_AT_data_member_location should be DW_OP_plus_uconst */ + ret = dwarf_getlocation(&attr, &expr, &nexpr); + if (ret < 0 || nexpr == 0) + return -ENOENT; + + if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { + pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", + expr[0].atom, nexpr); + return -ENOTSUP; + } + *offs = (Dwarf_Word)expr[0].number; + } + return 0; +} + /* Return values for die_find callbacks */ enum { DIE_FIND_CB_FOUND = 0, /* End of Search */ @@ -482,9 +509,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, Dwarf_Die *die_mem) { struct kprobe_trace_arg_ref *ref = *ref_ptr; - Dwarf_Attribute attr; Dwarf_Die type; Dwarf_Word offs; + int ret; pr_debug("converting %s in %s\n", field->name, varname); if (die_get_real_type(vr_die, &type) == NULL) { @@ -542,17 +569,17 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, } /* Get the offset of the field */ - if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL || - dwarf_formudata(&attr, &offs) != 0) { + ret = die_get_data_member_location(die_mem, &offs); + if (ret < 0) { pr_warning("Failed to get the offset of %s.\n", field->name); - return -ENOENT; + return ret; } ref->offset += (long)offs; /* Converting next field */ if (field->next) return convert_variable_fields(die_mem, field->name, - field->next, &ref, die_mem); + field->next, &ref, die_mem); else return 0; } -- cgit v0.10.2 From f6c903f5856ffa75ae19dcee4dbb5093e320d45c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:40:07 -0400 Subject: perf probe: Show function entry line as probe-able Function entry line should be shown as probe-able line, because each function has declared line attribute. LKML-Reference: <20100414224007.14630.96915.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 03b4691..3e79775 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1127,6 +1127,45 @@ end: return ret; } +/* Add a line and store the src path */ +static int line_range_add_line(const char *src, unsigned int lineno, + struct line_range *lr) +{ + /* Copy real path */ + if (!lr->path) { + lr->path = strdup(src); + if (lr->path == NULL) + return -ENOMEM; + } + return line_list__add_line(&lr->line_list, lineno); +} + +/* Search function declaration lines */ +static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) +{ + struct dwarf_callback_param *param = data; + struct line_finder *lf = param->data; + const char *src; + int lineno; + + src = dwarf_decl_file(sp_die); + if (src && strtailcmp(src, lf->fname) != 0) + return DWARF_CB_OK; + + if (dwarf_decl_line(sp_die, &lineno) != 0 || + (lf->lno_s > lineno || lf->lno_e < lineno)) + return DWARF_CB_OK; + + param->retval = line_range_add_line(src, lineno, lf->lr); + return DWARF_CB_OK; +} + +static int find_line_range_func_decl_lines(struct line_finder *lf) +{ + struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; + dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); + return param.retval; +} /* Find line range from its line number */ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) @@ -1135,7 +1174,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; - int lineno; + int lineno, ret = 0; const char *src; Dwarf_Die die_mem; @@ -1145,6 +1184,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) return -ENOENT; } + /* Search probable lines on lines list */ for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); if (dwarf_lineno(line, &lineno) != 0 || @@ -1167,22 +1207,34 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) if (strtailcmp(src, lf->fname) != 0) continue; - /* Copy real path */ - if (!lf->lr->path) { - lf->lr->path = strdup(src); - if (lf->lr->path == NULL) - return -ENOMEM; - } - line_list__add_line(&lf->lr->line_list, lineno); + ret = line_range_add_line(src, lineno, lf->lr); + if (ret < 0) + return ret; } + + /* + * Dwarf lines doesn't include function declarations. We have to + * check functions list or given function. + */ + if (sp_die) { + src = dwarf_decl_file(sp_die); + if (src && dwarf_decl_line(sp_die, &lineno) == 0 && + (lf->lno_s <= lineno && lf->lno_e >= lineno)) + ret = line_range_add_line(src, lineno, lf->lr); + } else + ret = find_line_range_func_decl_lines(lf); + /* Update status */ - if (!list_empty(&lf->lr->line_list)) - lf->found = 1; + if (ret >= 0) + if (!list_empty(&lf->lr->line_list)) + ret = lf->found = 1; + else + ret = 0; /* Lines are not found */ else { free(lf->lr->path); lf->lr->path = NULL; } - return lf->found; + return ret; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) -- cgit v0.10.2 From 95476b64ab11d528de2557366ec584977c215b9e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 23:42:18 +0200 Subject: perf: Fix hlist related build error hlist helpers need to be available for all software events, not only trace events. Pull them out outside the ifdef CONFIG_EVENT_TRACING section. Fixes: kernel/perf_event.c:4573: error: implicit declaration of function 'swevent_hlist_put' kernel/perf_event.c:4614: error: implicit declaration of function 'swevent_hlist_get' kernel/perf_event.c:5534: error: implicit declaration of function 'swevent_hlist_release Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras LKML-Reference: <1271281338-23491-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 095101d..07b7a43 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4313,36 +4313,6 @@ static const struct pmu perf_ops_task_clock = { .read = task_clock_perf_event_read, }; -#ifdef CONFIG_EVENT_TRACING - -void perf_tp_event(int event_id, u64 addr, u64 count, void *record, - int entry_size, struct pt_regs *regs) -{ - struct perf_sample_data data; - struct perf_raw_record raw = { - .size = entry_size, - .data = record, - }; - - perf_sample_data_init(&data, addr); - data.raw = &raw; - - /* Trace events already protected against recursion */ - do_perf_sw_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, - &data, regs); -} -EXPORT_SYMBOL_GPL(perf_tp_event); - -static int perf_tp_event_match(struct perf_event *event, - struct perf_sample_data *data) -{ - void *record = data->raw->data; - - if (likely(!event->filter) || filter_match_preds(event->filter, record)) - return 1; - return 0; -} - static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) { struct swevent_hlist *hlist; @@ -4442,6 +4412,36 @@ static int swevent_hlist_get(struct perf_event *event) return err; } +#ifdef CONFIG_EVENT_TRACING + +void perf_tp_event(int event_id, u64 addr, u64 count, void *record, + int entry_size, struct pt_regs *regs) +{ + struct perf_sample_data data; + struct perf_raw_record raw = { + .size = entry_size, + .data = record, + }; + + perf_sample_data_init(&data, addr); + data.raw = &raw; + + /* Trace events already protected against recursion */ + do_perf_sw_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, + &data, regs); +} +EXPORT_SYMBOL_GPL(perf_tp_event); + +static int perf_tp_event_match(struct perf_event *event, + struct perf_sample_data *data) +{ + void *record = data->raw->data; + + if (likely(!event->filter) || filter_match_preds(event->filter, record)) + return 1; + return 0; +} + static void tp_perf_event_destroy(struct perf_event *event) { perf_trace_disable(event->attr.config); -- cgit v0.10.2 From a1e2f60e3efc812bf66a2be0d8530ee175003f6d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 14 Apr 2010 23:58:03 +0200 Subject: perf: Fix dynamic field detection Checking if a tracing field is an array with a dynamic length requires to check the field type and seek the "__data_loc" string that prepends the actual type, as can be found in a trace event format file: field:__data_loc char[] name; offset:16; size:4; signed:1; But we actually use strcmp() to check if the field type fully matches "__data_loc", which may fail as we trip over the rest of the type. To fix this, use strncmp to only check if it starts with "__data_loc". Signed-off-by: Thomas Gleixner Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Tom Zanussi Cc: Steven Rostedt LKML-Reference: <1271282283-23721-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 17d6d66..d6ef414 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -761,7 +761,7 @@ static int field_is_string(struct format_field *field) static int field_is_dynamic(struct format_field *field) { - if (!strcmp(field->type, "__data_loc")) + if (!strncmp(field->type, "__data_loc", 10)) return 1; return 0; -- cgit v0.10.2 From 7865e817e9b4b378ac57ab7f16183100b95466ce Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 19:42:07 +0200 Subject: perf: Make -f the default for perf record Force the overwriting mode by default if append mode is not explicit. Adding -f every time one uses perf on a daily basis quickly becomes a burden. Keep the -f among the options though to avoid breaking some random users scripts. Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index fc46c0b..b29bd2d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -58,7 +58,7 @@ OPTIONS -f:: --force:: - Overwrite existing data file. + Overwrite existing data file. (deprecated) -c:: --count=:: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9a95136..dcda899 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -26,6 +26,11 @@ #include #include +enum write_mode_t { + WRITE_FORCE, + WRITE_APPEND +}; + static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static long default_interval = 0; @@ -47,8 +52,7 @@ static pid_t *all_tids = NULL; static int thread_num = 0; static pid_t child_pid = -1; static bool inherit = true; -static bool force = false; -static bool append_file = false; +static enum write_mode_t write_mode = WRITE_FORCE; static bool call_graph = false; static bool inherit_stat = false; static bool no_samples = false; @@ -450,26 +454,19 @@ static int __cmd_record(int argc, const char **argv) } if (!stat(output_name, &st) && st.st_size) { - if (!force) { - if (!append_file) { - pr_err("Error, output file %s exists, use -A " - "to append or -f to overwrite.\n", - output_name); - exit(-1); - } - } else { + if (write_mode == WRITE_FORCE) { char oldname[PATH_MAX]; snprintf(oldname, sizeof(oldname), "%s.old", output_name); unlink(oldname); rename(output_name, oldname); } - } else { - append_file = false; + } else if (write_mode == WRITE_APPEND) { + write_mode = WRITE_FORCE; } flags = O_CREAT|O_RDWR; - if (append_file) + if (write_mode == WRITE_APPEND) file_new = 0; else flags |= O_TRUNC; @@ -480,7 +477,8 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - session = perf_session__new(output_name, O_WRONLY, force); + session = perf_session__new(output_name, O_WRONLY, + write_mode == WRITE_FORCE); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; @@ -667,6 +665,8 @@ static const char * const record_usage[] = { NULL }; +static bool force, append_file; + static const struct option options[] = { OPT_CALLBACK('e', "event", NULL, "event", "event selector. use 'perf list' to list available events", @@ -688,7 +688,7 @@ static const struct option options[] = { OPT_INTEGER('C', "profile_cpu", &profile_cpu, "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, - "overwrite existing data file"), + "overwrite existing data file (deprecated)"), OPT_LONG('c', "count", &default_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", @@ -725,6 +725,16 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) !system_wide && profile_cpu == -1) usage_with_options(record_usage, options); + if (force && append_file) { + fprintf(stderr, "Can't overwrite and append at the same time." + " You need to choose between -f and -A"); + usage_with_options(record_usage, options); + } else if (append_file) { + write_mode = WRITE_APPEND; + } else { + write_mode = WRITE_FORCE; + } + symbol__init(); if (!nr_counters) { -- cgit v0.10.2 From bdef3b02ceeb97f5f67fcfa6dff13c4e70b34fb7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 20:05:17 +0200 Subject: perf: Always record tracepoints raw samples from perf record Trace events are mostly used for tracing rather than simple counting. Don't bother anymore with adding -R when using them, just record raw samples of trace events every time. Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index b29bd2d..020d871 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -101,7 +101,7 @@ OPTIONS -R:: --raw-samples:: -Collect raw sample records from all opened counters (typically for tracepoint counters). +Collect raw sample records from all opened counters (default for tracepoint counters). SEE ALSO -------- diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 435781e..880070c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -410,7 +410,6 @@ static enum event_result parse_single_tracepoint_event(char *sys_name, const char *evt_name, unsigned int evt_length, - char *flags, struct perf_event_attr *attr, const char **strp) { @@ -419,13 +418,9 @@ parse_single_tracepoint_event(char *sys_name, u64 id; int fd; - if (flags) { - if (!strncmp(flags, "record", strlen(flags))) { - attr->sample_type |= PERF_SAMPLE_RAW; - attr->sample_type |= PERF_SAMPLE_TIME; - attr->sample_type |= PERF_SAMPLE_CPU; - } - } + attr->sample_type |= PERF_SAMPLE_RAW; + attr->sample_type |= PERF_SAMPLE_TIME; + attr->sample_type |= PERF_SAMPLE_CPU; snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); @@ -533,8 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, flags); } else return parse_single_tracepoint_event(sys_name, evt_name, - evt_length, flags, - attr, strp); + evt_length, attr, strp); } static enum event_result -- cgit v0.10.2 From f92128193094c288bc315db1694fafeaeb7ee1d0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 14 Apr 2010 22:09:02 +0200 Subject: perf: Make the trace events sample period default to 1 Trace events are mostly used for tracing and then require not to be lost when possible. As opposite to hardware events that really require to trigger after a given sample period, trace events mostly need to trigger everytime. It is a frustrating experience to trace with perf and realize we lost a lot of events because we forgot the "-c 1" option. Then default sample_period to 1 for trace events but let the user override it. Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Thomas Gleixner diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index dcda899..ca2affc 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -33,11 +33,13 @@ enum write_mode_t { static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; +static unsigned int user_interval = UINT_MAX; static long default_interval = 0; static int nr_cpus = 0; static unsigned int page_size; static unsigned int mmap_pages = 128; +static unsigned int user_freq = UINT_MAX; static int freq = 1000; static int output; static const char *output_name = "perf.data"; @@ -255,10 +257,19 @@ static void create_counter(int counter, int cpu) if (nr_counters > 1) attr->sample_type |= PERF_SAMPLE_ID; - if (freq) { - attr->sample_type |= PERF_SAMPLE_PERIOD; - attr->freq = 1; - attr->sample_freq = freq; + /* + * We default some events to a 1 default interval. But keep + * it a weak assumption overridable by the user. + */ + if (!attr->sample_period || (user_freq != UINT_MAX && + user_interval != UINT_MAX)) { + if (freq) { + attr->sample_type |= PERF_SAMPLE_PERIOD; + attr->freq = 1; + attr->sample_freq = freq; + } else { + attr->sample_period = default_interval; + } } if (no_samples) @@ -689,13 +700,13 @@ static const struct option options[] = { "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, "overwrite existing data file (deprecated)"), - OPT_LONG('c', "count", &default_interval, + OPT_LONG('c', "count", &user_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), - OPT_INTEGER('F', "freq", &freq, + OPT_INTEGER('F', "freq", &user_freq, "profile at this frequency"), OPT_INTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), @@ -716,7 +727,6 @@ static const struct option options[] = { int cmd_record(int argc, const char **argv, const char *prefix __used) { - int counter; int i,j; argc = parse_options(argc, argv, options, record_usage, @@ -774,6 +784,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (!event_array) return -ENOMEM; + if (user_interval != UINT_MAX) + default_interval = user_interval; + if (user_freq != UINT_MAX) + freq = user_freq; + /* * User specified count overrides default frequency. */ @@ -786,12 +801,5 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) exit(EXIT_FAILURE); } - for (counter = 0; counter < nr_counters; counter++) { - if (attrs[counter].sample_period) - continue; - - attrs[counter].sample_period = default_interval; - } - return __cmd_record(argc, argv); } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 880070c..3b4ec67 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -422,6 +422,8 @@ parse_single_tracepoint_event(char *sys_name, attr->sample_type |= PERF_SAMPLE_TIME; attr->sample_type |= PERF_SAMPLE_CPU; + attr->sample_period = 1; + snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); -- cgit v0.10.2 From 39447b386c846bbf1c56f6403c5282837486200f Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Mon, 19 Apr 2010 13:32:41 +0800 Subject: perf: Enhance perf to allow for guest statistic collection from host Below patch introduces perf_guest_info_callbacks and related register/unregister functions. Add more PERF_RECORD_MISC_XXX bits meaning guest kernel and guest user space. Signed-off-by: Zhang Yanmin Signed-off-by: Avi Kivity diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index f6d43db..254883d 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -135,17 +135,10 @@ extern void perf_events_lapic_init(void); */ #define PERF_EFLAGS_EXACT (1UL << 3) -#define perf_misc_flags(regs) \ -({ int misc = 0; \ - if (user_mode(regs)) \ - misc |= PERF_RECORD_MISC_USER; \ - else \ - misc |= PERF_RECORD_MISC_KERNEL; \ - if (regs->flags & PERF_EFLAGS_EXACT) \ - misc |= PERF_RECORD_MISC_EXACT; \ - misc; }) - -#define perf_instruction_pointer(regs) ((regs)->ip) +struct pt_regs; +extern unsigned long perf_instruction_pointer(struct pt_regs *regs); +extern unsigned long perf_misc_flags(struct pt_regs *regs); +#define perf_misc_flags(regs) perf_misc_flags(regs) #else static inline void init_hw_perf_events(void) { } diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 626154a..2ea78ab 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1720,6 +1720,11 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) { struct perf_callchain_entry *entry; + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { + /* TODO: We don't support guest os callchain now */ + return NULL; + } + if (in_nmi()) entry = &__get_cpu_var(pmc_nmi_entry); else @@ -1743,3 +1748,29 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski regs->cs = __KERNEL_CS; local_save_flags(regs->flags); } + +unsigned long perf_instruction_pointer(struct pt_regs *regs) +{ + unsigned long ip; + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) + ip = perf_guest_cbs->get_guest_ip(); + else + ip = instruction_pointer(regs); + return ip; +} + +unsigned long perf_misc_flags(struct pt_regs *regs) +{ + int misc = 0; + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { + misc |= perf_guest_cbs->is_user_mode() ? + PERF_RECORD_MISC_GUEST_USER : + PERF_RECORD_MISC_GUEST_KERNEL; + } else + misc |= user_mode(regs) ? PERF_RECORD_MISC_USER : + PERF_RECORD_MISC_KERNEL; + if (regs->flags & PERF_EFLAGS_EXACT) + misc |= PERF_RECORD_MISC_EXACT; + + return misc; +} diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index bf896d0..24de5f1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -288,11 +288,13 @@ struct perf_event_mmap_page { __u64 data_tail; /* user-space written tail */ }; -#define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0) +#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) #define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) #define PERF_RECORD_MISC_KERNEL (1 << 0) #define PERF_RECORD_MISC_USER (2 << 0) #define PERF_RECORD_MISC_HYPERVISOR (3 << 0) +#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) +#define PERF_RECORD_MISC_GUEST_USER (5 << 0) #define PERF_RECORD_MISC_EXACT (1 << 14) /* @@ -446,6 +448,12 @@ enum perf_callchain_context { # include #endif +struct perf_guest_info_callbacks { + int (*is_in_guest) (void); + int (*is_user_mode) (void); + unsigned long (*get_guest_ip) (void); +}; + #ifdef CONFIG_HAVE_HW_BREAKPOINT #include #endif @@ -932,6 +940,12 @@ static inline void perf_event_mmap(struct vm_area_struct *vma) __perf_event_mmap(vma); } +extern struct perf_guest_info_callbacks *perf_guest_cbs; +extern int perf_register_guest_info_callbacks( + struct perf_guest_info_callbacks *); +extern int perf_unregister_guest_info_callbacks( + struct perf_guest_info_callbacks *); + extern void perf_event_comm(struct task_struct *tsk); extern void perf_event_fork(struct task_struct *tsk); @@ -1001,6 +1015,11 @@ perf_sw_event(u32 event_id, u64 nr, int nmi, static inline void perf_bp_event(struct perf_event *event, void *data) { } +static inline int perf_register_guest_info_callbacks +(struct perf_guest_info_callbacks *) {return 0; } +static inline int perf_unregister_guest_info_callbacks +(struct perf_guest_info_callbacks *) {return 0; } + static inline void perf_event_mmap(struct vm_area_struct *vma) { } static inline void perf_event_comm(struct task_struct *tsk) { } static inline void perf_event_fork(struct task_struct *tsk) { } diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 07b7a43..9dbe8cd 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2798,6 +2798,27 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski /* + * We assume there is only KVM supporting the callbacks. + * Later on, we might change it to a list if there is + * another virtualization implementation supporting the callbacks. + */ +struct perf_guest_info_callbacks *perf_guest_cbs; + +int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) +{ + perf_guest_cbs = cbs; + return 0; +} +EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); + +int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) +{ + perf_guest_cbs = NULL; + return 0; +} +EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); + +/* * Output */ static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail, @@ -3749,7 +3770,7 @@ void __perf_event_mmap(struct vm_area_struct *vma) .event_id = { .header = { .type = PERF_RECORD_MMAP, - .misc = 0, + .misc = PERF_RECORD_MISC_USER, /* .size */ }, /* .pid */ -- cgit v0.10.2 From ff9d07a0e7ce756a183e7c2e483aec452ee6b574 Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Mon, 19 Apr 2010 13:32:45 +0800 Subject: KVM: Implement perf callbacks for guest sampling Below patch implements the perf_guest_info_callbacks on kvm. Signed-off-by: Zhang Yanmin Signed-off-by: Avi Kivity diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 686492ed..82be6da 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3654,8 +3654,11 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) /* We need to handle NMIs before interrupts are enabled */ if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR && - (exit_intr_info & INTR_INFO_VALID_MASK)) + (exit_intr_info & INTR_INFO_VALID_MASK)) { + kvm_before_handle_nmi(&vmx->vcpu); asm("int $2"); + kvm_after_handle_nmi(&vmx->vcpu); + } idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 24cd0ee..c3a33b2 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #undef TRACE_INCLUDE_FILE #define CREATE_TRACE_POINTS @@ -3765,6 +3766,47 @@ static void kvm_timer_init(void) } } +static DEFINE_PER_CPU(struct kvm_vcpu *, current_vcpu); + +static int kvm_is_in_guest(void) +{ + return percpu_read(current_vcpu) != NULL; +} + +static int kvm_is_user_mode(void) +{ + int user_mode = 3; + if (percpu_read(current_vcpu)) + user_mode = kvm_x86_ops->get_cpl(percpu_read(current_vcpu)); + return user_mode != 0; +} + +static unsigned long kvm_get_guest_ip(void) +{ + unsigned long ip = 0; + if (percpu_read(current_vcpu)) + ip = kvm_rip_read(percpu_read(current_vcpu)); + return ip; +} + +static struct perf_guest_info_callbacks kvm_guest_cbs = { + .is_in_guest = kvm_is_in_guest, + .is_user_mode = kvm_is_user_mode, + .get_guest_ip = kvm_get_guest_ip, +}; + +void kvm_before_handle_nmi(struct kvm_vcpu *vcpu) +{ + percpu_write(current_vcpu, vcpu); +} +EXPORT_SYMBOL_GPL(kvm_before_handle_nmi); + +void kvm_after_handle_nmi(struct kvm_vcpu *vcpu) +{ + percpu_write(current_vcpu, NULL); +} +EXPORT_SYMBOL_GPL(kvm_after_handle_nmi); + int kvm_arch_init(void *opaque) { int r; @@ -3801,6 +3843,8 @@ int kvm_arch_init(void *opaque) kvm_timer_init(); + perf_register_guest_info_callbacks(&kvm_guest_cbs); + return 0; out: @@ -3809,6 +3853,8 @@ out: void kvm_arch_exit(void) { + perf_unregister_guest_info_callbacks(&kvm_guest_cbs); + if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) cpufreq_unregister_notifier(&kvmclock_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 2d101639..b7a4047 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -65,4 +65,7 @@ static inline int is_paging(struct kvm_vcpu *vcpu) return kvm_read_cr0_bits(vcpu, X86_CR0_PG); } +void kvm_before_handle_nmi(struct kvm_vcpu *vcpu); +void kvm_after_handle_nmi(struct kvm_vcpu *vcpu); + #endif -- cgit v0.10.2 From a1645ce12adb6c9cc9e19d7695466204e3f017fe Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Mon, 19 Apr 2010 13:32:50 +0800 Subject: perf: 'perf kvm' tool for monitoring guest performance from host Here is the patch of userspace perf tool. Signed-off-by: Zhang Yanmin Signed-off-by: Avi Kivity diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt new file mode 100644 index 0000000..93400a0 --- /dev/null +++ b/tools/perf/Documentation/perf-kvm.txt @@ -0,0 +1,67 @@ +perf-kvm(1) +============== + +NAME +---- +perf-kvm - Tool to trace/measure kvm guest os + +SYNOPSIS +-------- +[verse] +'perf kvm' [--host] [--guest] [--guestmount= + [--guestkallsyms= --guestmodules= | --guestvmlinux=]] + {top|record|report|diff|buildid-list} +'perf kvm' [--host] [--guest] [--guestkallsyms= --guestmodules= + | --guestvmlinux=] {top|record|report|diff|buildid-list} + +DESCRIPTION +----------- +There are a couple of variants of perf kvm: + + 'perf kvm [options] top ' to generates and displays + a performance counter profile of guest os in realtime + of an arbitrary workload. + + 'perf kvm record ' to record the performance couinter profile + of an arbitrary workload and save it into a perf data file. If both + --host and --guest are input, the perf data file name is perf.data.kvm. + If there is no --host but --guest, the file name is perf.data.guest. + If there is no --guest but --host, the file name is perf.data.host. + + 'perf kvm report' to display the performance counter profile information + recorded via perf kvm record. + + 'perf kvm diff' to displays the performance difference amongst two perf.data + files captured via perf record. + + 'perf kvm buildid-list' to display the buildids found in a perf data file, + so that other tools can be used to fetch packages with matching symbol tables + for use by perf report. + +OPTIONS +------- +--host=:: + Collect host side perforamnce profile. +--guest=:: + Collect guest side perforamnce profile. +--guestmount=:: + Guest os root file system mount directory. Users mounts guest os + root directories under by a specific filesystem access method, + typically, sshfs. For example, start 2 guest os. The one's pid is 8888 + and the other's is 9999. + #mkdir ~/guestmount; cd ~/guestmount + #sshfs -o allow_other,direct_io -p 5551 localhost:/ 8888/ + #sshfs -o allow_other,direct_io -p 5552 localhost:/ 9999/ + #perf kvm --host --guest --guestmount=~/guestmount top +--guestkallsyms=:: + Guest os /proc/kallsyms file copy. 'perf' kvm' reads it to get guest + kernel symbols. Users copy it out from guest os. +--guestmodules=:: + Guest os /proc/modules file copy. 'perf' kvm' reads it to get guest + kernel module information. Users copy it out from guest os. +--guestvmlinux=:: + Guest os kernel vmlinux. + +SEE ALSO +-------- +linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 57b3569..3cb3449 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -472,6 +472,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o BUILTIN_OBJS += $(OUTPUT)builtin-probe.o BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o BUILTIN_OBJS += $(OUTPUT)builtin-lock.o +BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o PERFLIBS = $(LIB_FILE) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 06eaebe..f924b43 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -571,7 +571,7 @@ static int __cmd_annotate(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(stdout); + dsos__fprintf(&session->kerninfo_root, stdout); perf_session__collapse_resort(&session->hists); perf_session__output_resort(&session->hists, session->event_total[0]); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index af2ad8b..623afe3 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - dsos__fprintf_buildid(stdout, with_hits); + dsos__fprintf_buildid(&session->kerninfo_root, stdout, with_hits); perf_session__delete(session); return err; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a1d94d..207e860 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -33,7 +33,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - he->count += count; + __perf_session__add_count(he, al, count); return 0; } @@ -225,6 +225,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used) input_new = argv[1]; } else input_new = argv[0]; + } else if (symbol_conf.default_guest_vmlinux_name || + symbol_conf.default_guest_kallsyms) { + input_old = "perf.data.host"; + input_new = "perf.data.guest"; } symbol_conf.exclude_other = false; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 513aa8a..db474bb 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -351,6 +351,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, int n_lines, int is_caller) { struct rb_node *next; + struct kernel_info *kerninfo; printf("%.102s\n", graph_dotted_line); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); @@ -359,10 +360,16 @@ static void __print_result(struct rb_root *root, struct perf_session *session, next = rb_first(root); + kerninfo = kerninfo__findhost(&session->kerninfo_root); + if (!kerninfo) { + pr_err("__print_result: couldn't find kernel information\n"); + return; + } while (next && n_lines--) { struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; + struct map_groups *kmaps = &kerninfo->kmaps; struct map *map; char buf[BUFSIZ]; u64 addr; @@ -370,8 +377,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = map_groups__find_function(&session->kmaps, - addr, &map, NULL); + sym = map_groups__find_function(kmaps, addr, + &map, NULL); } else addr = data->ptr; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c new file mode 100644 index 0000000..a4c7cae --- /dev/null +++ b/tools/perf/builtin-kvm.c @@ -0,0 +1,144 @@ +#include "builtin.h" +#include "perf.h" + +#include "util/util.h" +#include "util/cache.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/header.h" +#include "util/session.h" + +#include "util/parse-options.h" +#include "util/trace-event.h" + +#include "util/debug.h" + +#include + +#include +#include +#include + +static char *file_name; +static char name_buffer[256]; + +int perf_host = 1; +int perf_guest; + +static const char * const kvm_usage[] = { + "perf kvm [] {top|record|report|diff|buildid-list}", + NULL +}; + +static const struct option kvm_options[] = { + OPT_STRING('i', "input", &file_name, "file", + "Input file name"), + OPT_STRING('o', "output", &file_name, "file", + "Output file name"), + OPT_BOOLEAN(0, "guest", &perf_guest, + "Collect guest os data"), + OPT_BOOLEAN(0, "host", &perf_host, + "Collect guest os data"), + OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", + "guest mount directory under which every guest os" + " instance has a subdir"), + OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, + "file", "file saving guest os vmlinux"), + OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, + "file", "file saving guest os /proc/kallsyms"), + OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, + "file", "file saving guest os /proc/modules"), + OPT_END() +}; + +static int __cmd_record(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("record"); + rec_argv[i++] = strdup("-o"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_record(i, rec_argv, NULL); +} + +static int __cmd_report(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("report"); + rec_argv[i++] = strdup("-i"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_report(i, rec_argv, NULL); +} + +static int __cmd_buildid_list(int argc, const char **argv) +{ + int rec_argc, i = 0, j; + const char **rec_argv; + + rec_argc = argc + 2; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + rec_argv[i++] = strdup("buildid-list"); + rec_argv[i++] = strdup("-i"); + rec_argv[i++] = strdup(file_name); + for (j = 1; j < argc; j++, i++) + rec_argv[i] = argv[j]; + + BUG_ON(i != rec_argc); + + return cmd_buildid_list(i, rec_argv, NULL); +} + +int cmd_kvm(int argc, const char **argv, const char *prefix __used) +{ + perf_host = perf_guest = 0; + + argc = parse_options(argc, argv, kvm_options, kvm_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (!argc) + usage_with_options(kvm_usage, kvm_options); + + if (!perf_host) + perf_guest = 1; + + if (!file_name) { + if (perf_host && !perf_guest) + sprintf(name_buffer, "perf.data.host"); + else if (!perf_host && perf_guest) + sprintf(name_buffer, "perf.data.guest"); + else + sprintf(name_buffer, "perf.data.kvm"); + file_name = name_buffer; + } + + if (!strncmp(argv[0], "rec", 3)) + return __cmd_record(argc, argv); + else if (!strncmp(argv[0], "rep", 3)) + return __cmd_report(argc, argv); + else if (!strncmp(argv[0], "diff", 4)) + return cmd_diff(argc, argv, NULL); + else if (!strncmp(argv[0], "top", 3)) + return cmd_top(argc, argv, NULL); + else if (!strncmp(argv[0], "buildid-list", 12)) + return __cmd_buildid_list(argc, argv); + else + usage_with_options(kvm_usage, kvm_options); + + return 0; +} diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a1b99ee..27f992a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -456,6 +456,52 @@ static void atexit_header(void) } } +static void event__synthesize_guest_os(struct kernel_info *kerninfo, + void *data __attribute__((unused))) +{ + int err; + char *guest_kallsyms; + char path[PATH_MAX]; + + if (is_host_kernel(kerninfo)) + return; + + /* + *As for guest kernel when processing subcommand record&report, + *we arrange module mmap prior to guest kernel mmap and trigger + *a preload dso because default guest module symbols are loaded + *from guest kallsyms instead of /lib/modules/XXX/XXX. This + *method is used to avoid symbol missing when the first addr is + *in module instead of in guest kernel. + */ + err = event__synthesize_modules(process_synthesized_event, + session, + kerninfo); + if (err < 0) + pr_err("Couldn't record guest kernel [%d]'s reference" + " relocation symbol.\n", kerninfo->pid); + + if (is_default_guest(kerninfo)) + guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + guest_kallsyms = path; + } + + /* + * We use _stext for guest kernel because guest kernel's /proc/kallsyms + * have no _text sometimes. + */ + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, kerninfo, "_text"); + if (err < 0) + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, kerninfo, "_stext"); + if (err < 0) + pr_err("Couldn't record guest kernel [%d]'s reference" + " relocation symbol.\n", kerninfo->pid); +} + static int __cmd_record(int argc, const char **argv) { int i, counter; @@ -467,6 +513,7 @@ static int __cmd_record(int argc, const char **argv) int child_ready_pipe[2], go_pipe[2]; const bool forks = argc > 0; char buf; + struct kernel_info *kerninfo; page_size = sysconf(_SC_PAGE_SIZE); @@ -635,21 +682,31 @@ static int __cmd_record(int argc, const char **argv) advance_output(err); } + kerninfo = kerninfo__findhost(&session->kerninfo_root); + if (!kerninfo) { + pr_err("Couldn't find native kernel information.\n"); + return -1; + } + err = event__synthesize_kernel_mmap(process_synthesized_event, - session, "_text"); + session, kerninfo, "_text"); if (err < 0) err = event__synthesize_kernel_mmap(process_synthesized_event, - session, "_stext"); + session, kerninfo, "_stext"); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } - err = event__synthesize_modules(process_synthesized_event, session); + err = event__synthesize_modules(process_synthesized_event, + session, kerninfo); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } + if (perf_guest) + kerninfo__process_allkernels(&session->kerninfo_root, + event__synthesize_guest_os, session); if (!system_wide && profile_cpu == -1) event__synthesize_thread(target_tid, process_synthesized_event, diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 7da5fb3..816edae 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -108,7 +108,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return -ENOMEM; if (hit) - he->count += data->period; + __perf_session__add_count(he, al, data->period); if (symbol_conf.use_callchain) { if (!hit) @@ -313,7 +313,7 @@ static int __cmd_report(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(stdout); + dsos__fprintf(&session->kerninfo_root, stdout); next = rb_first(&session->stats_by_id); while (next) { @@ -450,6 +450,8 @@ static const struct option options[] = { "sort by key(s): pid, comm, dso, symbol, parent"), OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, "Don't shorten the pathnames taking into account the cwd"), + OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, + "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", "regex filter to identify parent, see: '--sort parent'"), OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 40f24dd..dfd7ea7 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -420,8 +420,9 @@ static double sym_weight(const struct sym_entry *sym) } static long samples; -static long userspace_samples; +static long kernel_samples, us_samples; static long exact_samples; +static long guest_us_samples, guest_kernel_samples; static const char CONSOLE_CLEAR[] = ""; static void __list_insert_active_sym(struct sym_entry *syme) @@ -461,7 +462,10 @@ static void print_sym_table(void) int printed = 0, j; int counter, snap = !display_weighted ? sym_counter : 0; float samples_per_sec = samples/delay_secs; - float ksamples_per_sec = (samples-userspace_samples)/delay_secs; + float ksamples_per_sec = kernel_samples/delay_secs; + float us_samples_per_sec = (us_samples)/delay_secs; + float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; + float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; float esamples_percent = (100.0*exact_samples)/samples; float sum_ksamples = 0.0; struct sym_entry *syme, *n; @@ -470,7 +474,8 @@ static void print_sym_table(void) int sym_width = 0, dso_width = 0, dso_short_width = 0; const int win_width = winsize.ws_col - 1; - samples = userspace_samples = exact_samples = 0; + samples = us_samples = kernel_samples = exact_samples = 0; + guest_kernel_samples = guest_us_samples = 0; /* Sort the active symbols */ pthread_mutex_lock(&active_symbols_lock); @@ -501,10 +506,30 @@ static void print_sym_table(void) puts(CONSOLE_CLEAR); printf("%-*.*s\n", win_width, win_width, graph_dotted_line); - printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% exact: %4.1f%% [", - samples_per_sec, - 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)), - esamples_percent); + if (!perf_guest) { + printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" + " exact: %4.1f%% [", + samples_per_sec, + 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / + samples_per_sec)), + esamples_percent); + } else { + printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" + " guest kernel:%4.1f%% guest us:%4.1f%%" + " exact: %4.1f%% [", + samples_per_sec, + 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec - + guest_kernel_samples_per_sec) / + samples_per_sec)), + 100.0 - (100.0 * ((samples_per_sec - + guest_us_samples_per_sec) / + samples_per_sec)), + esamples_percent); + } if (nr_counters == 1 || !display_weighted) { printf("%Ld", (u64)attrs[0].sample_period); @@ -597,7 +622,6 @@ static void print_sym_table(void) syme = rb_entry(nd, struct sym_entry, rb_node); sym = sym_entry__symbol(syme); - if (++printed > print_entries || (int)syme->snap_count < count_filter) continue; @@ -761,7 +785,7 @@ static int key_mapped(int c) return 0; } -static void handle_keypress(int c) +static void handle_keypress(struct perf_session *session, int c) { if (!key_mapped(c)) { struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; @@ -830,7 +854,7 @@ static void handle_keypress(int c) case 'Q': printf("exiting.\n"); if (dump_symtab) - dsos__fprintf(stderr); + dsos__fprintf(&session->kerninfo_root, stderr); exit(0); case 's': prompt_symbol(&sym_filter_entry, "Enter details symbol"); @@ -866,6 +890,7 @@ static void *display_thread(void *arg __used) struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct termios tc, save; int delay_msecs, c; + struct perf_session *session = (struct perf_session *) arg; tcgetattr(0, &save); tc = save; @@ -886,7 +911,7 @@ repeat: c = getc(stdin); tcsetattr(0, TCSAFLUSH, &save); - handle_keypress(c); + handle_keypress(session, c); goto repeat; return NULL; @@ -957,24 +982,46 @@ static void event__process_sample(const event_t *self, u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; + struct kernel_info *kerninfo; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; ++samples; switch (origin) { case PERF_RECORD_MISC_USER: - ++userspace_samples; + ++us_samples; if (hide_user_symbols) return; + kerninfo = kerninfo__findhost(&session->kerninfo_root); break; case PERF_RECORD_MISC_KERNEL: + ++kernel_samples; if (hide_kernel_symbols) return; + kerninfo = kerninfo__findhost(&session->kerninfo_root); break; + case PERF_RECORD_MISC_GUEST_KERNEL: + ++guest_kernel_samples; + kerninfo = kerninfo__find(&session->kerninfo_root, + self->ip.pid); + break; + case PERF_RECORD_MISC_GUEST_USER: + ++guest_us_samples; + /* + * TODO: we don't process guest user from host side + * except simple counting. + */ + return; default: return; } + if (!kerninfo && perf_guest) { + pr_err("Can't find guest [%d]'s kernel information\n", + self->ip.pid); + return; + } + if (self->header.misc & PERF_RECORD_MISC_EXACT) exact_samples++; @@ -994,7 +1041,7 @@ static void event__process_sample(const event_t *self, * --hide-kernel-symbols, even if the user specifies an * invalid --vmlinux ;-) */ - if (al.map == session->vmlinux_maps[MAP__FUNCTION] && + if (al.map == kerninfo->vmlinux_maps[MAP__FUNCTION] && RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { pr_err("The %s file can't be used\n", symbol_conf.vmlinux_name); @@ -1261,7 +1308,7 @@ static int __cmd_top(void) perf_session__mmap_read(session); - if (pthread_create(&thread, NULL, display_thread, NULL)) { + if (pthread_create(&thread, NULL, display_thread, session)) { printf("Could not create display thread.\n"); exit(-1); } diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 10fe49e..ab28bca 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -32,5 +32,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_probe(int argc, const char **argv, const char *prefix); extern int cmd_kmem(int argc, const char **argv, const char *prefix); extern int cmd_lock(int argc, const char **argv, const char *prefix); +extern int cmd_kvm(int argc, const char **argv, const char *prefix); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index db6ee94..2a1162d 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -19,3 +19,4 @@ perf-trace mainporcelain common perf-probe mainporcelain common perf-kmem mainporcelain common perf-lock mainporcelain common +perf-kvm mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index d4be55b..985cdb4 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -307,6 +307,7 @@ static void handle_internal_command(int argc, const char **argv) { "probe", cmd_probe, 0 }, { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, + { "kvm", cmd_kvm, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index ec21274..02821fe 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -131,4 +131,6 @@ struct ip_callchain { u64 ips[0]; }; +extern int perf_host, perf_guest; + #endif diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 04904b3..0f60a390 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -24,7 +24,7 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) } thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - event->ip.ip, &al); + event->ip.pid, event->ip.ip, &al); if (al.map != NULL) al.map->dso->hit = 1; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 571fb25..e3fa8d3 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -112,7 +112,11 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, event_t ev = { .header = { .type = PERF_RECORD_MMAP, - .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ + /* + * Just like the kernel, see __perf_event_mmap + * in kernel/perf_event.c + */ + .misc = PERF_RECORD_MISC_USER, }, }; int n; @@ -167,11 +171,23 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, } int event__synthesize_modules(event__handler_t process, - struct perf_session *session) + struct perf_session *session, + struct kernel_info *kerninfo) { struct rb_node *nd; + struct map_groups *kmaps = &kerninfo->kmaps; + u16 misc; - for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); + /* + * kernel uses 0 for user space maps, see kernel/perf_event.c + * __perf_event_mmap + */ + if (is_host_kernel(kerninfo)) + misc = PERF_RECORD_MISC_KERNEL; + else + misc = PERF_RECORD_MISC_GUEST_KERNEL; + + for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { event_t ev; size_t size; @@ -182,12 +198,13 @@ int event__synthesize_modules(event__handler_t process, size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); memset(&ev, 0, sizeof(ev)); - ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ + ev.mmap.header.misc = misc; ev.mmap.header.type = PERF_RECORD_MMAP; ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); ev.mmap.start = pos->start; ev.mmap.len = pos->end - pos->start; + ev.mmap.pid = kerninfo->pid; memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); @@ -250,13 +267,18 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, + struct kernel_info *kerninfo, const char *symbol_name) { size_t size; + const char *filename, *mmap_name; + char path[PATH_MAX]; + char name_buff[PATH_MAX]; + struct map *map; + event_t ev = { .header = { .type = PERF_RECORD_MMAP, - .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ }, }; /* @@ -266,16 +288,37 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) + mmap_name = kern_mmap_name(kerninfo, name_buff); + if (is_host_kernel(kerninfo)) { + /* + * kernel uses PERF_RECORD_MISC_USER for user space maps, + * see kernel/perf_event.c __perf_event_mmap + */ + ev.header.misc = PERF_RECORD_MISC_KERNEL; + filename = "/proc/kallsyms"; + } else { + ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; + if (is_default_guest(kerninfo)) + filename = (char *) symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + filename = path; + } + } + + if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) return -ENOENT; + map = kerninfo->vmlinux_maps[MAP__FUNCTION]; size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), - "[kernel.kallsyms.%s]", symbol_name) + 1; + "%s%s", mmap_name, symbol_name) + 1; size = ALIGN(size, sizeof(u64)); - ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); + ev.mmap.header.size = (sizeof(ev.mmap) - + (sizeof(ev.mmap.filename) - size)); ev.mmap.pgoff = args.start; - ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; - ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; + ev.mmap.start = map->start; + ev.mmap.len = map->end - ev.mmap.start; + ev.mmap.pid = kerninfo->pid; return process(&ev, session); } @@ -329,22 +372,50 @@ int event__process_lost(event_t *self, struct perf_session *session) return 0; } -int event__process_mmap(event_t *self, struct perf_session *session) +static void event_set_kernel_mmap_len(struct map **maps, event_t *self) +{ + maps[MAP__FUNCTION]->start = self->mmap.start; + maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; + /* + * Be a bit paranoid here, some perf.data file came with + * a zero sized synthesized MMAP event for the kernel. + */ + if (maps[MAP__FUNCTION]->end == 0) + maps[MAP__FUNCTION]->end = ~0UL; +} + +static int event__process_kernel_mmap(event_t *self, + struct perf_session *session) { - struct thread *thread; struct map *map; + char kmmap_prefix[PATH_MAX]; + struct kernel_info *kerninfo; + enum dso_kernel_type kernel_type; + bool is_kernel_mmap; + + kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid); + if (!kerninfo) { + pr_err("Can't find id %d's kerninfo\n", self->mmap.pid); + goto out_problem; + } - dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", - self->mmap.pid, self->mmap.tid, self->mmap.start, - self->mmap.len, self->mmap.pgoff, self->mmap.filename); + kern_mmap_name(kerninfo, kmmap_prefix); + if (is_host_kernel(kerninfo)) + kernel_type = DSO_TYPE_KERNEL; + else + kernel_type = DSO_TYPE_GUEST_KERNEL; - if (self->mmap.pid == 0) { - static const char kmmap_prefix[] = "[kernel.kallsyms."; + is_kernel_mmap = memcmp(self->mmap.filename, + kmmap_prefix, + strlen(kmmap_prefix)) == 0; + if (self->mmap.filename[0] == '/' || + (!is_kernel_mmap && self->mmap.filename[0] == '[')) { - if (self->mmap.filename[0] == '/') { - char short_module_name[1024]; - char *name = strrchr(self->mmap.filename, '/'), *dot; + char short_module_name[1024]; + char *name, *dot; + if (self->mmap.filename[0] == '/') { + name = strrchr(self->mmap.filename, '/'); if (name == NULL) goto out_problem; @@ -352,59 +423,86 @@ int event__process_mmap(event_t *self, struct perf_session *session) dot = strrchr(name, '.'); if (dot == NULL) goto out_problem; - snprintf(short_module_name, sizeof(short_module_name), - "[%.*s]", (int)(dot - name), name); + "[%.*s]", (int)(dot - name), name); strxfrchar(short_module_name, '-', '_'); - - map = perf_session__new_module_map(session, - self->mmap.start, - self->mmap.filename); - if (map == NULL) - goto out_problem; - - name = strdup(short_module_name); - if (name == NULL) - goto out_problem; - - map->dso->short_name = name; - map->end = map->start + self->mmap.len; - } else if (memcmp(self->mmap.filename, kmmap_prefix, - sizeof(kmmap_prefix) - 1) == 0) { - const char *symbol_name = (self->mmap.filename + - sizeof(kmmap_prefix) - 1); + } else + strcpy(short_module_name, self->mmap.filename); + + map = map_groups__new_module(&kerninfo->kmaps, + self->mmap.start, + self->mmap.filename, + kerninfo); + if (map == NULL) + goto out_problem; + + name = strdup(short_module_name); + if (name == NULL) + goto out_problem; + + map->dso->short_name = name; + map->end = map->start + self->mmap.len; + } else if (is_kernel_mmap) { + const char *symbol_name = (self->mmap.filename + + strlen(kmmap_prefix)); + /* + * Should be there already, from the build-id table in + * the header. + */ + struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel, + kmmap_prefix); + if (kernel == NULL) + goto out_problem; + + kernel->kernel = kernel_type; + if (__map_groups__create_kernel_maps(&kerninfo->kmaps, + kerninfo->vmlinux_maps, kernel) < 0) + goto out_problem; + + event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self); + perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps, + symbol_name, + self->mmap.pgoff); + if (is_default_guest(kerninfo)) { /* - * Should be there already, from the build-id table in - * the header. + * preload dso of guest kernel and modules */ - struct dso *kernel = __dsos__findnew(&dsos__kernel, - "[kernel.kallsyms]"); - if (kernel == NULL) - goto out_problem; - - kernel->kernel = 1; - if (__perf_session__create_kernel_maps(session, kernel) < 0) - goto out_problem; + dso__load(kernel, + kerninfo->vmlinux_maps[MAP__FUNCTION], + NULL); + } + } + return 0; +out_problem: + return -1; +} - session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; - session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; - /* - * Be a bit paranoid here, some perf.data file came with - * a zero sized synthesized MMAP event for the kernel. - */ - if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) - session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; +int event__process_mmap(event_t *self, struct perf_session *session) +{ + struct kernel_info *kerninfo; + struct thread *thread; + struct map *map; + u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + int ret = 0; - perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, - self->mmap.pgoff); - } + dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", + self->mmap.pid, self->mmap.tid, self->mmap.start, + self->mmap.len, self->mmap.pgoff, self->mmap.filename); + + if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || + cpumode == PERF_RECORD_MISC_KERNEL) { + ret = event__process_kernel_mmap(self, session); + if (ret < 0) + goto out_problem; return 0; } thread = perf_session__findnew(session, self->mmap.pid); - map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, - self->mmap.pid, self->mmap.filename, MAP__FUNCTION, - session->cwd, session->cwdlen); + kerninfo = kerninfo__findhost(&session->kerninfo_root); + map = map__new(&kerninfo->dsos__user, self->mmap.start, + self->mmap.len, self->mmap.pgoff, + self->mmap.pid, self->mmap.filename, + MAP__FUNCTION, session->cwd, session->cwdlen); if (thread == NULL || map == NULL) goto out_problem; @@ -444,22 +542,52 @@ int event__process_task(event_t *self, struct perf_session *session) void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al) { struct map_groups *mg = &self->mg; + struct kernel_info *kerninfo = NULL; al->thread = self; al->addr = addr; + al->cpumode = cpumode; + al->filtered = false; - if (cpumode == PERF_RECORD_MISC_KERNEL) { + if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; - mg = &session->kmaps; - } else if (cpumode == PERF_RECORD_MISC_USER) + kerninfo = kerninfo__findhost(&session->kerninfo_root); + mg = &kerninfo->kmaps; + } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; - else { - al->level = 'H'; + kerninfo = kerninfo__findhost(&session->kerninfo_root); + } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { + al->level = 'g'; + kerninfo = kerninfo__find(&session->kerninfo_root, pid); + if (!kerninfo) { + al->map = NULL; + return; + } + mg = &kerninfo->kmaps; + } else { + /* + * 'u' means guest os user space. + * TODO: We don't support guest user space. Might support late. + */ + if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) + al->level = 'u'; + else + al->level = 'H'; al->map = NULL; + + if ((cpumode == PERF_RECORD_MISC_GUEST_USER || + cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && + !perf_guest) + al->filtered = true; + if ((cpumode == PERF_RECORD_MISC_USER || + cpumode == PERF_RECORD_MISC_KERNEL) && + !perf_host) + al->filtered = true; + return; } try_again: @@ -474,8 +602,11 @@ try_again: * "[vdso]" dso, but for now lets use the old trick of looking * in the whole kernel symbol list. */ - if ((long long)al->addr < 0 && mg != &session->kmaps) { - mg = &session->kmaps; + if ((long long)al->addr < 0 && + cpumode == PERF_RECORD_MISC_KERNEL && + kerninfo && + mg != &kerninfo->kmaps) { + mg = &kerninfo->kmaps; goto try_again; } } else @@ -484,11 +615,11 @@ try_again: void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al, symbol_filter_t filter) { - thread__find_addr_map(self, session, cpumode, type, addr, al); + thread__find_addr_map(self, session, cpumode, type, pid, addr, al); if (al->map != NULL) al->sym = map__find_symbol(al->map, al->addr, filter); else @@ -524,7 +655,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, - self->ip.ip, al); + self->ip.pid, self->ip.ip, al); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : ""); @@ -554,7 +685,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) goto out_filtered; - al->filtered = false; return 0; out_filtered: diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index e5740ea..4af2ed5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -79,6 +79,7 @@ struct sample_data { struct build_id_event { struct perf_event_header header; + pid_t pid; u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; char filename[]; }; @@ -154,10 +155,13 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, void event__synthesize_threads(event__handler_t process, struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, - struct perf_session *session, - const char *symbol_name); + struct perf_session *session, + struct kernel_info *kerninfo, + const char *symbol_name); + int event__synthesize_modules(event__handler_t process, - struct perf_session *session); + struct perf_session *session, + struct kernel_info *kerninfo); int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 628173b..75d0167 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -190,7 +190,8 @@ static int write_padded(int fd, const void *bf, size_t count, continue; \ else -static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) +static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, + u16 misc, int fd) { struct dso *pos; @@ -205,6 +206,7 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) len = ALIGN(len, NAME_ALIGN); memset(&b, 0, sizeof(b)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); + b.pid = pid; b.header.misc = misc; b.header.size = sizeof(b) + len; err = do_write(fd, &b, sizeof(b)); @@ -219,13 +221,33 @@ static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) return 0; } -static int dsos__write_buildid_table(int fd) +static int dsos__write_buildid_table(struct perf_header *header, int fd) { - int err = __dsos__write_buildid_table(&dsos__kernel, - PERF_RECORD_MISC_KERNEL, fd); - if (err == 0) - err = __dsos__write_buildid_table(&dsos__user, - PERF_RECORD_MISC_USER, fd); + struct perf_session *session = container_of(header, + struct perf_session, header); + struct rb_node *nd; + int err = 0; + u16 kmisc, umisc; + + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + if (is_host_kernel(pos)) { + kmisc = PERF_RECORD_MISC_KERNEL; + umisc = PERF_RECORD_MISC_USER; + } else { + kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + umisc = PERF_RECORD_MISC_GUEST_USER; + } + + err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid, + kmisc, fd); + if (err == 0) + err = __dsos__write_buildid_table(&pos->dsos__user, + pos->pid, umisc, fd); + if (err) + break; + } return err; } @@ -342,9 +364,12 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) return err; } -static int dsos__cache_build_ids(void) +static int dsos__cache_build_ids(struct perf_header *self) { - int err_kernel, err_user; + struct perf_session *session = container_of(self, + struct perf_session, header); + struct rb_node *nd; + int ret = 0; char debugdir[PATH_MAX]; snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), @@ -353,9 +378,30 @@ static int dsos__cache_build_ids(void) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; - err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); - err_user = __dsos__cache_build_ids(&dsos__user, debugdir); - return err_kernel || err_user ? -1 : 0; + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir); + ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir); + } + return ret ? -1 : 0; +} + +static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) +{ + bool ret = false; + struct perf_session *session = container_of(self, + struct perf_session, header); + struct rb_node *nd; + + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits); + ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits); + } + + return ret; } static int perf_header__adds_write(struct perf_header *self, int fd) @@ -366,7 +412,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) u64 sec_start; int idx = 0, err; - if (dsos__read_build_ids(true)) + if (dsos__read_build_ids(self, true)) perf_header__set_feat(self, HEADER_BUILD_ID); nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); @@ -401,14 +447,14 @@ static int perf_header__adds_write(struct perf_header *self, int fd) /* Write build-ids */ buildid_sec->offset = lseek(fd, 0, SEEK_CUR); - err = dsos__write_buildid_table(fd); + err = dsos__write_buildid_table(self, fd); if (err < 0) { pr_debug("failed to write buildid table\n"); goto out_free; } buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; - dsos__cache_build_ids(); + dsos__cache_build_ids(self); } lseek(fd, sec_start, SEEK_SET); @@ -633,6 +679,85 @@ int perf_file_header__read(struct perf_file_header *self, return 0; } +static int __event_process_build_id(struct build_id_event *bev, + char *filename, + struct perf_session *session) +{ + int err = -1; + struct list_head *head; + struct kernel_info *kerninfo; + u16 misc; + struct dso *dso; + enum dso_kernel_type dso_type; + + kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid); + if (!kerninfo) + goto out; + + misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + switch (misc) { + case PERF_RECORD_MISC_KERNEL: + dso_type = DSO_TYPE_KERNEL; + head = &kerninfo->dsos__kernel; + break; + case PERF_RECORD_MISC_GUEST_KERNEL: + dso_type = DSO_TYPE_GUEST_KERNEL; + head = &kerninfo->dsos__kernel; + break; + case PERF_RECORD_MISC_USER: + case PERF_RECORD_MISC_GUEST_USER: + dso_type = DSO_TYPE_USER; + head = &kerninfo->dsos__user; + break; + default: + goto out; + } + + dso = __dsos__findnew(head, filename); + if (dso != NULL) { + dso__set_build_id(dso, &bev->build_id); + if (filename[0] == '[') + dso->kernel = dso_type; + } + + err = 0; +out: + return err; +} + +static int perf_header__read_build_ids(struct perf_header *self, + int input, u64 offset, u64 size) +{ + struct perf_session *session = container_of(self, + struct perf_session, header); + struct build_id_event bev; + char filename[PATH_MAX]; + u64 limit = offset + size; + int err = -1; + + while (offset < limit) { + ssize_t len; + + if (read(input, &bev, sizeof(bev)) != sizeof(bev)) + goto out; + + if (self->needs_swap) + perf_event_header__bswap(&bev.header); + + len = bev.header.size - sizeof(bev); + if (read(input, filename, len) != len) + goto out; + + __event_process_build_id(&bev, filename, session); + + offset += bev.header.size; + } + err = 0; +out: + return err; +} + static int perf_file_section__process(struct perf_file_section *self, struct perf_header *ph, int feat, int fd) @@ -989,6 +1114,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session) { event_t ev; @@ -1005,6 +1131,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc; + ev.build_id.pid = kerninfo->pid; ev.build_id.header.size = sizeof(ev.build_id) + len; memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); @@ -1015,6 +1142,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, static int __event_synthesize_build_ids(struct list_head *head, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session) { struct dso *pos; @@ -1024,7 +1152,8 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, if (!pos->hit) continue; - err = event__synthesize_build_id(pos, misc, process, session); + err = event__synthesize_build_id(pos, misc, process, + kerninfo, session); if (err < 0) return err; } @@ -1035,44 +1164,48 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, int event__synthesize_build_ids(event__handler_t process, struct perf_session *session) { - int err; + int err = 0; + u16 kmisc, umisc; + struct kernel_info *pos; + struct rb_node *nd; - if (!dsos__read_build_ids(true)) + if (!dsos__read_build_ids(&session->header, true)) return 0; - err = __event_synthesize_build_ids(&dsos__kernel, - PERF_RECORD_MISC_KERNEL, - process, session); - if (err == 0) - err = __event_synthesize_build_ids(&dsos__user, - PERF_RECORD_MISC_USER, - process, session); + for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { + pos = rb_entry(nd, struct kernel_info, rb_node); + if (is_host_kernel(pos)) { + kmisc = PERF_RECORD_MISC_KERNEL; + umisc = PERF_RECORD_MISC_USER; + } else { + kmisc = PERF_RECORD_MISC_GUEST_KERNEL; + umisc = PERF_RECORD_MISC_GUEST_USER; + } + + err = __event_synthesize_build_ids(&pos->dsos__kernel, + kmisc, process, pos, session); + if (err == 0) + err = __event_synthesize_build_ids(&pos->dsos__user, + umisc, process, pos, session); + if (err) + break; + } if (err < 0) { pr_debug("failed to synthesize build ids\n"); return err; } - dsos__cache_build_ids(); + dsos__cache_build_ids(&session->header); return 0; } int event__process_build_id(event_t *self, - struct perf_session *session __unused) + struct perf_session *session) { - struct list_head *head = &dsos__user; - struct dso *dso; - - if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL) - head = &dsos__kernel; - - dso = __dsos__findnew(head, self->build_id.filename); - if (dso != NULL) { - dso__set_build_id(dso, &self->build_id.build_id); - if (head == &dsos__kernel && self->build_id.filename[0] == '[') - dso->kernel = 1; - } - + __event_process_build_id(&self->build_id, + self->build_id.filename, + session); return 0; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 4214e23..2759154 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -120,6 +120,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, + struct kernel_info *kerninfo, struct perf_session *session); int event__synthesize_build_ids(event__handler_t process, struct perf_session *session); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9c2b874..ad6b22d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,6 +8,30 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; +void __perf_session__add_count(struct hist_entry *he, + struct addr_location *al, + u64 count) +{ + he->count += count; + + switch (al->cpumode) { + case PERF_RECORD_MISC_KERNEL: + he->count_sys += count; + break; + case PERF_RECORD_MISC_USER: + he->count_us += count; + break; + case PERF_RECORD_MISC_GUEST_KERNEL: + he->count_guest_sys += count; + break; + case PERF_RECORD_MISC_GUEST_USER: + he->count_guest_us += count; + break; + default: + break; + } +} + /* * histogram, sorted on item, collects counts */ @@ -464,7 +488,7 @@ int hist_entry__snprintf(struct hist_entry *self, u64 session_total) { struct sort_entry *se; - u64 count, total; + u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; const char *sep = symbol_conf.field_sep; int ret; @@ -474,9 +498,17 @@ int hist_entry__snprintf(struct hist_entry *self, if (pair_session) { count = self->pair ? self->pair->count : 0; total = pair_session->events_stats.total; + count_sys = self->pair ? self->pair->count_sys : 0; + count_us = self->pair ? self->pair->count_us : 0; + count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; + count_guest_us = self->pair ? self->pair->count_guest_us : 0; } else { count = self->count; total = session_total; + count_sys = self->count_sys; + count_us = self->count_us; + count_guest_sys = self->count_guest_sys; + count_guest_us = self->count_guest_us; } if (total) { @@ -487,6 +519,26 @@ int hist_entry__snprintf(struct hist_entry *self, else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", (count * 100.0) / total); + if (symbol_conf.show_cpu_utilization) { + ret += percent_color_snprintf(s + ret, size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_sys * 100.0) / total); + ret += percent_color_snprintf(s + ret, size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_us * 100.0) / total); + if (perf_guest) { + ret += percent_color_snprintf(s + ret, + size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_guest_sys * 100.0) / + total); + ret += percent_color_snprintf(s + ret, + size - ret, + sep ? "%.2f" : " %6.2f%%", + (count_guest_us * 100.0) / + total); + } + } } else ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); @@ -597,6 +649,24 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fputs(" Samples ", fp); } + if (symbol_conf.show_cpu_utilization) { + if (sep) { + ret += fprintf(fp, "%csys", *sep); + ret += fprintf(fp, "%cus", *sep); + if (perf_guest) { + ret += fprintf(fp, "%cguest sys", *sep); + ret += fprintf(fp, "%cguest us", *sep); + } + } else { + ret += fprintf(fp, " sys "); + ret += fprintf(fp, " us "); + if (perf_guest) { + ret += fprintf(fp, " guest sys "); + ret += fprintf(fp, " guest us "); + } + } + } + if (pair) { if (sep) ret += fprintf(fp, "%cDelta", *sep); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ad17f0a..9df1c34 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -12,6 +12,9 @@ struct addr_location; struct symbol; struct rb_root; +void __perf_session__add_count(struct hist_entry *he, + struct addr_location *al, + u64 count); struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *parent, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 37913b2..7facd01 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "map.h" const char *map_type__name[MAP__NR_TYPES] = { @@ -37,9 +38,11 @@ void map__init(struct map *self, enum map_type type, self->map_ip = map__map_ip; self->unmap_ip = map__unmap_ip; RB_CLEAR_NODE(&self->rb_node); + self->groups = NULL; } -struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, +struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, + u64 pgoff, u32 pid, char *filename, enum map_type type, char *cwd, int cwdlen) { struct map *self = malloc(sizeof(*self)); @@ -66,7 +69,7 @@ struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, filename = newfilename; } - dso = dsos__findnew(filename); + dso = __dsos__findnew(dsos__list, filename); if (dso == NULL) goto out_delete; @@ -242,6 +245,7 @@ void map_groups__init(struct map_groups *self) self->maps[i] = RB_ROOT; INIT_LIST_HEAD(&self->removed_maps[i]); } + self->this_kerninfo = NULL; } void map_groups__flush(struct map_groups *self) @@ -508,3 +512,134 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } + +struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, + pid_t pid, const char *root_dir) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo, *pos; + + kerninfo = malloc(sizeof(struct kernel_info)); + if (!kerninfo) + return NULL; + + kerninfo->pid = pid; + map_groups__init(&kerninfo->kmaps); + kerninfo->root_dir = strdup(root_dir); + RB_CLEAR_NODE(&kerninfo->rb_node); + INIT_LIST_HEAD(&kerninfo->dsos__user); + INIT_LIST_HEAD(&kerninfo->dsos__kernel); + kerninfo->kmaps.this_kerninfo = kerninfo; + + while (*p != NULL) { + parent = *p; + pos = rb_entry(parent, struct kernel_info, rb_node); + if (pid < pos->pid) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&kerninfo->rb_node, parent, p); + rb_insert_color(&kerninfo->rb_node, kerninfo_root); + + return kerninfo; +} + +struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo; + struct kernel_info *default_kerninfo = NULL; + + while (*p != NULL) { + parent = *p; + kerninfo = rb_entry(parent, struct kernel_info, rb_node); + if (pid < kerninfo->pid) + p = &(*p)->rb_left; + else if (pid > kerninfo->pid) + p = &(*p)->rb_right; + else + return kerninfo; + if (!kerninfo->pid) + default_kerninfo = kerninfo; + } + + return default_kerninfo; +} + +struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root) +{ + struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node *parent = NULL; + struct kernel_info *kerninfo; + pid_t pid = HOST_KERNEL_ID; + + while (*p != NULL) { + parent = *p; + kerninfo = rb_entry(parent, struct kernel_info, rb_node); + if (pid < kerninfo->pid) + p = &(*p)->rb_left; + else if (pid > kerninfo->pid) + p = &(*p)->rb_right; + else + return kerninfo; + } + + return NULL; +} + +struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid) +{ + char path[PATH_MAX]; + const char *root_dir; + int ret; + struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid); + + if (!kerninfo || kerninfo->pid != pid) { + if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) + root_dir = ""; + else { + if (!symbol_conf.guestmount) + goto out; + sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + ret = access(path, R_OK); + if (ret) { + pr_err("Can't access file %s\n", path); + goto out; + } + root_dir = path; + } + kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir); + } + +out: + return kerninfo; +} + +void kerninfo__process_allkernels(struct rb_root *kerninfo_root, + process_kernel_info process, + void *data) +{ + struct rb_node *nd; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + process(pos, data); + } +} + +char *kern_mmap_name(struct kernel_info *kerninfo, char *buff) +{ + if (is_host_kernel(kerninfo)) + sprintf(buff, "[%s]", "kernel.kallsyms"); + else if (is_default_guest(kerninfo)) + sprintf(buff, "[%s]", "guest.kernel.kallsyms"); + else + sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid); + + return buff; +} diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2031278..30d38d6 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -19,6 +19,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; struct dso; struct ref_reloc_sym; struct map_groups; +struct kernel_info; struct map { union { @@ -36,6 +37,7 @@ struct map { u64 (*unmap_ip)(struct map *, u64); struct dso *dso; + struct map_groups *groups; }; struct kmap { @@ -43,6 +45,26 @@ struct kmap { struct map_groups *kmaps; }; +struct map_groups { + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; + struct kernel_info *this_kerninfo; +}; + +/* Native host kernel uses -1 as pid index in kernel_info */ +#define HOST_KERNEL_ID (-1) +#define DEFAULT_GUEST_KERNEL_ID (0) + +struct kernel_info { + struct rb_node rb_node; + pid_t pid; + char *root_dir; + struct list_head dsos__user; + struct list_head dsos__kernel; + struct map_groups kmaps; + struct map *vmlinux_maps[MAP__NR_TYPES]; +}; + static inline struct kmap *map__kmap(struct map *self) { return (struct kmap *)(self + 1); @@ -74,7 +96,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); -struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, +struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, + u64 pgoff, u32 pid, char *filename, enum map_type type, char *cwd, int cwdlen); void map__delete(struct map *self); struct map *map__clone(struct map *self); @@ -91,11 +114,6 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); -struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; -}; - size_t __map_groups__fprintf_maps(struct map_groups *self, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); @@ -106,9 +124,40 @@ int map_groups__clone(struct map_groups *self, size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); +struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, + pid_t pid, const char *root_dir); +struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid); +struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid); +struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root); +char *kern_mmap_name(struct kernel_info *kerninfo, char *buff); + +/* + * Default guest kernel is defined by parameter --guestkallsyms + * and --guestmodules + */ +static inline int is_default_guest(struct kernel_info *kerninfo) +{ + if (!kerninfo) + return 0; + return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID; +} + +static inline int is_host_kernel(struct kernel_info *kerninfo) +{ + if (!kerninfo) + return 0; + return kerninfo->pid == HOST_KERNEL_ID; +} + +typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data); +void kerninfo__process_allkernels(struct rb_root *kerninfo_root, + process_kernel_info process, + void *data); + static inline void map_groups__insert(struct map_groups *self, struct map *map) { - maps__insert(&self->maps[map->type], map); + maps__insert(&self->maps[map->type], map); + map->groups = self; } static inline struct map *map_groups__find(struct map_groups *self, @@ -148,13 +197,11 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel); -int map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES]); -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename); +struct map *map_groups__new_module(struct map_groups *self, + u64 start, + const char *filename, + struct kernel_info *kerninfo); + void map_groups__flush(struct map_groups *self); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5bf8ab0..3967f8f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES]; /* Initialize symbol maps and path of vmlinux */ static int init_vmlinux(void) { + struct dso *kernel; int ret; symbol_conf.sort_by_name = true; @@ -91,8 +92,12 @@ static int init_vmlinux(void) goto out; } + kernel = dso__new_kernel(symbol_conf.vmlinux_name); + if (kernel == NULL) + die("Failed to create kernel dso."); + map_groups__init(&kmap_groups); - ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); + ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); if (ret < 0) pr_debug("Failed to create kernel maps.\n"); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0fdf3eb..7d88ae5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -67,6 +67,17 @@ void perf_session__update_sample_type(struct perf_session *self) self->sample_type = perf_header__sample_type(&self->header); } +int perf_session__create_kernel_maps(struct perf_session *self) +{ + int ret; + struct rb_root *root = &self->kerninfo_root; + + ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); + if (ret >= 0) + ret = map_groups__create_guest_kernel_maps(root); + return ret; +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -86,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwd = NULL; self->cwdlen = 0; self->unknown_events = 0; - map_groups__init(&self->kmaps); + self->kerninfo_root = RB_ROOT; if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -157,8 +168,9 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, continue; } + al.filtered = false; thread__find_addr_location(thread, self, cpumode, - MAP__FUNCTION, ip, &al, NULL); + MAP__FUNCTION, thread->pid, ip, &al, NULL); if (al.sym != NULL) { if (sort__has_parent && !*parent && symbol__match_parent_regex(al.sym)) @@ -399,46 +411,6 @@ void perf_event_header__bswap(struct perf_event_header *self) self->size = bswap_16(self->size); } -int perf_header__read_build_ids(struct perf_header *self, - int input, u64 offset, u64 size) -{ - struct build_id_event bev; - char filename[PATH_MAX]; - u64 limit = offset + size; - int err = -1; - - while (offset < limit) { - struct dso *dso; - ssize_t len; - struct list_head *head = &dsos__user; - - if (read(input, &bev, sizeof(bev)) != sizeof(bev)) - goto out; - - if (self->needs_swap) - perf_event_header__bswap(&bev.header); - - len = bev.header.size - sizeof(bev); - if (read(input, filename, len) != len) - goto out; - - if (bev.header.misc & PERF_RECORD_MISC_KERNEL) - head = &dsos__kernel; - - dso = __dsos__findnew(head, filename); - if (dso != NULL) { - dso__set_build_id(dso, &bev.build_id); - if (head == &dsos__kernel && filename[0] == '[') - dso->kernel = 1; - } - - offset += bev.header.size; - } - err = 0; -out: - return err; -} - static struct thread *perf_session__register_idle_thread(struct perf_session *self) { struct thread *thread = perf_session__findnew(self, 0); @@ -690,26 +662,33 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg) return true; } -int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr) { char *bracket; enum map_type i; + struct ref_reloc_sym *ref; + + ref = zalloc(sizeof(struct ref_reloc_sym)); + if (ref == NULL) + return -ENOMEM; - self->ref_reloc_sym.name = strdup(symbol_name); - if (self->ref_reloc_sym.name == NULL) + ref->name = strdup(symbol_name); + if (ref->name == NULL) { + free(ref); return -ENOMEM; + } - bracket = strchr(self->ref_reloc_sym.name, ']'); + bracket = strchr(ref->name, ']'); if (bracket) *bracket = '\0'; - self->ref_reloc_sym.addr = addr; + ref->addr = addr; for (i = 0; i < MAP__NR_TYPES; ++i) { - struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); - kmap->ref_reloc_sym = &self->ref_reloc_sym; + struct kmap *kmap = map__kmap(maps[i]); + kmap->ref_reloc_sym = ref; } return 0; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0ac14d4..5e47c87 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -15,17 +15,15 @@ struct perf_session { struct perf_header header; unsigned long size; unsigned long mmap_window; - struct map_groups kmaps; struct rb_root threads; struct thread *last_match; - struct map *vmlinux_maps[MAP__NR_TYPES]; + struct rb_root kerninfo_root; struct events_stats events_stats; struct rb_root stats_by_id; unsigned long event_total[PERF_RECORD_MAX]; unsigned long unknown_events; struct rb_root hists; u64 sample_type; - struct ref_reloc_sym ref_reloc_sym; int fd; bool fd_pipe; int cwdlen; @@ -69,33 +67,13 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, bool perf_session__has_traces(struct perf_session *self, const char *msg); -int perf_header__read_build_ids(struct perf_header *self, int input, - u64 offset, u64 file_size); - -int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, +int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr); void mem_bswap_64(void *src, int byte_size); -static inline int __perf_session__create_kernel_maps(struct perf_session *self, - struct dso *kernel) -{ - return __map_groups__create_kernel_maps(&self->kmaps, - self->vmlinux_maps, kernel); -} - -static inline int perf_session__create_kernel_maps(struct perf_session *self) -{ - return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); -} - -static inline struct map * - perf_session__new_module_map(struct perf_session *self, - u64 start, const char *filename) -{ - return map_groups__new_module(&self->kmaps, start, filename); -} +int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 1d857aa..b7c54ee 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -44,6 +44,11 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; u64 count; + u64 count_sys; + u64 count_us; + u64 count_guest_sys; + u64 count_guest_us; + /* * XXX WARNING! * thread _has_ to come after ms, see diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f3d4151..e782e7d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -28,6 +28,8 @@ static void dsos__add(struct list_head *head, struct dso *dso); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); static int dso__load_kernel_sym(struct dso *self, struct map *map, symbol_filter_t filter); +static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, + symbol_filter_t filter); static int vmlinux_path__nr_entries; static char **vmlinux_path; @@ -186,6 +188,7 @@ struct dso *dso__new(const char *name) self->loaded = 0; self->sorted_by_name = 0; self->has_build_id = 0; + self->kernel = DSO_TYPE_USER; } return self; @@ -402,12 +405,9 @@ int kallsyms__parse(const char *filename, void *arg, char *symbol_name; line_len = getline(&line, &n, file); - if (line_len < 0) + if (line_len < 0 || !line) break; - if (!line) - goto out_failure; - line[--line_len] = '\0'; /* \n */ len = hex2u64(line, &start); @@ -459,6 +459,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, * map__split_kallsyms, when we have split the maps per module */ symbols__insert(root, sym); + return 0; } @@ -483,6 +484,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; + struct kernel_info *kerninfo = kmaps->this_kerninfo; struct map *curr_map = map; struct symbol *pos; int count = 0; @@ -504,15 +506,33 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, *module++ = '\0'; if (strcmp(curr_map->dso->short_name, module)) { - curr_map = map_groups__find_by_name(kmaps, map->type, module); + if (curr_map != map && + self->kernel == DSO_TYPE_GUEST_KERNEL && + is_default_guest(kerninfo)) { + /* + * We assume all symbols of a module are + * continuous in * kallsyms, so curr_map + * points to a module and all its + * symbols are in its kmap. Mark it as + * loaded. + */ + dso__set_loaded(curr_map->dso, + curr_map->type); + } + + curr_map = map_groups__find_by_name(kmaps, + map->type, module); if (curr_map == NULL) { - pr_debug("/proc/{kallsyms,modules} " + pr_err("%s/proc/{kallsyms,modules} " "inconsistency while looking " - "for \"%s\" module!\n", module); - return -1; + "for \"%s\" module!\n", + kerninfo->root_dir, module); + curr_map = map; + goto discard_symbol; } - if (curr_map->dso->loaded) + if (curr_map->dso->loaded && + !is_default_guest(kmaps->this_kerninfo)) goto discard_symbol; } /* @@ -525,13 +545,21 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, char dso_name[PATH_MAX]; struct dso *dso; - snprintf(dso_name, sizeof(dso_name), "[kernel].%d", - kernel_range++); + if (self->kernel == DSO_TYPE_GUEST_KERNEL) + snprintf(dso_name, sizeof(dso_name), + "[guest.kernel].%d", + kernel_range++); + else + snprintf(dso_name, sizeof(dso_name), + "[kernel].%d", + kernel_range++); dso = dso__new(dso_name); if (dso == NULL) return -1; + dso->kernel = self->kernel; + curr_map = map__new2(pos->start, dso, map->type); if (curr_map == NULL) { dso__delete(dso); @@ -555,6 +583,12 @@ discard_symbol: rb_erase(&pos->rb_node, root); } } + if (curr_map != map && + self->kernel == DSO_TYPE_GUEST_KERNEL && + is_default_guest(kmaps->this_kerninfo)) { + dso__set_loaded(curr_map->dso, curr_map->type); + } + return count; } @@ -565,7 +599,10 @@ int dso__load_kallsyms(struct dso *self, const char *filename, return -1; symbols__fixup_end(&self->symbols[map->type]); - self->origin = DSO__ORIG_KERNEL; + if (self->kernel == DSO_TYPE_GUEST_KERNEL) + self->origin = DSO__ORIG_GUEST_KERNEL; + else + self->origin = DSO__ORIG_KERNEL; return dso__split_kallsyms(self, map, filter); } @@ -952,7 +989,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, nr_syms = shdr.sh_size / shdr.sh_entsize; memset(&sym, 0, sizeof(sym)); - if (!self->kernel) { + if (self->kernel == DSO_TYPE_USER) { self->adjust_symbols = (ehdr.e_type == ET_EXEC || elf_section_by_name(elf, &ehdr, &shdr, ".gnu.prelink_undo", @@ -984,7 +1021,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, section_name = elf_sec__name(&shdr, secstrs); - if (self->kernel || kmodule) { + if (self->kernel != DSO_TYPE_USER || kmodule) { char dso_name[PATH_MAX]; if (strcmp(section_name, @@ -1011,6 +1048,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, curr_dso = dso__new(dso_name); if (curr_dso == NULL) goto out_elf_end; + curr_dso->kernel = self->kernel; curr_map = map__new2(start, curr_dso, map->type); if (curr_map == NULL) { @@ -1021,7 +1059,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, curr_map->unmap_ip = identity__map_ip; curr_dso->origin = self->origin; map_groups__insert(kmap->kmaps, curr_map); - dsos__add(&dsos__kernel, curr_dso); + dsos__add(&self->node, curr_dso); dso__set_loaded(curr_dso, map->type); } else curr_dso = curr_map->dso; @@ -1083,7 +1121,7 @@ static bool dso__build_id_equal(const struct dso *self, u8 *build_id) return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; } -static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) +bool __dsos__read_build_ids(struct list_head *head, bool with_hits) { bool have_build_id = false; struct dso *pos; @@ -1101,13 +1139,6 @@ static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) return have_build_id; } -bool dsos__read_build_ids(bool with_hits) -{ - bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), - ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); - return kbuildids || ubuildids; -} - /* * Align offset to 4 bytes as needed for note name and descriptor data. */ @@ -1242,6 +1273,8 @@ char dso__symtab_origin(const struct dso *self) [DSO__ORIG_BUILDID] = 'b', [DSO__ORIG_DSO] = 'd', [DSO__ORIG_KMODULE] = 'K', + [DSO__ORIG_GUEST_KERNEL] = 'g', + [DSO__ORIG_GUEST_KMODULE] = 'G', }; if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) @@ -1257,11 +1290,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = -1; int fd; + struct kernel_info *kerninfo; + const char *root_dir; dso__set_loaded(self, map->type); - if (self->kernel) + if (self->kernel == DSO_TYPE_KERNEL) return dso__load_kernel_sym(self, map, filter); + else if (self->kernel == DSO_TYPE_GUEST_KERNEL) + return dso__load_guest_kernel_sym(self, map, filter); + + if (map->groups && map->groups->this_kerninfo) + kerninfo = map->groups->this_kerninfo; + else + kerninfo = NULL; name = malloc(size); if (!name) @@ -1315,6 +1357,13 @@ more: case DSO__ORIG_DSO: snprintf(name, size, "%s", self->long_name); break; + case DSO__ORIG_GUEST_KMODULE: + if (map->groups && map->groups->this_kerninfo) + root_dir = map->groups->this_kerninfo->root_dir; + else + root_dir = ""; + snprintf(name, size, "%s%s", root_dir, self->long_name); + break; default: goto out; @@ -1368,7 +1417,8 @@ struct map *map_groups__find_by_name(struct map_groups *self, return NULL; } -static int dso__kernel_module_get_build_id(struct dso *self) +static int dso__kernel_module_get_build_id(struct dso *self, + const char *root_dir) { char filename[PATH_MAX]; /* @@ -1378,8 +1428,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) const char *name = self->short_name + 1; snprintf(filename, sizeof(filename), - "/sys/module/%.*s/notes/.note.gnu.build-id", - (int)strlen(name - 1), name); + "%s/sys/module/%.*s/notes/.note.gnu.build-id", + root_dir, (int)strlen(name) - 1, name); if (sysfs__read_build_id(filename, self->build_id, sizeof(self->build_id)) == 0) @@ -1388,7 +1438,8 @@ static int dso__kernel_module_get_build_id(struct dso *self) return 0; } -static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) +static int map_groups__set_modules_path_dir(struct map_groups *self, + const char *dir_name) { struct dirent *dent; DIR *dir = opendir(dir_name); @@ -1400,8 +1451,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n while ((dent = readdir(dir)) != NULL) { char path[PATH_MAX]; + struct stat st; + + /*sshfs might return bad dent->d_type, so we have to stat*/ + sprintf(path, "%s/%s", dir_name, dent->d_name); + if (stat(path, &st)) + continue; - if (dent->d_type == DT_DIR) { + if (S_ISDIR(st.st_mode)) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; @@ -1433,7 +1490,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_n if (long_name == NULL) goto failure; dso__set_long_name(map->dso, long_name); - dso__kernel_module_get_build_id(map->dso); + dso__kernel_module_get_build_id(map->dso, ""); } } @@ -1443,16 +1500,46 @@ failure: return -1; } -static int map_groups__set_modules_path(struct map_groups *self) +static char *get_kernel_version(const char *root_dir) { - struct utsname uts; + char version[PATH_MAX]; + FILE *file; + char *name, *tmp; + const char *prefix = "Linux version "; + + sprintf(version, "%s/proc/version", root_dir); + file = fopen(version, "r"); + if (!file) + return NULL; + + version[0] = '\0'; + tmp = fgets(version, sizeof(version), file); + fclose(file); + + name = strstr(version, prefix); + if (!name) + return NULL; + name += strlen(prefix); + tmp = strchr(name, ' '); + if (tmp) + *tmp = '\0'; + + return strdup(name); +} + +static int map_groups__set_modules_path(struct map_groups *self, + const char *root_dir) +{ + char *version; char modules_path[PATH_MAX]; - if (uname(&uts) < 0) + version = get_kernel_version(root_dir); + if (!version) return -1; - snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", - uts.release); + snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", + root_dir, version); + free(version); return map_groups__set_modules_path_dir(self, modules_path); } @@ -1477,11 +1564,13 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) } struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename) + const char *filename, + struct kernel_info *kerninfo) { struct map *map; - struct dso *dso = __dsos__findnew(&dsos__kernel, filename); + struct dso *dso; + dso = __dsos__findnew(&kerninfo->dsos__kernel, filename); if (dso == NULL) return NULL; @@ -1489,21 +1578,37 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, if (map == NULL) return NULL; - dso->origin = DSO__ORIG_KMODULE; + if (is_host_kernel(kerninfo)) + dso->origin = DSO__ORIG_KMODULE; + else + dso->origin = DSO__ORIG_GUEST_KMODULE; map_groups__insert(self, map); return map; } -static int map_groups__create_modules(struct map_groups *self) +static int map_groups__create_modules(struct kernel_info *kerninfo) { char *line = NULL; size_t n; - FILE *file = fopen("/proc/modules", "r"); + FILE *file; struct map *map; + const char *root_dir; + const char *modules; + char path[PATH_MAX]; + + if (is_default_guest(kerninfo)) + modules = symbol_conf.default_guest_modules; + else { + sprintf(path, "%s/proc/modules", kerninfo->root_dir); + modules = path; + } + file = fopen(modules, "r"); if (file == NULL) return -1; + root_dir = kerninfo->root_dir; + while (!feof(file)) { char name[PATH_MAX]; u64 start; @@ -1532,16 +1637,17 @@ static int map_groups__create_modules(struct map_groups *self) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = map_groups__new_module(self, start, name); + map = map_groups__new_module(&kerninfo->kmaps, + start, name, kerninfo); if (map == NULL) goto out_delete_line; - dso__kernel_module_get_build_id(map->dso); + dso__kernel_module_get_build_id(map->dso, root_dir); } free(line); fclose(file); - return map_groups__set_modules_path(self); + return map_groups__set_modules_path(&kerninfo->kmaps, root_dir); out_delete_line: free(line); @@ -1708,8 +1814,57 @@ out_fixup: return err; } -LIST_HEAD(dsos__user); -LIST_HEAD(dsos__kernel); +static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, + symbol_filter_t filter) +{ + int err; + const char *kallsyms_filename = NULL; + struct kernel_info *kerninfo; + char path[PATH_MAX]; + + if (!map->groups) { + pr_debug("Guest kernel map hasn't the point to groups\n"); + return -1; + } + kerninfo = map->groups->this_kerninfo; + + if (is_default_guest(kerninfo)) { + /* + * if the user specified a vmlinux filename, use it and only + * it, reporting errors to the user if it cannot be used. + * Or use file guest_kallsyms inputted by user on commandline + */ + if (symbol_conf.default_guest_vmlinux_name != NULL) { + err = dso__load_vmlinux(self, map, + symbol_conf.default_guest_vmlinux_name, filter); + goto out_try_fixup; + } + + kallsyms_filename = symbol_conf.default_guest_kallsyms; + if (!kallsyms_filename) + return -1; + } else { + sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + kallsyms_filename = path; + } + + err = dso__load_kallsyms(self, kallsyms_filename, map, filter); + if (err > 0) + pr_debug("Using %s for symbols\n", kallsyms_filename); + +out_try_fixup: + if (err > 0) { + if (kallsyms_filename != NULL) { + kern_mmap_name(kerninfo, path); + dso__set_long_name(self, + strdup(path)); + } + map__fixup_start(map); + map__fixup_end(map); + } + + return err; +} static void dsos__add(struct list_head *head, struct dso *dso) { @@ -1752,10 +1907,16 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp) } } -void dsos__fprintf(FILE *fp) +void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp) { - __dsos__fprintf(&dsos__kernel, fp); - __dsos__fprintf(&dsos__user, fp); + struct rb_node *nd; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + __dsos__fprintf(&pos->dsos__kernel, fp); + __dsos__fprintf(&pos->dsos__user, fp); + } } static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, @@ -1773,10 +1934,21 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) +size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, + FILE *fp, bool with_hits) { - return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + - __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); + struct rb_node *nd; + size_t ret = 0; + + for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { + struct kernel_info *pos = rb_entry(nd, struct kernel_info, + rb_node); + ret += __dsos__fprintf_buildid(&pos->dsos__kernel, + fp, with_hits); + ret += __dsos__fprintf_buildid(&pos->dsos__user, + fp, with_hits); + } + return ret; } struct dso *dso__new_kernel(const char *name) @@ -1785,28 +1957,59 @@ struct dso *dso__new_kernel(const char *name) if (self != NULL) { dso__set_short_name(self, "[kernel]"); - self->kernel = 1; + self->kernel = DSO_TYPE_KERNEL; + } + + return self; +} + +static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, + const char *name) +{ + char buff[PATH_MAX]; + struct dso *self; + + kern_mmap_name(kerninfo, buff); + self = dso__new(name ?: buff); + if (self != NULL) { + dso__set_short_name(self, "[guest.kernel]"); + self->kernel = DSO_TYPE_GUEST_KERNEL; } return self; } -void dso__read_running_kernel_build_id(struct dso *self) +void dso__read_running_kernel_build_id(struct dso *self, + struct kernel_info *kerninfo) { - if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, + char path[PATH_MAX]; + + if (is_default_guest(kerninfo)) + return; + sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir); + if (sysfs__read_build_id(path, self->build_id, sizeof(self->build_id)) == 0) self->has_build_id = true; } -static struct dso *dsos__create_kernel(const char *vmlinux) +static struct dso *dsos__create_kernel(struct kernel_info *kerninfo) { - struct dso *kernel = dso__new_kernel(vmlinux); + const char *vmlinux_name = NULL; + struct dso *kernel; - if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel); - dsos__add(&dsos__kernel, kernel); + if (is_host_kernel(kerninfo)) { + vmlinux_name = symbol_conf.vmlinux_name; + kernel = dso__new_kernel(vmlinux_name); + } else { + if (is_default_guest(kerninfo)) + vmlinux_name = symbol_conf.default_guest_vmlinux_name; + kernel = dso__new_guest_kernel(kerninfo, vmlinux_name); } + if (kernel != NULL) { + dso__read_running_kernel_build_id(kernel, kerninfo); + dsos__add(&kerninfo->dsos__kernel, kernel); + } return kernel; } @@ -1950,23 +2153,29 @@ out_free_comm_list: return -1; } -int map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES]) +int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid) { - struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); + struct kernel_info *kerninfo; + struct dso *kernel; + kerninfo = kerninfo__findnew(kerninfo_root, pid); + if (kerninfo == NULL) + return -1; + kernel = dsos__create_kernel(kerninfo); if (kernel == NULL) return -1; - if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) + if (__map_groups__create_kernel_maps(&kerninfo->kmaps, + kerninfo->vmlinux_maps, kernel) < 0) return -1; - if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) + if (symbol_conf.use_modules && + map_groups__create_modules(kerninfo) < 0) pr_debug("Problems creating module maps, continuing anyway...\n"); /* * Now that we have all the maps created, just set the ->end of them: */ - map_groups__fixup_end(self); + map_groups__fixup_end(&kerninfo->kmaps); return 0; } @@ -2012,3 +2221,46 @@ char *strxfrchar(char *s, char from, char to) return s; } + +int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) +{ + int ret = 0; + struct dirent **namelist = NULL; + int i, items = 0; + char path[PATH_MAX]; + pid_t pid; + + if (symbol_conf.default_guest_vmlinux_name || + symbol_conf.default_guest_modules || + symbol_conf.default_guest_kallsyms) { + map_groups__create_kernel_maps(kerninfo_root, + DEFAULT_GUEST_KERNEL_ID); + } + + if (symbol_conf.guestmount) { + items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); + if (items <= 0) + return -ENOENT; + for (i = 0; i < items; i++) { + if (!isdigit(namelist[i]->d_name[0])) { + /* Filter out . and .. */ + continue; + } + pid = atoi(namelist[i]->d_name); + sprintf(path, "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); + ret = access(path, R_OK); + if (ret) { + pr_debug("Can't access file %s\n", path); + goto failure; + } + map_groups__create_kernel_maps(kerninfo_root, + pid); + } +failure: + free(namelist); + } + + return ret; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 757fae3..478f5ab 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -69,10 +69,15 @@ struct symbol_conf { show_nr_samples, use_callchain, exclude_other, - full_paths; + full_paths, + show_cpu_utilization; const char *vmlinux_name, *field_sep; - char *dso_list_str, + const char *default_guest_vmlinux_name, + *default_guest_kallsyms, + *default_guest_modules; + const char *guestmount; + char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; @@ -106,6 +111,13 @@ struct addr_location { u64 addr; char level; bool filtered; + unsigned int cpumode; +}; + +enum dso_kernel_type { + DSO_TYPE_USER = 0, + DSO_TYPE_KERNEL, + DSO_TYPE_GUEST_KERNEL }; struct dso { @@ -115,7 +127,7 @@ struct dso { u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; - u8 kernel:1; + enum dso_kernel_type kernel; u8 hit:1; u8 annotate_warned:1; unsigned char origin; @@ -143,34 +155,30 @@ static inline void dso__set_loaded(struct dso *self, enum map_type type) void dso__sort_by_name(struct dso *self, enum map_type type); -extern struct list_head dsos__user, dsos__kernel; - struct dso *__dsos__findnew(struct list_head *head, const char *name); -static inline struct dso *dsos__findnew(const char *name) -{ - return __dsos__findnew(&dsos__user, name); -} - int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); -void dsos__fprintf(FILE *fp); -size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); +void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp); +size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, + FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); enum dso_origin { DSO__ORIG_KERNEL = 0, + DSO__ORIG_GUEST_KERNEL, DSO__ORIG_JAVA_JIT, DSO__ORIG_BUILD_ID_CACHE, DSO__ORIG_FEDORA, DSO__ORIG_UBUNTU, DSO__ORIG_BUILDID, DSO__ORIG_DSO, + DSO__ORIG_GUEST_KMODULE, DSO__ORIG_KMODULE, DSO__ORIG_NOT_FOUND, }; @@ -178,19 +186,26 @@ enum dso_origin { char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); -void dso__read_running_kernel_build_id(struct dso *self); +void dso__read_running_kernel_build_id(struct dso *self, + struct kernel_info *kerninfo); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, const char *name); int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); -bool dsos__read_build_ids(bool with_hits); +bool __dsos__read_build_ids(struct list_head *head, bool with_hits); int build_id__sprintf(const u8 *self, int len, char *bf); int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); +int __map_groups__create_kernel_maps(struct map_groups *self, + struct map *vmlinux_maps[MAP__NR_TYPES], + struct dso *kernel); +int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid); +int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root); + int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9c488fc..1dfd9ff 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -33,12 +33,12 @@ static inline struct map *thread__find_map(struct thread *self, void thread__find_addr_map(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al); void thread__find_addr_location(struct thread *self, struct perf_session *session, u8 cpumode, - enum map_type type, u64 addr, + enum map_type type, pid_t pid, u64 addr, struct addr_location *al, symbol_filter_t filter); #endif /* __PERF_THREAD_H */ -- cgit v0.10.2 From dcf46b9443ad48a227a61713adea001228925adf Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Tue, 20 Apr 2010 10:13:58 +0800 Subject: perf & kvm: Clean up some of the guest profiling callback API details Fix some build bug and programming style issues: - use valid C - fix up various style details Signed-off-by: Zhang Yanmin Cc: Avi Kivity Cc: Peter Zijlstra Cc: Sheng Yang Cc: Marcelo Tosatti Cc: oerg Roedel Cc: Jes Sorensen Cc: Gleb Natapov Cc: Zachary Amsden Cc: zhiteng.huang@intel.com Cc: tim.c.chen@intel.com Cc: Arnaldo Carvalho de Melo LKML-Reference: <1271729638.2078.624.camel@ymzhang.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 2ea78ab..7de7061 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1752,23 +1752,31 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski unsigned long perf_instruction_pointer(struct pt_regs *regs) { unsigned long ip; + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) ip = perf_guest_cbs->get_guest_ip(); else ip = instruction_pointer(regs); + return ip; } unsigned long perf_misc_flags(struct pt_regs *regs) { int misc = 0; + if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { - misc |= perf_guest_cbs->is_user_mode() ? - PERF_RECORD_MISC_GUEST_USER : - PERF_RECORD_MISC_GUEST_KERNEL; - } else - misc |= user_mode(regs) ? PERF_RECORD_MISC_USER : - PERF_RECORD_MISC_KERNEL; + if (perf_guest_cbs->is_user_mode()) + misc |= PERF_RECORD_MISC_GUEST_USER; + else + misc |= PERF_RECORD_MISC_GUEST_KERNEL; + } else { + if (user_mode(regs)) + misc |= PERF_RECORD_MISC_USER; + else + misc |= PERF_RECORD_MISC_KERNEL; + } + if (regs->flags & PERF_EFLAGS_EXACT) misc |= PERF_RECORD_MISC_EXACT; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c3a33b2..21b9b6a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3776,16 +3776,20 @@ static int kvm_is_in_guest(void) static int kvm_is_user_mode(void) { int user_mode = 3; + if (percpu_read(current_vcpu)) user_mode = kvm_x86_ops->get_cpl(percpu_read(current_vcpu)); + return user_mode != 0; } static unsigned long kvm_get_guest_ip(void) { unsigned long ip = 0; + if (percpu_read(current_vcpu)) ip = kvm_rip_read(percpu_read(current_vcpu)); + return ip; } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 24de5f1..ace31fb 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -941,10 +941,8 @@ static inline void perf_event_mmap(struct vm_area_struct *vma) } extern struct perf_guest_info_callbacks *perf_guest_cbs; -extern int perf_register_guest_info_callbacks( - struct perf_guest_info_callbacks *); -extern int perf_unregister_guest_info_callbacks( - struct perf_guest_info_callbacks *); +extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); +extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); extern void perf_event_comm(struct task_struct *tsk); extern void perf_event_fork(struct task_struct *tsk); @@ -1016,9 +1014,9 @@ static inline void perf_bp_event(struct perf_event *event, void *data) { } static inline int perf_register_guest_info_callbacks -(struct perf_guest_info_callbacks *) {return 0; } +(struct perf_guest_info_callbacks *callbacks) { return 0; } static inline int perf_unregister_guest_info_callbacks -(struct perf_guest_info_callbacks *) {return 0; } +(struct perf_guest_info_callbacks *callbacks) { return 0; } static inline void perf_event_mmap(struct vm_area_struct *vma) { } static inline void perf_event_comm(struct task_struct *tsk) { } -- cgit v0.10.2 From 6eca8cc35b50af1037bc919106dd6dd332c959c2 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 21 Apr 2010 02:01:05 +0200 Subject: perf: Fix perf probe build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we run into dry run mode, we want to make write_kprobe_trace_event to succeed on writing the event. Let's initialize it to 0. Fixes the following build error: util/probe-event.c:1266: attention : «ret» may be used uninitialized in this function util/probe-event.c:1266: note: «ret» was declared here Signed-off-by: Frederic Weisbecker Acked-by: Masami Hiramatsu Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo LKML-Reference: <1271808065-25290-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3967f8f..4fb4803 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1263,7 +1263,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { - int ret; + int ret = 0; char *buf = synthesize_kprobe_trace_command(tev); if (!buf) { -- cgit v0.10.2 From cd932c593995abee1d1a8a0bfe608f7d103d87ad Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Tue, 20 Apr 2010 16:58:32 +1000 Subject: perf: Move arch specific code into separate arch directory The perf userspace tool included some architecture specific code to map registers from the DWARF register number into the names used by the regs and stack access API. This moves the architecture specific code out into a separate arch/x86 directory along with the infrastructure required to use it. Signed-off-by: Ian Munsie Acked-by: Masami Hiramatsu Signed-off-by: Paul Mackerras diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3cb3449..e8bf2e1 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -173,6 +173,20 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') +ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/s390x/s390/ -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ + -e s/sh[234].*/sh/ ) + +# Additional ARCH settings for x86 +ifeq ($(ARCH),i386) + ARCH := x86 +endif +ifeq ($(ARCH),x86_64) + ARCH := x86 +endif + # CFLAGS and LDFLAGS are for the users to override from the command line. # @@ -285,7 +299,7 @@ endif # Those must not be GNU-specific; they are shared with perl/ which may # be built by a different compiler. (Note that this is an artifact now # but it still might be nice to keep that distinction.) -BASIC_CFLAGS = -Iutil/include +BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include BASIC_LDFLAGS = # Guard against environment variables @@ -367,6 +381,7 @@ LIB_H += util/include/asm/byteorder.h LIB_H += util/include/asm/swab.h LIB_H += util/include/asm/system.h LIB_H += util/include/asm/uaccess.h +LIB_H += util/include/dwarf-regs.h LIB_H += perf.h LIB_H += util/cache.h LIB_H += util/callchain.h @@ -487,6 +502,15 @@ PERFLIBS = $(LIB_FILE) -include config.mak.autogen -include config.mak +ifndef NO_DWARF +ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) + msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); + NO_DWARF := 1 +endif # Dwarf support +endif # NO_DWARF + +-include arch/$(ARCH)/Makefile + ifeq ($(uname_S),Darwin) ifndef NO_FINK ifeq ($(shell test -d /sw/lib && echo y),y) @@ -519,15 +543,15 @@ else msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); endif -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); -else ifndef NO_DWARF +ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) + msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); +else BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT EXTLIBS += -lelf -ldw LIB_OBJS += $(OUTPUT)util/probe-finder.o -endif -endif +endif # PERF_HAVE_DWARF_REGS +endif # NO_DWARF ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile new file mode 100644 index 0000000..15130b50 --- /dev/null +++ b/tools/perf/arch/x86/Makefile @@ -0,0 +1,4 @@ +ifndef NO_DWARF +PERF_HAVE_DWARF_REGS := 1 +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o +endif diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c new file mode 100644 index 0000000..a794d30 --- /dev/null +++ b/tools/perf/arch/x86/util/dwarf-regs.c @@ -0,0 +1,75 @@ +/* + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + * Extracted from probe-finder.c + * + * Written by Masami Hiramatsu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +/* + * Generic dwarf analysis helpers + */ + +#define X86_32_MAX_REGS 8 +const char *x86_32_regs_table[X86_32_MAX_REGS] = { + "%ax", + "%cx", + "%dx", + "%bx", + "$stack", /* Stack address instead of %sp */ + "%bp", + "%si", + "%di", +}; + +#define X86_64_MAX_REGS 16 +const char *x86_64_regs_table[X86_64_MAX_REGS] = { + "%ax", + "%dx", + "%cx", + "%bx", + "%si", + "%di", + "%bp", + "%sp", + "%r8", + "%r9", + "%r10", + "%r11", + "%r12", + "%r13", + "%r14", + "%r15", +}; + +/* TODO: switching by dwarf address size */ +#ifdef __x86_64__ +#define ARCH_MAX_REGS X86_64_MAX_REGS +#define arch_regs_table x86_64_regs_table +#else +#define ARCH_MAX_REGS X86_32_MAX_REGS +#define arch_regs_table x86_32_regs_table +#endif + +/* Return architecture dependent register string (for kprobe-tracer) */ +const char *get_arch_regstr(unsigned int n) +{ + return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; +} diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h new file mode 100644 index 0000000..cf6727e --- /dev/null +++ b/tools/perf/util/include/dwarf-regs.h @@ -0,0 +1,8 @@ +#ifndef _PERF_DWARF_REGS_H_ +#define _PERF_DWARF_REGS_H_ + +#ifdef DWARF_SUPPORT +const char *get_arch_regstr(unsigned int n); +#endif + +#endif diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3e79775..e7ee52f 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "string.h" #include "event.h" @@ -38,61 +39,9 @@ #include "util.h" #include "probe-finder.h" - -/* - * Generic dwarf analysis helpers - */ - -#define X86_32_MAX_REGS 8 -const char *x86_32_regs_table[X86_32_MAX_REGS] = { - "%ax", - "%cx", - "%dx", - "%bx", - "$stack", /* Stack address instead of %sp */ - "%bp", - "%si", - "%di", -}; - -#define X86_64_MAX_REGS 16 -const char *x86_64_regs_table[X86_64_MAX_REGS] = { - "%ax", - "%dx", - "%cx", - "%bx", - "%si", - "%di", - "%bp", - "%sp", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", -}; - -/* TODO: switching by dwarf address size */ -#ifdef __x86_64__ -#define ARCH_MAX_REGS X86_64_MAX_REGS -#define arch_regs_table x86_64_regs_table -#else -#define ARCH_MAX_REGS X86_32_MAX_REGS -#define arch_regs_table x86_32_regs_table -#endif - /* Kprobe tracer basic type is up to u64 */ #define MAX_BASIC_TYPE_BITS 64 -/* Return architecture dependent register string (for kprobe-tracer) */ -static const char *get_arch_regstr(unsigned int n) -{ - return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; -} - /* * Compare the tail of two strings. * Return 0 if whole of either string is same as another's tail part. @@ -447,7 +396,7 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) regs = get_arch_regstr(regn); if (!regs) { - pr_warning("%u exceeds max register number.\n", regn); + pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); return -ERANGE; } -- cgit v0.10.2 From fead7960f0b645348fa4757100f4d57654bf9ffa Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Tue, 20 Apr 2010 16:58:33 +1000 Subject: perf probe: Add PowerPC DWARF register number mappings This adds mappings from the register numbers from DWARF to the register names used in the PowerPC Regs and Stack Access API. This allows perf probe to be used to record variable contents on PowerPC. This requires the functionality represented by the config symbol HAVE_REGS_AND_STACK_ACCESS_API in order to function, although it will compile without it. That functionality is added for PowerPC in commit 359e4284 ("powerpc: Add kprobe-based event tracer"). Signed-off-by: Ian Munsie Acked-by: Masami Hiramatsu Signed-off-by: Paul Mackerras diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile new file mode 100644 index 0000000..15130b50 --- /dev/null +++ b/tools/perf/arch/powerpc/Makefile @@ -0,0 +1,4 @@ +ifndef NO_DWARF +PERF_HAVE_DWARF_REGS := 1 +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o +endif diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c new file mode 100644 index 0000000..48ae0c5 --- /dev/null +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c @@ -0,0 +1,88 @@ +/* + * Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2010 Ian Munsie, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + + +struct pt_regs_dwarfnum { + const char *name; + unsigned int dwarfnum; +}; + +#define STR(s) #s +#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} +#define GPR_DWARFNUM_NAME(num) \ + {.name = STR(%gpr##num), .dwarfnum = num} +#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} + +/* + * Reference: + * http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html + */ +static const struct pt_regs_dwarfnum regdwarfnum_table[] = { + GPR_DWARFNUM_NAME(0), + GPR_DWARFNUM_NAME(1), + GPR_DWARFNUM_NAME(2), + GPR_DWARFNUM_NAME(3), + GPR_DWARFNUM_NAME(4), + GPR_DWARFNUM_NAME(5), + GPR_DWARFNUM_NAME(6), + GPR_DWARFNUM_NAME(7), + GPR_DWARFNUM_NAME(8), + GPR_DWARFNUM_NAME(9), + GPR_DWARFNUM_NAME(10), + GPR_DWARFNUM_NAME(11), + GPR_DWARFNUM_NAME(12), + GPR_DWARFNUM_NAME(13), + GPR_DWARFNUM_NAME(14), + GPR_DWARFNUM_NAME(15), + GPR_DWARFNUM_NAME(16), + GPR_DWARFNUM_NAME(17), + GPR_DWARFNUM_NAME(18), + GPR_DWARFNUM_NAME(19), + GPR_DWARFNUM_NAME(20), + GPR_DWARFNUM_NAME(21), + GPR_DWARFNUM_NAME(22), + GPR_DWARFNUM_NAME(23), + GPR_DWARFNUM_NAME(24), + GPR_DWARFNUM_NAME(25), + GPR_DWARFNUM_NAME(26), + GPR_DWARFNUM_NAME(27), + GPR_DWARFNUM_NAME(28), + GPR_DWARFNUM_NAME(29), + GPR_DWARFNUM_NAME(30), + GPR_DWARFNUM_NAME(31), + REG_DWARFNUM_NAME("%msr", 66), + REG_DWARFNUM_NAME("%ctr", 109), + REG_DWARFNUM_NAME("%link", 108), + REG_DWARFNUM_NAME("%xer", 101), + REG_DWARFNUM_NAME("%dar", 119), + REG_DWARFNUM_NAME("%dsisr", 118), + REG_DWARFNUM_END, +}; + +/** + * get_arch_regstr() - lookup register name from it's DWARF register number + * @n: the DWARF register number + * + * get_arch_regstr() returns the name of the register in struct + * regdwarfnum_table from it's DWARF register number. If the register is not + * found in the table, this returns NULL; + */ +const char *get_arch_regstr(unsigned int n) +{ + const struct pt_regs_dwarfnum *roff; + for (roff = regdwarfnum_table; roff->name != NULL; roff++) + if (roff->dwarfnum == n) + return roff->name; + return NULL; +} -- cgit v0.10.2 From e4cef1f65061429c3e8b356233e87dc6653a9da5 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Wed, 21 Apr 2010 21:23:54 +0900 Subject: perf lock: Fix state machine to recognize lock sequence Previous state machine of perf lock was really broken. This patch improves it a little. This patch prepares the list of state machine that represents lock sequences for each threads. These state machines can be one of these sequences: 1) acquire -> acquired -> release 2) acquire -> contended -> acquired -> release 3) acquire (w/ try) -> release 4) acquire (w/ read) -> release The case of 4) is a little special. Double acquire of read lock is allowed, so the state machine counts read lock number, and permits double acquire and release. But, things are not so simple. Something in my model is still wrong. I counted the number of lock instances with bad sequence, and ratio is like this (case of tracing whoami): bad:233, total:2279 version 2: * threads are now identified with tid, not pid * prepared SEQ_STATE_READ_ACQUIRED for read lock. * bunch of struct lock_seq_stat is now linked list * debug information enhanced (this have to be removed someday) e.g. | === output for debug=== | | bad:233, total:2279 | bad rate:0.000000 | histogram of events caused bad sequence | acquire: 165 | acquired: 0 | contended: 0 | release: 68 Signed-off-by: Hitoshi Mitake Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jens Axboe Cc: Jason Baron Cc: Xiao Guangrong Cc: Ingo Molnar LKML-Reference: <1271852634-9351-1-git-send-email-mitake@dcl.info.waseda.ac.jp> [rename SEQ_STATE_UNINITED to SEQ_STATE_UNINITIALIZED] Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6c38e4f..716d8c5 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -23,6 +23,8 @@ #include #include +static struct perf_session *session; + /* based on kernel/lockdep.c */ #define LOCKHASH_BITS 12 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) @@ -32,9 +34,6 @@ static struct list_head lockhash_table[LOCKHASH_SIZE]; #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) -#define LOCK_STATE_UNLOCKED 0 /* initial state */ -#define LOCK_STATE_LOCKED 1 - struct lock_stat { struct list_head hash_entry; struct rb_node rb; /* used for sorting */ @@ -47,20 +46,151 @@ struct lock_stat { void *addr; /* address of lockdep_map, used as ID */ char *name; /* for strcpy(), we cannot use const */ - int state; - u64 prev_event_time; /* timestamp of previous event */ - - unsigned int nr_acquired; unsigned int nr_acquire; + unsigned int nr_acquired; unsigned int nr_contended; unsigned int nr_release; + unsigned int nr_readlock; + unsigned int nr_trylock; /* these times are in nano sec. */ u64 wait_time_total; u64 wait_time_min; u64 wait_time_max; + + int discard; /* flag of blacklist */ +}; + +/* + * States of lock_seq_stat + * + * UNINITIALIZED is required for detecting first event of acquire. + * As the nature of lock events, there is no guarantee + * that the first event for the locks are acquire, + * it can be acquired, contended or release. + */ +#define SEQ_STATE_UNINITIALIZED 0 /* initial state */ +#define SEQ_STATE_RELEASED 1 +#define SEQ_STATE_ACQUIRING 2 +#define SEQ_STATE_ACQUIRED 3 +#define SEQ_STATE_READ_ACQUIRED 4 +#define SEQ_STATE_CONTENDED 5 + +/* + * MAX_LOCK_DEPTH + * Imported from include/linux/sched.h. + * Should this be synchronized? + */ +#define MAX_LOCK_DEPTH 48 + +/* + * struct lock_seq_stat: + * Place to put on state of one lock sequence + * 1) acquire -> acquired -> release + * 2) acquire -> contended -> acquired -> release + * 3) acquire (with read or try) -> release + * 4) Are there other patterns? + */ +struct lock_seq_stat { + struct list_head list; + int state; + u64 prev_event_time; + void *addr; + + int read_count; }; +struct thread_stat { + struct rb_node rb; + + u32 tid; + struct list_head seq_list; +}; + +static struct rb_root thread_stats; + +static struct thread_stat *thread_stat_find(u32 tid) +{ + struct rb_node *node; + struct thread_stat *st; + + node = thread_stats.rb_node; + while (node) { + st = container_of(node, struct thread_stat, rb); + if (st->tid == tid) + return st; + else if (tid < st->tid) + node = node->rb_left; + else + node = node->rb_right; + } + + return NULL; +} + +static void thread_stat_insert(struct thread_stat *new) +{ + struct rb_node **rb = &thread_stats.rb_node; + struct rb_node *parent = NULL; + struct thread_stat *p; + + while (*rb) { + p = container_of(*rb, struct thread_stat, rb); + parent = *rb; + + if (new->tid < p->tid) + rb = &(*rb)->rb_left; + else if (new->tid > p->tid) + rb = &(*rb)->rb_right; + else + BUG_ON("inserting invalid thread_stat\n"); + } + + rb_link_node(&new->rb, parent, rb); + rb_insert_color(&new->rb, &thread_stats); +} + +static struct thread_stat *thread_stat_findnew_after_first(u32 tid) +{ + struct thread_stat *st; + + st = thread_stat_find(tid); + if (st) + return st; + + st = zalloc(sizeof(struct thread_stat)); + if (!st) + die("memory allocation failed\n"); + + st->tid = tid; + INIT_LIST_HEAD(&st->seq_list); + + thread_stat_insert(st); + + return st; +} + +static struct thread_stat *thread_stat_findnew_first(u32 tid); +static struct thread_stat *(*thread_stat_findnew)(u32 tid) = + thread_stat_findnew_first; + +static struct thread_stat *thread_stat_findnew_first(u32 tid) +{ + struct thread_stat *st; + + st = zalloc(sizeof(struct thread_stat)); + if (!st) + die("memory allocation failed\n"); + st->tid = tid; + INIT_LIST_HEAD(&st->seq_list); + + rb_link_node(&st->rb, NULL, &thread_stats.rb_node); + rb_insert_color(&st->rb, &thread_stats); + + thread_stat_findnew = thread_stat_findnew_after_first; + return st; +} + /* build simple key function one is bigger than two */ #define SINGLE_KEY(member) \ static int lock_stat_key_ ## member(struct lock_stat *one, \ @@ -175,8 +305,6 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name) goto alloc_failed; strcpy(new->name, name); - /* LOCK_STATE_UNLOCKED == 0 isn't guaranteed forever */ - new->state = LOCK_STATE_UNLOCKED; new->wait_time_min = ULLONG_MAX; list_add(&new->hash_entry, entry); @@ -198,6 +326,7 @@ struct raw_event_sample { struct trace_acquire_event { void *addr; const char *name; + int flag; }; struct trace_acquired_event { @@ -241,120 +370,246 @@ struct trace_lock_handler { struct thread *thread); }; +static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) +{ + struct lock_seq_stat *seq; + + list_for_each_entry(seq, &ts->seq_list, list) { + if (seq->addr == addr) + return seq; + } + + seq = zalloc(sizeof(struct lock_seq_stat)); + if (!seq) + die("Not enough memory\n"); + seq->state = SEQ_STATE_UNINITIALIZED; + seq->addr = addr; + + list_add(&seq->list, &ts->seq_list); + return seq; +} + +static int bad_hist[4]; + static void report_lock_acquire_event(struct trace_acquire_event *acquire_event, struct event *__event __used, int cpu __used, - u64 timestamp, + u64 timestamp __used, struct thread *thread __used) { - struct lock_stat *st; + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; + + ls = lock_stat_findnew(acquire_event->addr, acquire_event->name); + if (ls->discard) + return; - st = lock_stat_findnew(acquire_event->addr, acquire_event->name); + ts = thread_stat_findnew(thread->pid); + seq = get_seq(ts, acquire_event->addr); - switch (st->state) { - case LOCK_STATE_UNLOCKED: + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + case SEQ_STATE_RELEASED: + if (!acquire_event->flag) { + seq->state = SEQ_STATE_ACQUIRING; + } else { + if (acquire_event->flag & 1) + ls->nr_trylock++; + if (acquire_event->flag & 2) + ls->nr_readlock++; + seq->state = SEQ_STATE_READ_ACQUIRED; + seq->read_count = 1; + ls->nr_acquired++; + } + break; + case SEQ_STATE_READ_ACQUIRED: + if (acquire_event->flag & 2) { + seq->read_count++; + ls->nr_acquired++; + goto end; + } else { + goto broken; + } break; - case LOCK_STATE_LOCKED: + case SEQ_STATE_ACQUIRED: + case SEQ_STATE_ACQUIRING: + case SEQ_STATE_CONTENDED: +broken: + /* broken lock sequence, discard it */ + ls->discard = 1; + bad_hist[0]++; + list_del(&seq->list); + free(seq); + goto end; break; default: - BUG_ON(1); + BUG_ON("Unknown state of lock sequence found!\n"); break; } - st->prev_event_time = timestamp; + ls->nr_acquire++; + seq->prev_event_time = timestamp; +end: + return; } static void report_lock_acquired_event(struct trace_acquired_event *acquired_event, struct event *__event __used, int cpu __used, - u64 timestamp, + u64 timestamp __used, struct thread *thread __used) { - struct lock_stat *st; + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; + u64 contended_term; - st = lock_stat_findnew(acquired_event->addr, acquired_event->name); + ls = lock_stat_findnew(acquired_event->addr, acquired_event->name); + if (ls->discard) + return; + + ts = thread_stat_findnew(thread->pid); + seq = get_seq(ts, acquired_event->addr); - switch (st->state) { - case LOCK_STATE_UNLOCKED: - st->state = LOCK_STATE_LOCKED; - st->nr_acquired++; + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + /* orphan event, do nothing */ + return; + case SEQ_STATE_ACQUIRING: break; - case LOCK_STATE_LOCKED: + case SEQ_STATE_CONTENDED: + contended_term = timestamp - seq->prev_event_time; + ls->wait_time_total += contended_term; + + if (contended_term < ls->wait_time_min) + ls->wait_time_min = contended_term; + else if (ls->wait_time_max < contended_term) + ls->wait_time_max = contended_term; break; + case SEQ_STATE_RELEASED: + case SEQ_STATE_ACQUIRED: + case SEQ_STATE_READ_ACQUIRED: + /* broken lock sequence, discard it */ + ls->discard = 1; + bad_hist[1]++; + list_del(&seq->list); + free(seq); + goto end; + break; + default: - BUG_ON(1); + BUG_ON("Unknown state of lock sequence found!\n"); break; } - st->prev_event_time = timestamp; + seq->state = SEQ_STATE_ACQUIRED; + ls->nr_acquired++; + seq->prev_event_time = timestamp; +end: + return; } static void report_lock_contended_event(struct trace_contended_event *contended_event, struct event *__event __used, int cpu __used, - u64 timestamp, + u64 timestamp __used, struct thread *thread __used) { - struct lock_stat *st; + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; + + ls = lock_stat_findnew(contended_event->addr, contended_event->name); + if (ls->discard) + return; - st = lock_stat_findnew(contended_event->addr, contended_event->name); + ts = thread_stat_findnew(thread->pid); + seq = get_seq(ts, contended_event->addr); - switch (st->state) { - case LOCK_STATE_UNLOCKED: + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + /* orphan event, do nothing */ + return; + case SEQ_STATE_ACQUIRING: break; - case LOCK_STATE_LOCKED: - st->nr_contended++; + case SEQ_STATE_RELEASED: + case SEQ_STATE_ACQUIRED: + case SEQ_STATE_READ_ACQUIRED: + case SEQ_STATE_CONTENDED: + /* broken lock sequence, discard it */ + ls->discard = 1; + bad_hist[2]++; + list_del(&seq->list); + free(seq); + goto end; break; default: - BUG_ON(1); + BUG_ON("Unknown state of lock sequence found!\n"); break; } - st->prev_event_time = timestamp; + seq->state = SEQ_STATE_CONTENDED; + ls->nr_contended++; + seq->prev_event_time = timestamp; +end: + return; } static void report_lock_release_event(struct trace_release_event *release_event, struct event *__event __used, int cpu __used, - u64 timestamp, + u64 timestamp __used, struct thread *thread __used) { - struct lock_stat *st; - u64 hold_time; + struct lock_stat *ls; + struct thread_stat *ts; + struct lock_seq_stat *seq; - st = lock_stat_findnew(release_event->addr, release_event->name); + ls = lock_stat_findnew(release_event->addr, release_event->name); + if (ls->discard) + return; - switch (st->state) { - case LOCK_STATE_UNLOCKED: - break; - case LOCK_STATE_LOCKED: - st->state = LOCK_STATE_UNLOCKED; - hold_time = timestamp - st->prev_event_time; + ts = thread_stat_findnew(thread->pid); + seq = get_seq(ts, release_event->addr); - if (timestamp < st->prev_event_time) { - /* terribly, this can happen... */ + switch (seq->state) { + case SEQ_STATE_UNINITIALIZED: + goto end; + break; + case SEQ_STATE_ACQUIRED: + break; + case SEQ_STATE_READ_ACQUIRED: + seq->read_count--; + BUG_ON(seq->read_count < 0); + if (!seq->read_count) { + ls->nr_release++; goto end; } - - if (st->wait_time_min > hold_time) - st->wait_time_min = hold_time; - if (st->wait_time_max < hold_time) - st->wait_time_max = hold_time; - st->wait_time_total += hold_time; - - st->nr_release++; + break; + case SEQ_STATE_ACQUIRING: + case SEQ_STATE_CONTENDED: + case SEQ_STATE_RELEASED: + /* broken lock sequence, discard it */ + ls->discard = 1; + bad_hist[3]++; + goto free_seq; break; default: - BUG_ON(1); + BUG_ON("Unknown state of lock sequence found!\n"); break; } + ls->nr_release++; +free_seq: + list_del(&seq->list); + free(seq); end: - st->prev_event_time = timestamp; + return; } /* lock oriented handlers */ @@ -381,6 +636,7 @@ process_lock_acquire_event(void *data, tmp = raw_field_value(event, "lockdep_addr", data); memcpy(&acquire_event.addr, &tmp, sizeof(void *)); acquire_event.name = (char *)raw_field_ptr(event, "name", data); + acquire_event.flag = (int)raw_field_value(event, "flag", data); if (trace_handler->acquire_event) trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread); @@ -441,8 +697,8 @@ process_lock_release_event(void *data, } static void -process_raw_event(void *data, int cpu, - u64 timestamp, struct thread *thread) +process_raw_event(void *data, int cpu __used, + u64 timestamp __used, struct thread *thread __used) { struct event *event; int type; @@ -604,14 +860,15 @@ static void queue_raw_event(void *data, int raw_size, int cpu, } } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct perf_session *s) { struct thread *thread; struct sample_data data; bzero(&data, sizeof(struct sample_data)); - event__parse_sample(event, session->sample_type, &data); - thread = perf_session__findnew(session, data.pid); + event__parse_sample(event, s->sample_type, &data); + /* CAUTION: using tid as thread.pid */ + thread = perf_session__findnew(s, data.tid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", @@ -634,8 +891,8 @@ static void print_result(void) { struct lock_stat *st; char cut_name[20]; + int bad, total; - printf("%18s ", "ID"); printf("%20s ", "Name"); printf("%10s ", "acquired"); printf("%10s ", "contended"); @@ -646,11 +903,15 @@ static void print_result(void) printf("\n\n"); + bad = total = 0; while ((st = pop_from_result())) { + total++; + if (st->discard) { + bad++; + continue; + } bzero(cut_name, 20); - printf("%p ", st->addr); - if (strlen(st->name) < 16) { /* output raw name */ printf("%20s ", st->name); @@ -673,6 +934,21 @@ static void print_result(void) 0 : st->wait_time_min); printf("\n"); } + + { + /* Output for debug, this have to be removed */ + int i; + const char *name[4] = + { "acquire", "acquired", "contended", "release" }; + + printf("\n=== output for debug===\n\n"); + printf("bad:%d, total:%d\n", bad, total); + printf("bad rate:%f\n", (double)(bad / total)); + + printf("histogram of events caused bad sequence\n"); + for (i = 0; i < 4; i++) + printf(" %10s: %d\n", name[i], bad_hist[i]); + } } static void dump_map(void) @@ -692,8 +968,6 @@ static struct perf_event_ops eops = { .comm = event__process_comm, }; -static struct perf_session *session; - static int read_events(void) { session = perf_session__new(input_name, O_RDONLY, 0); -- cgit v0.10.2 From 5710fcad7c367adefe5634dc998f1f88780a8457 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 21 Apr 2010 18:06:01 +0200 Subject: perf: Fix initialization bug in parse_single_tracepoint_event() The parse_single_tracepoint_event() was setting some attributes before it validated the event was indeed a tracepoint event. This caused problems with other initialization routines like in the builtin-top.c module whereby sample_period is not set if not 0. Signed-off-by: Stephane Eranian Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar LKML-Reference: <4bcf232b.698fd80a.6fbe.ffffb737@mx.google.com> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 3b4ec67..600d327 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -418,12 +418,6 @@ parse_single_tracepoint_event(char *sys_name, u64 id; int fd; - attr->sample_type |= PERF_SAMPLE_RAW; - attr->sample_type |= PERF_SAMPLE_TIME; - attr->sample_type |= PERF_SAMPLE_CPU; - - attr->sample_period = 1; - snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); @@ -442,6 +436,13 @@ parse_single_tracepoint_event(char *sys_name, attr->type = PERF_TYPE_TRACEPOINT; *strp = evt_name + evt_length; + attr->sample_type |= PERF_SAMPLE_RAW; + attr->sample_type |= PERF_SAMPLE_TIME; + attr->sample_type |= PERF_SAMPLE_CPU; + + attr->sample_period = 1; + + return EVT_HANDLED; } -- cgit v0.10.2 From c61e52ee705f938596d307625dce00cc4345aaf0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 00:04:12 +0200 Subject: perf: Generalize perf lock's sample event reordering to the session layer The sample events recorded by perf record are not time ordered because we have one buffer per cpu for each event (even demultiplexed per task/per cpu for task bound events). But when we read trace events we want them to be ordered by time because many state machines are involved. There are currently two ways perf tools deal with that: - use -M to multiplex every buffers (perf sched, perf kmem) But this creates a lot of contention in SMP machines on record time. - use a post-processing time reordering (perf timechart, perf lock) The reordering used by timechart is simple but doesn't scale well with huge flow of events, in terms of performance and memory use (unusable with perf lock for example). Perf lock has its own samples reordering that flushes its memory use in a regular basis and that uses a sorting based on the previous event queued (a new event to be queued is close to the previous one most of the time). This patch proposes to export perf lock's samples reordering facility to the session layer that reads the events. So if a tool wants to get ordered sample events, it needs to set its struct perf_event_ops::ordered_samples to true and that's it. This prepares tracing based perf tools to get rid of the need to use buffers multiplexing (-M) or to implement their own reordering. Also lower the flush period to 2 as it's sufficient already. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 716d8c5..ce27675 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -316,8 +316,6 @@ alloc_failed: static char const *input_name = "perf.data"; -static int profile_cpu = -1; - struct raw_event_sample { u32 size; char data[0]; @@ -697,8 +695,7 @@ process_lock_release_event(void *data, } static void -process_raw_event(void *data, int cpu __used, - u64 timestamp __used, struct thread *thread __used) +process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) { struct event *event; int type; @@ -716,176 +713,6 @@ process_raw_event(void *data, int cpu __used, process_lock_release_event(data, event, cpu, timestamp, thread); } -struct raw_event_queue { - u64 timestamp; - int cpu; - void *data; - struct thread *thread; - struct list_head list; -}; - -static LIST_HEAD(raw_event_head); - -#define FLUSH_PERIOD (5 * NSEC_PER_SEC) - -static u64 flush_limit = ULLONG_MAX; -static u64 last_flush = 0; -struct raw_event_queue *last_inserted; - -static void flush_raw_event_queue(u64 limit) -{ - struct raw_event_queue *tmp, *iter; - - list_for_each_entry_safe(iter, tmp, &raw_event_head, list) { - if (iter->timestamp > limit) - return; - - if (iter == last_inserted) - last_inserted = NULL; - - process_raw_event(iter->data, iter->cpu, iter->timestamp, - iter->thread); - - last_flush = iter->timestamp; - list_del(&iter->list); - free(iter->data); - free(iter); - } -} - -static void __queue_raw_event_end(struct raw_event_queue *new) -{ - struct raw_event_queue *iter; - - list_for_each_entry_reverse(iter, &raw_event_head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, &raw_event_head); -} - -static void __queue_raw_event_before(struct raw_event_queue *new, - struct raw_event_queue *iter) -{ - list_for_each_entry_continue_reverse(iter, &raw_event_head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, &raw_event_head); -} - -static void __queue_raw_event_after(struct raw_event_queue *new, - struct raw_event_queue *iter) -{ - list_for_each_entry_continue(iter, &raw_event_head, list) { - if (iter->timestamp > new->timestamp) { - list_add_tail(&new->list, &iter->list); - return; - } - } - list_add_tail(&new->list, &raw_event_head); -} - -/* The queue is ordered by time */ -static void __queue_raw_event(struct raw_event_queue *new) -{ - if (!last_inserted) { - __queue_raw_event_end(new); - return; - } - - /* - * Most of the time the current event has a timestamp - * very close to the last event inserted, unless we just switched - * to another event buffer. Having a sorting based on a list and - * on the last inserted event that is close to the current one is - * probably more efficient than an rbtree based sorting. - */ - if (last_inserted->timestamp >= new->timestamp) - __queue_raw_event_before(new, last_inserted); - else - __queue_raw_event_after(new, last_inserted); -} - -static void queue_raw_event(void *data, int raw_size, int cpu, - u64 timestamp, struct thread *thread) -{ - struct raw_event_queue *new; - - if (flush_limit == ULLONG_MAX) - flush_limit = timestamp + FLUSH_PERIOD; - - if (timestamp < last_flush) { - printf("Warning: Timestamp below last timeslice flush\n"); - return; - } - - new = malloc(sizeof(*new)); - if (!new) - die("Not enough memory\n"); - - new->timestamp = timestamp; - new->cpu = cpu; - new->thread = thread; - - new->data = malloc(raw_size); - if (!new->data) - die("Not enough memory\n"); - - memcpy(new->data, data, raw_size); - - __queue_raw_event(new); - last_inserted = new; - - /* - * We want to have a slice of events covering 2 * FLUSH_PERIOD - * If FLUSH_PERIOD is big enough, it ensures every events that occured - * in the first half of the timeslice have all been buffered and there - * are none remaining (we need that because of the weakly ordered - * event recording we have). Then once we reach the 2 * FLUSH_PERIOD - * timeslice, we flush the first half to be gentle with the memory - * (the second half can still get new events in the middle, so wait - * another period to flush it) - */ - if (new->timestamp > flush_limit && - new->timestamp - flush_limit > FLUSH_PERIOD) { - flush_limit += FLUSH_PERIOD; - flush_raw_event_queue(flush_limit); - } -} - -static int process_sample_event(event_t *event, struct perf_session *s) -{ - struct thread *thread; - struct sample_data data; - - bzero(&data, sizeof(struct sample_data)); - event__parse_sample(event, s->sample_type, &data); - /* CAUTION: using tid as thread.pid */ - thread = perf_session__findnew(s, data.tid); - - if (thread == NULL) { - pr_debug("problem processing %d event, skipping it.\n", - event->header.type); - return -1; - } - - dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - - if (profile_cpu != -1 && profile_cpu != (int) data.cpu) - return 0; - - queue_raw_event(data.raw_data, data.raw_size, data.cpu, data.time, thread); - - return 0; -} - /* TODO: various way to print, coloring, nano or milli sec */ static void print_result(void) { @@ -963,9 +790,30 @@ static void dump_map(void) } } +static int process_sample_event(event_t *self, struct perf_session *s) +{ + struct sample_data data; + struct thread *thread; + + bzero(&data, sizeof(data)); + event__parse_sample(self, s->sample_type, &data); + + thread = perf_session__findnew(s, data.tid); + if (thread == NULL) { + pr_debug("problem processing %d event, skipping it.\n", + self->header.type); + return -1; + } + + process_raw_event(data.raw_data, data.cpu, data.time, thread); + + return 0; +} + static struct perf_event_ops eops = { .sample = process_sample_event, .comm = event__process_comm, + .ordered_samples = true, }; static int read_events(void) @@ -994,7 +842,6 @@ static void __cmd_report(void) setup_pager(); select_key(); read_events(); - flush_raw_event_queue(ULLONG_MAX); sort_result(); print_result(); } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7d88ae5..b7aade2 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -98,6 +98,8 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwdlen = 0; self->unknown_events = 0; self->kerninfo_root = RB_ROOT; + self->ordered_samples.flush_limit = ULLONG_MAX; + INIT_LIST_HEAD(&self->ordered_samples.samples_head); if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -351,6 +353,178 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_HEADER_MAX] = NULL, }; +struct sample_queue { + u64 timestamp; + struct sample_event *event; + struct list_head list; +}; + +#define FLUSH_PERIOD (2 * NSEC_PER_SEC) + +static void flush_sample_queue(struct perf_session *s, + struct perf_event_ops *ops) +{ + struct list_head *head = &s->ordered_samples.samples_head; + u64 limit = s->ordered_samples.flush_limit; + struct sample_queue *tmp, *iter; + + if (!ops->ordered_samples) + return; + + list_for_each_entry_safe(iter, tmp, head, list) { + if (iter->timestamp > limit) + return; + + if (iter == s->ordered_samples.last_inserted) + s->ordered_samples.last_inserted = NULL; + + ops->sample((event_t *)iter->event, s); + + s->ordered_samples.last_flush = iter->timestamp; + list_del(&iter->list); + free(iter->event); + free(iter); + } +} + +static void __queue_sample_end(struct sample_queue *new, struct list_head *head) +{ + struct sample_queue *iter; + + list_for_each_entry_reverse(iter, head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, head); +} + +static void __queue_sample_before(struct sample_queue *new, + struct sample_queue *iter, + struct list_head *head) +{ + list_for_each_entry_continue_reverse(iter, head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, head); +} + +static void __queue_sample_after(struct sample_queue *new, + struct sample_queue *iter, + struct list_head *head) +{ + list_for_each_entry_continue(iter, head, list) { + if (iter->timestamp > new->timestamp) { + list_add_tail(&new->list, &iter->list); + return; + } + } + list_add_tail(&new->list, head); +} + +/* The queue is ordered by time */ +static void __queue_sample_event(struct sample_queue *new, + struct perf_session *s) +{ + struct sample_queue *last_inserted = s->ordered_samples.last_inserted; + struct list_head *head = &s->ordered_samples.samples_head; + + + if (!last_inserted) { + __queue_sample_end(new, head); + return; + } + + /* + * Most of the time the current event has a timestamp + * very close to the last event inserted, unless we just switched + * to another event buffer. Having a sorting based on a list and + * on the last inserted event that is close to the current one is + * probably more efficient than an rbtree based sorting. + */ + if (last_inserted->timestamp >= new->timestamp) + __queue_sample_before(new, last_inserted, head); + else + __queue_sample_after(new, last_inserted, head); +} + +static int queue_sample_event(event_t *event, struct sample_data *data, + struct perf_session *s, + struct perf_event_ops *ops) +{ + u64 timestamp = data->time; + struct sample_queue *new; + u64 flush_limit; + + + if (s->ordered_samples.flush_limit == ULLONG_MAX) + s->ordered_samples.flush_limit = timestamp + FLUSH_PERIOD; + + if (timestamp < s->ordered_samples.last_flush) { + printf("Warning: Timestamp below last timeslice flush\n"); + return -EINVAL; + } + + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + + new->timestamp = timestamp; + + new->event = malloc(event->header.size); + if (!new->event) { + free(new); + return -ENOMEM; + } + + memcpy(new->event, event, event->header.size); + + __queue_sample_event(new, s); + s->ordered_samples.last_inserted = new; + + /* + * We want to have a slice of events covering 2 * FLUSH_PERIOD + * If FLUSH_PERIOD is big enough, it ensures every events that occured + * in the first half of the timeslice have all been buffered and there + * are none remaining (we need that because of the weakly ordered + * event recording we have). Then once we reach the 2 * FLUSH_PERIOD + * timeslice, we flush the first half to be gentle with the memory + * (the second half can still get new events in the middle, so wait + * another period to flush it) + */ + flush_limit = s->ordered_samples.flush_limit; + + if (new->timestamp > flush_limit && + new->timestamp - flush_limit > FLUSH_PERIOD) { + s->ordered_samples.flush_limit += FLUSH_PERIOD; + flush_sample_queue(s, ops); + } + + return 0; +} + +static int perf_session__process_sample(event_t *event, struct perf_session *s, + struct perf_event_ops *ops) +{ + struct sample_data data; + + if (!ops->ordered_samples) + return ops->sample(event, s); + + bzero(&data, sizeof(struct sample_data)); + event__parse_sample(event, s->sample_type, &data); + + queue_sample_event(event, &data, s, ops); + + return 0; +} + static int perf_session__process_event(struct perf_session *self, event_t *event, struct perf_event_ops *ops, @@ -371,7 +545,7 @@ static int perf_session__process_event(struct perf_session *self, switch (event->header.type) { case PERF_RECORD_SAMPLE: - return ops->sample(event, self); + return perf_session__process_sample(event, self, ops); case PERF_RECORD_MMAP: return ops->mmap(event, self); case PERF_RECORD_COMM: @@ -611,6 +785,9 @@ more: goto more; done: err = 0; + /* do the final flush for ordered samples */ + self->ordered_samples.flush_limit = ULLONG_MAX; + flush_sample_queue(self, ops); out_err: ui_progress__delete(progress); return err; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5e47c87..796e229 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -8,9 +8,17 @@ #include #include "../../../include/linux/perf_event.h" +struct sample_queue; struct ip_callchain; struct thread; +struct ordered_samples { + u64 last_flush; + u64 flush_limit; + struct list_head samples_head; + struct sample_queue *last_inserted; +}; + struct perf_session { struct perf_header header; unsigned long size; @@ -28,6 +36,7 @@ struct perf_session { bool fd_pipe; int cwdlen; char *cwd; + struct ordered_samples ordered_samples; char filename[0]; }; @@ -47,6 +56,7 @@ struct perf_event_ops { event_type, tracing_data, build_id; + bool ordered_samples; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force); -- cgit v0.10.2 From a64eae703b390185abe1d15fa932b48f04fa7fbb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 00:29:40 +0200 Subject: perf: Use generic sample reordering in perf sched Use the new generic sample events reordering from perf sched, this drops the need of multiplexing the buffers on record time, improving the scalability of perf sched. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 09ddc8e..94453f1 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1651,9 +1651,10 @@ static int process_lost_event(event_t *event __used, } static struct perf_event_ops event_ops = { - .sample = process_sample_event, - .comm = event__process_comm, - .lost = process_lost_event, + .sample = process_sample_event, + .comm = event__process_comm, + .lost = process_lost_event, + .ordered_samples = true, }; static int read_events(void) @@ -1850,7 +1851,6 @@ static const char *record_args[] = { "record", "-a", "-R", - "-M", "-f", "-m", "1024", "-c", "1", -- cgit v0.10.2 From 587570d4cc3cac80da7d569bee9cea3ca104d60e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 00:34:53 +0200 Subject: perf: Use generic sample reordering in perf kmem Use the new generic sample events reordering from perf kmem, this drops the need of multiplexing the buffers on record time, improving the scalability of perf kmem. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi Cc: Pekka Enberg Cc: Li Zefan diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index db474bb..ab906cb 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -335,8 +335,9 @@ static int process_sample_event(event_t *event, struct perf_session *session) } static struct perf_event_ops event_ops = { - .sample = process_sample_event, - .comm = event__process_comm, + .sample = process_sample_event, + .comm = event__process_comm, + .ordered_samples = true, }; static double fragmentation(unsigned long n_req, unsigned long n_alloc) @@ -730,7 +731,6 @@ static const char *record_args[] = { "record", "-a", "-R", - "-M", "-f", "-c", "1", "-e", "kmem:kmalloc", -- cgit v0.10.2 From e0a808c65c23f88e48a5fff48775b90e7919c64f Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 00:38:33 +0200 Subject: perf: Use generic sample reordering in perf trace Use the new generic sample events reordering from perf trace. Before that, the displayed traces were ordered as they were in the input as recorded by perf record (not time ordered). This makes eventually perf trace displaying the events as beeing time ordered. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2eefb33..1d034f6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -108,6 +108,7 @@ static struct perf_event_ops event_ops = { .event_type = event__process_event_type, .tracing_data = event__process_tracing_data, .build_id = event__process_build_id, + .ordered_samples = true, }; extern volatile int session_done; -- cgit v0.10.2 From 9df9bbba9f7e2e4ffdc51bbbfa524b67691321d2 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 01:18:48 +0200 Subject: perf: Use generic sample reordering in perf timechart Use the new generic sample events reordering from perf timechart, this drops the ad hoc sample reordering it was using before. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi Cc: Arjan van de Ven diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 96f4a09..c35aa44 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -143,9 +143,6 @@ struct wake_event { static struct power_event *power_events; static struct wake_event *wake_events; -struct sample_wrapper *all_samples; - - struct process_filter; struct process_filter { char *name; @@ -566,88 +563,6 @@ static void end_sample_processing(void) } } -static u64 sample_time(event_t *event, const struct perf_session *session) -{ - int cursor; - - cursor = 0; - if (session->sample_type & PERF_SAMPLE_IP) - cursor++; - if (session->sample_type & PERF_SAMPLE_TID) - cursor++; - if (session->sample_type & PERF_SAMPLE_TIME) - return event->sample.array[cursor]; - return 0; -} - - -/* - * We first queue all events, sorted backwards by insertion. - * The order will get flipped later. - */ -static int queue_sample_event(event_t *event, struct perf_session *session) -{ - struct sample_wrapper *copy, *prev; - int size; - - size = event->sample.header.size + sizeof(struct sample_wrapper) + 8; - - copy = malloc(size); - if (!copy) - return 1; - - memset(copy, 0, size); - - copy->next = NULL; - copy->timestamp = sample_time(event, session); - - memcpy(©->data, event, event->sample.header.size); - - /* insert in the right place in the list */ - - if (!all_samples) { - /* first sample ever */ - all_samples = copy; - return 0; - } - - if (all_samples->timestamp < copy->timestamp) { - /* insert at the head of the list */ - copy->next = all_samples; - all_samples = copy; - return 0; - } - - prev = all_samples; - while (prev->next) { - if (prev->next->timestamp < copy->timestamp) { - copy->next = prev->next; - prev->next = copy; - return 0; - } - prev = prev->next; - } - /* insert at the end of the list */ - prev->next = copy; - - return 0; -} - -static void sort_queued_samples(void) -{ - struct sample_wrapper *cursor, *next; - - cursor = all_samples; - all_samples = NULL; - - while (cursor) { - next = cursor->next; - cursor->next = all_samples; - all_samples = cursor; - cursor = next; - } -} - /* * Sort the pid datastructure */ @@ -1011,26 +926,12 @@ static void write_svg_file(const char *filename) svg_close(); } -static void process_samples(struct perf_session *session) -{ - struct sample_wrapper *cursor; - event_t *event; - - sort_queued_samples(); - - cursor = all_samples; - while (cursor) { - event = (void *)&cursor->data; - cursor = cursor->next; - process_sample_event(event, session); - } -} - static struct perf_event_ops event_ops = { - .comm = process_comm_event, - .fork = process_fork_event, - .exit = process_exit_event, - .sample = queue_sample_event, + .comm = process_comm_event, + .fork = process_fork_event, + .exit = process_exit_event, + .sample = process_sample_event, + .ordered_samples = true, }; static int __cmd_timechart(void) @@ -1048,8 +949,6 @@ static int __cmd_timechart(void) if (ret) goto out_delete; - process_samples(session); - end_sample_processing(); sort_pids(); @@ -1072,7 +971,6 @@ static const char *record_args[] = { "record", "-a", "-R", - "-M", "-f", "-c", "1", "-e", "power:power_start", -- cgit v0.10.2 From e1889d75aff0c3786bc53aeb7d9eaca0691c19c5 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 24 Apr 2010 01:55:09 +0200 Subject: perf: Add a perf trace option to check samples ordering reliability To ensure sample events time reordering is reliable, add a -d option to perf trace to check that automatically. Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Tom Zanussi diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1d034f6..77f556f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -11,6 +11,8 @@ static char const *script_name; static char const *generate_script_lang; +static bool debug_ordering; +static u64 last_timestamp; static int default_start_script(const char *script __unused, int argc __unused, @@ -87,6 +89,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) } if (session->sample_type & PERF_SAMPLE_RAW) { + if (debug_ordering) { + if (data.time < last_timestamp) { + pr_err("Samples misordered, previous: %llu " + "this: %llu\n", last_timestamp, + data.time); + } + last_timestamp = data.time; + } /* * FIXME: better resolve from pid from the struct trace_entry * field, although it should be the same than this perf @@ -532,6 +542,8 @@ static const struct option options[] = { "generate perf-trace.xx script in specified language"), OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_BOOLEAN('d', "debug-ordering", &debug_ordering, + "check that samples time ordering is monotonic"), OPT_END() }; -- cgit v0.10.2 From cfadf9d4ac4be940595ab73d3def24c23c8b875f Mon Sep 17 00:00:00 2001 From: William Cohen Date: Fri, 23 Apr 2010 16:36:21 -0400 Subject: perf: Some perf-kvm documentation edits asciidoc does not allow the "===" to be longer than the line above it. Also fix a couple types and formatting errors. Signed-off-by: William Cohen Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Ingo Molnar LKML-Reference: <4BD204C5.9000504@redhat.com> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 93400a0..d004e19 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -1,5 +1,5 @@ perf-kvm(1) -============== +=========== NAME ---- @@ -41,9 +41,9 @@ There are a couple of variants of perf kvm: OPTIONS ------- --host=:: - Collect host side perforamnce profile. + Collect host side performance profile. --guest=:: - Collect guest side perforamnce profile. + Collect guest side performance profile. --guestmount=:: Guest os root file system mount directory. Users mounts guest os root directories under by a specific filesystem access method, @@ -64,4 +64,5 @@ OPTIONS SEE ALSO -------- -linkperf:perf-top[1] perf-record[1] perf-report[1] perf-diff[1] perf-buildid-list[1] +linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1], +linkperf:perf-diff[1], linkperf:perf-buildid-list[1] -- cgit v0.10.2 From 0ab061cd523a7f2dbf1b59aab0542cb0ab2e4633 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 21 Apr 2010 15:56:16 -0400 Subject: perf tools: Initialize dso->node member in dso__new If dso->node member is not initialized, it causes a segmentation fault when adding to other lists. It should be initilized in dso__new(). Signed-off-by: Masami Hiramatsu Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar LKML-Reference: : <20100421195616.24664.89980.stgit@localhost6.localdomain6> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e782e7d..e77c33a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -189,6 +189,7 @@ struct dso *dso__new(const char *name) self->sorted_by_name = 0; self->has_build_id = 0; self->kernel = DSO_TYPE_USER; + INIT_LIST_HEAD(&self->node); } return self; -- cgit v0.10.2 From 15eca306ec95e164d05457f9f27c722f69af6d18 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 21 Apr 2010 15:56:24 -0400 Subject: perf probe: Fix to use symtab only if no debuginfo Fix perf probe to use symtab only if there is no debuginfo, because debuginfo has more information than symtab. If we can't find a function in debuginfo, we never find it in symtab. Signed-off-by: Masami Hiramatsu Reported-by: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar LKML-Reference: <20100421195624.24664.46214.stgit@localhost6.localdomain6> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4fb4803..5d3baec 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -180,15 +180,16 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, return -ENOENT; } /* Error path : ntevs < 0 */ - if (need_dwarf) { - if (ntevs == -EBADF) - pr_warning("No dwarf info found in the vmlinux - " - "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - return ntevs; + pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); + if (ntevs == -EBADF) { + pr_warning("Warning: No dwarf info found in the vmlinux - " + "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); + if (!need_dwarf) { + pr_debug("Trying to use symbols.\nn"); + return 0; + } } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - return 0; + return ntevs; } #define LINEBUF_SIZE 256 -- cgit v0.10.2 From 5d1ee0413c8e2e0aa48510b1edfb3c4d2d43455b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 21 Apr 2010 15:56:32 -0400 Subject: perf probe: Fix to exit callback soon after finding too many probe points Fix to exit callback soon after finding too many probe points. Don't try to continue searching because it already failed. Signed-off-by: Masami Hiramatsu Reported-by: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar LKML-Reference: <20100421195632.24664.42598.stgit@localhost6.localdomain6> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e7ee52f..0d795bc 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -871,6 +871,8 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) (uintmax_t)pf->addr); param->retval = convert_probe_point(in_die, pf); + if (param->retval < 0) + return DWARF_CB_ABORT; } return DWARF_CB_OK; @@ -1106,6 +1108,8 @@ static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) return DWARF_CB_OK; param->retval = line_range_add_line(src, lineno, lf->lr); + if (param->retval < 0) + return DWARF_CB_ABORT; return DWARF_CB_OK; } -- cgit v0.10.2 From ef4a356574426877d569f8b6579325537eb7909b Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 21 Apr 2010 15:56:40 -0400 Subject: perf probe: Add --max-probes option Add --max-probes option to change the maximum limit of findable probe points per event, since inlined function can be expanded into thousands of probe points. Default value is 128. Signed-off-by: Masami Hiramatsu Suggested-by: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar LKML-Reference: <20100421195640.24664.62984.stgit@localhost6.localdomain6> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 63c25d3..94a258c 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -62,6 +62,9 @@ OPTIONS Dry run. With this option, --add and --del doesn't execute actual adding and removal operations. +--max-probes:: + Set the maximum number of probe points for an event. Default is 128. + PROBE SYNTAX ------------ Probe points are defined by following syntax. diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index c1e5403..61c6d70 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -54,6 +54,7 @@ static struct { struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; + int max_probe_points; } params; @@ -179,6 +180,8 @@ static const struct option options[] = { "file", "vmlinux pathname"), #endif OPT__DRY_RUN(&probe_event_dry_run), + OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, + "Set how many probe points can be found for a probe."), OPT_END() }; @@ -200,6 +203,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } } + if (params.max_probe_points == 0) + params.max_probe_points = MAX_PROBES; + if ((!params.nevents && !params.dellist && !params.list_events && !params.show_lines)) usage_with_options(probe_usage, options); @@ -246,7 +252,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (params.nevents) { ret = add_perf_probe_events(params.events, params.nevents, - params.force_add); + params.force_add, + params.max_probe_points); if (ret < 0) { pr_err(" Error: Failed to add events. (%d)\n", ret); return ret; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5d3baec..9ded38c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -150,7 +150,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, /* Try to find perf_probe_event with debuginfo */ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, + int max_tevs) { bool need_dwarf = perf_probe_event_need_dwarf(pev); int fd, ntevs; @@ -166,7 +167,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, } /* Searching trace events corresponding to probe event */ - ntevs = find_kprobe_trace_events(fd, pev, tevs); + ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); close(fd); if (ntevs > 0) { /* Succeeded to find trace events */ @@ -318,7 +319,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, } static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs __unused) + struct kprobe_trace_event **tevs __unused, + int max_tevs __unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); @@ -1408,14 +1410,15 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, } static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, + int max_tevs) { struct symbol *sym; int ret = 0, i; struct kprobe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_kprobe_trace_events(pev, tevs); + ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); if (ret != 0) return ret; @@ -1487,7 +1490,7 @@ struct __event_package { }; int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add) + bool force_add, int max_tevs) { int i, j, ret; struct __event_package *pkgs; @@ -1506,7 +1509,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ ret = convert_to_kprobe_trace_events(pkgs[i].pev, - &pkgs[i].tevs); + &pkgs[i].tevs, max_tevs); if (ret < 0) goto end; pkgs[i].ntevs = ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e7ff0d0..e9db1a2 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -115,8 +115,8 @@ extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); extern int parse_line_range_desc(const char *cmd, struct line_range *lr); -extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, - bool force_add); +extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add, int max_probe_points); extern int del_perf_probe_events(struct strlist *dellist); extern int show_perf_probe_events(void); extern int show_line_range(struct line_range *lr); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 0d795bc..562b144 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -626,8 +626,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Attribute fb_attr; size_t nops; - if (pf->ntevs == MAX_PROBES) { - pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES); + if (pf->ntevs == pf->max_tevs) { + pr_warning("Too many( > %d) probe point found.\n", + pf->max_tevs); return -ERANGE; } tev = &pf->tevs[pf->ntevs++]; @@ -932,9 +933,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, int max_tevs) { - struct probe_finder pf = {.pev = pev}; + struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; @@ -942,7 +943,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, Dwarf *dbg; int ret = 0; - pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); if (pf.tevs == NULL) return -ENOMEM; *tevs = pf.tevs; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 310ce89..66f1980 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -18,7 +18,8 @@ static inline int is_c_varname(const char *name) #ifdef DWARF_SUPPORT /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs); + struct kprobe_trace_event **tevs, + int max_tevs); /* Find a perf_probe_point from debuginfo */ extern int find_perf_probe_point(int fd, unsigned long addr, @@ -32,7 +33,8 @@ extern int find_line_range(int fd, struct line_range *lr); struct probe_finder { struct perf_probe_event *pev; /* Target probe event */ struct kprobe_trace_event *tevs; /* Result trace events */ - int ntevs; /* number of trace events */ + int ntevs; /* Number of trace events */ + int max_tevs; /* Max number of trace events */ /* For function searching */ int lno; /* Line number */ -- cgit v0.10.2 From f93830fbb06b67848c762f2177c06cc3cbb97deb Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 26 Apr 2010 15:39:54 -0300 Subject: perf tools: Fix libdw-dev package name in error message The headers required for DWARF support are provided by the libdw-dev package in Debian-based distros. This patch corrects the elfutils-dev package name to libdw-dev in the Makefile error message when libdw.h is not found. Signed-off-by: Stefan Hajnoczi Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <1272292023-9869-1-git-send-email-stefanha@linux.vnet.ibm.com> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e8bf2e1..3ac6b67 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -504,7 +504,7 @@ PERFLIBS = $(LIB_FILE) ifndef NO_DWARF ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); + msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support endif # NO_DWARF -- cgit v0.10.2 From 23346f21b277e3aae5e9989e711a11cbe8133a45 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Apr 2010 21:17:50 -0300 Subject: perf tools: Rename "kernel_info" to "machine" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct kernel_info and kerninfo__ are too vague, what they really describe are machines, virtual ones or hosts. There are more changes to introduce helpers to shorten function calls and to make more clear what is really being done, but I left that for subsequent patches. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Zhang, Yanmin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index f924b43..986b2ef 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -571,7 +571,7 @@ static int __cmd_annotate(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(&session->kerninfo_root, stdout); + dsos__fprintf(&session->machines, stdout); perf_session__collapse_resort(&session->hists); perf_session__output_resort(&session->hists, session->event_total[0]); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 623afe3..b4a265a 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - dsos__fprintf_buildid(&session->kerninfo_root, stdout, with_hits); + dsos__fprintf_buildid(&session->machines, stdout, with_hits); perf_session__delete(session); return err; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index ab906cb..2067475 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -352,7 +352,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, int n_lines, int is_caller) { struct rb_node *next; - struct kernel_info *kerninfo; + struct machine *machine; printf("%.102s\n", graph_dotted_line); printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); @@ -361,8 +361,8 @@ static void __print_result(struct rb_root *root, struct perf_session *session, next = rb_first(root); - kerninfo = kerninfo__findhost(&session->kerninfo_root); - if (!kerninfo) { + machine = perf_session__find_host_machine(session); + if (!machine) { pr_err("__print_result: couldn't find kernel information\n"); return; } @@ -370,7 +370,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; - struct map_groups *kmaps = &kerninfo->kmaps; + struct map_groups *kmaps = &machine->kmaps; struct map *map; char buf[BUFSIZ]; u64 addr; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 27f992a..83b308a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -456,14 +456,14 @@ static void atexit_header(void) } } -static void event__synthesize_guest_os(struct kernel_info *kerninfo, - void *data __attribute__((unused))) +static void event__synthesize_guest_os(struct machine *machine, void *data) { int err; char *guest_kallsyms; char path[PATH_MAX]; + struct perf_session *psession = data; - if (is_host_kernel(kerninfo)) + if (machine__is_host(machine)) return; /* @@ -475,16 +475,15 @@ static void event__synthesize_guest_os(struct kernel_info *kerninfo, *in module instead of in guest kernel. */ err = event__synthesize_modules(process_synthesized_event, - session, - kerninfo); + psession, machine); if (err < 0) pr_err("Couldn't record guest kernel [%d]'s reference" - " relocation symbol.\n", kerninfo->pid); + " relocation symbol.\n", machine->pid); - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; else { - sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + sprintf(path, "%s/proc/kallsyms", machine->root_dir); guest_kallsyms = path; } @@ -493,13 +492,13 @@ static void event__synthesize_guest_os(struct kernel_info *kerninfo, * have no _text sometimes. */ err = event__synthesize_kernel_mmap(process_synthesized_event, - session, kerninfo, "_text"); + psession, machine, "_text"); if (err < 0) err = event__synthesize_kernel_mmap(process_synthesized_event, - session, kerninfo, "_stext"); + psession, machine, "_stext"); if (err < 0) pr_err("Couldn't record guest kernel [%d]'s reference" - " relocation symbol.\n", kerninfo->pid); + " relocation symbol.\n", machine->pid); } static int __cmd_record(int argc, const char **argv) @@ -513,7 +512,7 @@ static int __cmd_record(int argc, const char **argv) int child_ready_pipe[2], go_pipe[2]; const bool forks = argc > 0; char buf; - struct kernel_info *kerninfo; + struct machine *machine; page_size = sysconf(_SC_PAGE_SIZE); @@ -682,31 +681,30 @@ static int __cmd_record(int argc, const char **argv) advance_output(err); } - kerninfo = kerninfo__findhost(&session->kerninfo_root); - if (!kerninfo) { + machine = perf_session__find_host_machine(session); + if (!machine) { pr_err("Couldn't find native kernel information.\n"); return -1; } err = event__synthesize_kernel_mmap(process_synthesized_event, - session, kerninfo, "_text"); + session, machine, "_text"); if (err < 0) err = event__synthesize_kernel_mmap(process_synthesized_event, - session, kerninfo, "_stext"); + session, machine, "_stext"); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } err = event__synthesize_modules(process_synthesized_event, - session, kerninfo); + session, machine); if (err < 0) { pr_err("Couldn't record kernel reference relocation symbol.\n"); return err; } if (perf_guest) - kerninfo__process_allkernels(&session->kerninfo_root, - event__synthesize_guest_os, session); + perf_session__process_machines(session, event__synthesize_guest_os); if (!system_wide && profile_cpu == -1) event__synthesize_thread(target_tid, process_synthesized_event, diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 816edae..49cc367 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -313,7 +313,7 @@ static int __cmd_report(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(&session->kerninfo_root, stdout); + dsos__fprintf(&session->machines, stdout); next = rb_first(&session->stats_by_id); while (next) { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index dfd7ea7..c390f34 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -854,7 +854,7 @@ static void handle_keypress(struct perf_session *session, int c) case 'Q': printf("exiting.\n"); if (dump_symtab) - dsos__fprintf(&session->kerninfo_root, stderr); + dsos__fprintf(&session->machines, stderr); exit(0); case 's': prompt_symbol(&sym_filter_entry, "Enter details symbol"); @@ -982,7 +982,7 @@ static void event__process_sample(const event_t *self, u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; - struct kernel_info *kerninfo; + struct machine *machine; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; ++samples; @@ -992,18 +992,17 @@ static void event__process_sample(const event_t *self, ++us_samples; if (hide_user_symbols) return; - kerninfo = kerninfo__findhost(&session->kerninfo_root); + machine = perf_session__find_host_machine(session); break; case PERF_RECORD_MISC_KERNEL: ++kernel_samples; if (hide_kernel_symbols) return; - kerninfo = kerninfo__findhost(&session->kerninfo_root); + machine = perf_session__find_host_machine(session); break; case PERF_RECORD_MISC_GUEST_KERNEL: ++guest_kernel_samples; - kerninfo = kerninfo__find(&session->kerninfo_root, - self->ip.pid); + machine = perf_session__find_machine(session, self->ip.pid); break; case PERF_RECORD_MISC_GUEST_USER: ++guest_us_samples; @@ -1016,7 +1015,7 @@ static void event__process_sample(const event_t *self, return; } - if (!kerninfo && perf_guest) { + if (!machine && perf_guest) { pr_err("Can't find guest [%d]'s kernel information\n", self->ip.pid); return; @@ -1041,7 +1040,7 @@ static void event__process_sample(const event_t *self, * --hide-kernel-symbols, even if the user specifies an * invalid --vmlinux ;-) */ - if (al.map == kerninfo->vmlinux_maps[MAP__FUNCTION] && + if (al.map == machine->vmlinux_maps[MAP__FUNCTION] && RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { pr_err("The %s file can't be used\n", symbol_conf.vmlinux_name); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e3fa8d3..2f33ca9 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -172,17 +172,17 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, int event__synthesize_modules(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo) + struct machine *machine) { struct rb_node *nd; - struct map_groups *kmaps = &kerninfo->kmaps; + struct map_groups *kmaps = &machine->kmaps; u16 misc; /* * kernel uses 0 for user space maps, see kernel/perf_event.c * __perf_event_mmap */ - if (is_host_kernel(kerninfo)) + if (machine__is_host(machine)) misc = PERF_RECORD_MISC_KERNEL; else misc = PERF_RECORD_MISC_GUEST_KERNEL; @@ -204,7 +204,7 @@ int event__synthesize_modules(event__handler_t process, (sizeof(ev.mmap.filename) - size)); ev.mmap.start = pos->start; ev.mmap.len = pos->end - pos->start; - ev.mmap.pid = kerninfo->pid; + ev.mmap.pid = machine->pid; memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); @@ -267,7 +267,7 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo, + struct machine *machine, const char *symbol_name) { size_t size; @@ -288,8 +288,8 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - mmap_name = kern_mmap_name(kerninfo, name_buff); - if (is_host_kernel(kerninfo)) { + mmap_name = machine__mmap_name(machine, name_buff); + if (machine__is_host(machine)) { /* * kernel uses PERF_RECORD_MISC_USER for user space maps, * see kernel/perf_event.c __perf_event_mmap @@ -298,10 +298,10 @@ int event__synthesize_kernel_mmap(event__handler_t process, filename = "/proc/kallsyms"; } else { ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) filename = (char *) symbol_conf.default_guest_kallsyms; else { - sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + sprintf(path, "%s/proc/kallsyms", machine->root_dir); filename = path; } } @@ -309,7 +309,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) return -ENOENT; - map = kerninfo->vmlinux_maps[MAP__FUNCTION]; + map = machine->vmlinux_maps[MAP__FUNCTION]; size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), "%s%s", mmap_name, symbol_name) + 1; size = ALIGN(size, sizeof(u64)); @@ -318,7 +318,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, ev.mmap.pgoff = args.start; ev.mmap.start = map->start; ev.mmap.len = map->end - ev.mmap.start; - ev.mmap.pid = kerninfo->pid; + ev.mmap.pid = machine->pid; return process(&ev, session); } @@ -389,18 +389,18 @@ static int event__process_kernel_mmap(event_t *self, { struct map *map; char kmmap_prefix[PATH_MAX]; - struct kernel_info *kerninfo; + struct machine *machine; enum dso_kernel_type kernel_type; bool is_kernel_mmap; - kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid); - if (!kerninfo) { - pr_err("Can't find id %d's kerninfo\n", self->mmap.pid); + machine = perf_session__findnew_machine(session, self->mmap.pid); + if (!machine) { + pr_err("Can't find id %d's machine\n", self->mmap.pid); goto out_problem; } - kern_mmap_name(kerninfo, kmmap_prefix); - if (is_host_kernel(kerninfo)) + machine__mmap_name(machine, kmmap_prefix); + if (machine__is_host(machine)) kernel_type = DSO_TYPE_KERNEL; else kernel_type = DSO_TYPE_GUEST_KERNEL; @@ -429,10 +429,9 @@ static int event__process_kernel_mmap(event_t *self, } else strcpy(short_module_name, self->mmap.filename); - map = map_groups__new_module(&kerninfo->kmaps, - self->mmap.start, - self->mmap.filename, - kerninfo); + map = map_groups__new_module(&machine->kmaps, + self->mmap.start, + self->mmap.filename, machine); if (map == NULL) goto out_problem; @@ -449,27 +448,27 @@ static int event__process_kernel_mmap(event_t *self, * Should be there already, from the build-id table in * the header. */ - struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel, - kmmap_prefix); + struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, + kmmap_prefix); if (kernel == NULL) goto out_problem; kernel->kernel = kernel_type; - if (__map_groups__create_kernel_maps(&kerninfo->kmaps, - kerninfo->vmlinux_maps, kernel) < 0) + if (__map_groups__create_kernel_maps(&machine->kmaps, + machine->vmlinux_maps, + kernel) < 0) goto out_problem; - event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self); - perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps, - symbol_name, - self->mmap.pgoff); - if (is_default_guest(kerninfo)) { + event_set_kernel_mmap_len(machine->vmlinux_maps, self); + perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, + symbol_name, + self->mmap.pgoff); + if (machine__is_default_guest(machine)) { /* * preload dso of guest kernel and modules */ - dso__load(kernel, - kerninfo->vmlinux_maps[MAP__FUNCTION], - NULL); + dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], + NULL); } } return 0; @@ -479,7 +478,7 @@ out_problem: int event__process_mmap(event_t *self, struct perf_session *session) { - struct kernel_info *kerninfo; + struct machine *machine; struct thread *thread; struct map *map; u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -498,8 +497,8 @@ int event__process_mmap(event_t *self, struct perf_session *session) } thread = perf_session__findnew(session, self->mmap.pid); - kerninfo = kerninfo__findhost(&session->kerninfo_root); - map = map__new(&kerninfo->dsos__user, self->mmap.start, + machine = perf_session__find_host_machine(session); + map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, MAP__FUNCTION, session->cwd, session->cwdlen); @@ -546,7 +545,7 @@ void thread__find_addr_map(struct thread *self, struct addr_location *al) { struct map_groups *mg = &self->mg; - struct kernel_info *kerninfo = NULL; + struct machine *machine = NULL; al->thread = self; al->addr = addr; @@ -555,19 +554,19 @@ void thread__find_addr_map(struct thread *self, if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; - kerninfo = kerninfo__findhost(&session->kerninfo_root); - mg = &kerninfo->kmaps; + machine = perf_session__find_host_machine(session); + mg = &machine->kmaps; } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; - kerninfo = kerninfo__findhost(&session->kerninfo_root); + machine = perf_session__find_host_machine(session); } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { al->level = 'g'; - kerninfo = kerninfo__find(&session->kerninfo_root, pid); - if (!kerninfo) { + machine = perf_session__find_machine(session, pid); + if (!machine) { al->map = NULL; return; } - mg = &kerninfo->kmaps; + mg = &machine->kmaps; } else { /* * 'u' means guest os user space. @@ -603,10 +602,9 @@ try_again: * in the whole kernel symbol list. */ if ((long long)al->addr < 0 && - cpumode == PERF_RECORD_MISC_KERNEL && - kerninfo && - mg != &kerninfo->kmaps) { - mg = &kerninfo->kmaps; + cpumode == PERF_RECORD_MISC_KERNEL && + machine && mg != &machine->kmaps) { + mg = &machine->kmaps; goto try_again; } } else diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4af2ed5..b364da5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -156,12 +156,12 @@ void event__synthesize_threads(event__handler_t process, struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo, + struct machine *machine, const char *symbol_name); int event__synthesize_modules(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo); + struct machine *machine); int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 75d0167..6227dc4 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -229,10 +229,9 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) int err = 0; u16 kmisc, umisc; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - if (is_host_kernel(pos)) { + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + if (machine__is_host(pos)) { kmisc = PERF_RECORD_MISC_KERNEL; umisc = PERF_RECORD_MISC_USER; } else { @@ -240,11 +239,11 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) umisc = PERF_RECORD_MISC_GUEST_USER; } - err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid, - kmisc, fd); + err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid, + kmisc, fd); if (err == 0) - err = __dsos__write_buildid_table(&pos->dsos__user, - pos->pid, umisc, fd); + err = __dsos__write_buildid_table(&pos->user_dsos, + pos->pid, umisc, fd); if (err) break; } @@ -378,11 +377,10 @@ static int dsos__cache_build_ids(struct perf_header *self) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir); - ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir); + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir); + ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir); } return ret ? -1 : 0; } @@ -394,11 +392,10 @@ static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) struct perf_session, header); struct rb_node *nd; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits); - ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits); + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits); + ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits); } return ret; @@ -685,13 +682,13 @@ static int __event_process_build_id(struct build_id_event *bev, { int err = -1; struct list_head *head; - struct kernel_info *kerninfo; + struct machine *machine; u16 misc; struct dso *dso; enum dso_kernel_type dso_type; - kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid); - if (!kerninfo) + machine = perf_session__findnew_machine(session, bev->pid); + if (!machine) goto out; misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -699,16 +696,16 @@ static int __event_process_build_id(struct build_id_event *bev, switch (misc) { case PERF_RECORD_MISC_KERNEL: dso_type = DSO_TYPE_KERNEL; - head = &kerninfo->dsos__kernel; + head = &machine->kernel_dsos; break; case PERF_RECORD_MISC_GUEST_KERNEL: dso_type = DSO_TYPE_GUEST_KERNEL; - head = &kerninfo->dsos__kernel; + head = &machine->kernel_dsos; break; case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_GUEST_USER: dso_type = DSO_TYPE_USER; - head = &kerninfo->dsos__user; + head = &machine->user_dsos; break; default: goto out; @@ -1113,8 +1110,7 @@ int event__process_tracing_data(event_t *self, } int event__synthesize_build_id(struct dso *pos, u16 misc, - event__handler_t process, - struct kernel_info *kerninfo, + event__handler_t process, struct machine *machine, struct perf_session *session) { event_t ev; @@ -1131,7 +1127,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc; - ev.build_id.pid = kerninfo->pid; + ev.build_id.pid = machine->pid; ev.build_id.header.size = sizeof(ev.build_id) + len; memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); @@ -1142,7 +1138,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, static int __event_synthesize_build_ids(struct list_head *head, u16 misc, event__handler_t process, - struct kernel_info *kerninfo, + struct machine *machine, struct perf_session *session) { struct dso *pos; @@ -1153,7 +1149,7 @@ static int __event_synthesize_build_ids(struct list_head *head, u16 misc, continue; err = event__synthesize_build_id(pos, misc, process, - kerninfo, session); + machine, session); if (err < 0) return err; } @@ -1166,15 +1162,15 @@ int event__synthesize_build_ids(event__handler_t process, { int err = 0; u16 kmisc, umisc; - struct kernel_info *pos; + struct machine *pos; struct rb_node *nd; if (!dsos__read_build_ids(&session->header, true)) return 0; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - pos = rb_entry(nd, struct kernel_info, rb_node); - if (is_host_kernel(pos)) { + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + pos = rb_entry(nd, struct machine, rb_node); + if (machine__is_host(pos)) { kmisc = PERF_RECORD_MISC_KERNEL; umisc = PERF_RECORD_MISC_USER; } else { @@ -1182,11 +1178,11 @@ int event__synthesize_build_ids(event__handler_t process, umisc = PERF_RECORD_MISC_GUEST_USER; } - err = __event_synthesize_build_ids(&pos->dsos__kernel, - kmisc, process, pos, session); + err = __event_synthesize_build_ids(&pos->kernel_dsos, kmisc, + process, pos, session); if (err == 0) - err = __event_synthesize_build_ids(&pos->dsos__user, - umisc, process, pos, session); + err = __event_synthesize_build_ids(&pos->user_dsos, umisc, + process, pos, session); if (err) break; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 2759154..f39443d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -120,7 +120,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, - struct kernel_info *kerninfo, + struct machine *machine, struct perf_session *session); int event__synthesize_build_ids(event__handler_t process, struct perf_session *session); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 7facd01..da3d4e8 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -245,7 +245,7 @@ void map_groups__init(struct map_groups *self) self->maps[i] = RB_ROOT; INIT_LIST_HEAD(&self->removed_maps[i]); } - self->this_kerninfo = NULL; + self->machine = NULL; } void map_groups__flush(struct map_groups *self) @@ -513,133 +513,130 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } -struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, - pid_t pid, const char *root_dir) +struct machine *machines__add(struct rb_root *self, pid_t pid, + const char *root_dir) { - struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node **p = &self->rb_node; struct rb_node *parent = NULL; - struct kernel_info *kerninfo, *pos; + struct machine *pos, *machine = malloc(sizeof(*machine)); - kerninfo = malloc(sizeof(struct kernel_info)); - if (!kerninfo) + if (!machine) return NULL; - kerninfo->pid = pid; - map_groups__init(&kerninfo->kmaps); - kerninfo->root_dir = strdup(root_dir); - RB_CLEAR_NODE(&kerninfo->rb_node); - INIT_LIST_HEAD(&kerninfo->dsos__user); - INIT_LIST_HEAD(&kerninfo->dsos__kernel); - kerninfo->kmaps.this_kerninfo = kerninfo; + machine->pid = pid; + map_groups__init(&machine->kmaps); + machine->root_dir = strdup(root_dir); + RB_CLEAR_NODE(&machine->rb_node); + INIT_LIST_HEAD(&machine->user_dsos); + INIT_LIST_HEAD(&machine->kernel_dsos); + machine->kmaps.machine = machine; while (*p != NULL) { parent = *p; - pos = rb_entry(parent, struct kernel_info, rb_node); + pos = rb_entry(parent, struct machine, rb_node); if (pid < pos->pid) p = &(*p)->rb_left; else p = &(*p)->rb_right; } - rb_link_node(&kerninfo->rb_node, parent, p); - rb_insert_color(&kerninfo->rb_node, kerninfo_root); + rb_link_node(&machine->rb_node, parent, p); + rb_insert_color(&machine->rb_node, self); - return kerninfo; + return machine; } -struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid) +struct machine *machines__find(struct rb_root *self, pid_t pid) { - struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node **p = &self->rb_node; struct rb_node *parent = NULL; - struct kernel_info *kerninfo; - struct kernel_info *default_kerninfo = NULL; + struct machine *machine; + struct machine *default_machine = NULL; while (*p != NULL) { parent = *p; - kerninfo = rb_entry(parent, struct kernel_info, rb_node); - if (pid < kerninfo->pid) + machine = rb_entry(parent, struct machine, rb_node); + if (pid < machine->pid) p = &(*p)->rb_left; - else if (pid > kerninfo->pid) + else if (pid > machine->pid) p = &(*p)->rb_right; else - return kerninfo; - if (!kerninfo->pid) - default_kerninfo = kerninfo; + return machine; + if (!machine->pid) + default_machine = machine; } - return default_kerninfo; + return default_machine; } -struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root) +/* + * FIXME: Why repeatedly search for this? + */ +struct machine *machines__find_host(struct rb_root *self) { - struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node **p = &self->rb_node; struct rb_node *parent = NULL; - struct kernel_info *kerninfo; + struct machine *machine; pid_t pid = HOST_KERNEL_ID; while (*p != NULL) { parent = *p; - kerninfo = rb_entry(parent, struct kernel_info, rb_node); - if (pid < kerninfo->pid) + machine = rb_entry(parent, struct machine, rb_node); + if (pid < machine->pid) p = &(*p)->rb_left; - else if (pid > kerninfo->pid) + else if (pid > machine->pid) p = &(*p)->rb_right; else - return kerninfo; + return machine; } return NULL; } -struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid) +struct machine *machines__findnew(struct rb_root *self, pid_t pid) { char path[PATH_MAX]; const char *root_dir; - int ret; - struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid); + struct machine *machine = machines__find(self, pid); - if (!kerninfo || kerninfo->pid != pid) { + if (!machine || machine->pid != pid) { if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) root_dir = ""; else { if (!symbol_conf.guestmount) goto out; sprintf(path, "%s/%d", symbol_conf.guestmount, pid); - ret = access(path, R_OK); - if (ret) { + if (access(path, R_OK)) { pr_err("Can't access file %s\n", path); goto out; } root_dir = path; } - kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir); + machine = machines__add(self, pid, root_dir); } out: - return kerninfo; + return machine; } -void kerninfo__process_allkernels(struct rb_root *kerninfo_root, - process_kernel_info process, - void *data) +void machines__process(struct rb_root *self, machine__process_t process, void *data) { struct rb_node *nd; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); process(pos, data); } } -char *kern_mmap_name(struct kernel_info *kerninfo, char *buff) +char *machine__mmap_name(struct machine *self, char *buff) { - if (is_host_kernel(kerninfo)) + if (machine__is_host(self)) sprintf(buff, "[%s]", "kernel.kallsyms"); - else if (is_default_guest(kerninfo)) + else if (machine__is_default_guest(self)) sprintf(buff, "[%s]", "guest.kernel.kallsyms"); else - sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid); + sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", self->pid); return buff; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 30d38d6..4c1c2da 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "types.h" enum map_type { @@ -19,7 +20,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; struct dso; struct ref_reloc_sym; struct map_groups; -struct kernel_info; +struct machine; struct map { union { @@ -46,23 +47,23 @@ struct kmap { }; struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; - struct kernel_info *this_kerninfo; + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; + struct machine *machine; }; -/* Native host kernel uses -1 as pid index in kernel_info */ +/* Native host kernel uses -1 as pid index in machine */ #define HOST_KERNEL_ID (-1) #define DEFAULT_GUEST_KERNEL_ID (0) -struct kernel_info { - struct rb_node rb_node; - pid_t pid; - char *root_dir; - struct list_head dsos__user; - struct list_head dsos__kernel; +struct machine { + struct rb_node rb_node; + pid_t pid; + char *root_dir; + struct list_head user_dsos; + struct list_head kernel_dsos; struct map_groups kmaps; - struct map *vmlinux_maps[MAP__NR_TYPES]; + struct map *vmlinux_maps[MAP__NR_TYPES]; }; static inline struct kmap *map__kmap(struct map *self) @@ -124,36 +125,30 @@ int map_groups__clone(struct map_groups *self, size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); -struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, - pid_t pid, const char *root_dir); -struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid); -struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid); -struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root); -char *kern_mmap_name(struct kernel_info *kerninfo, char *buff); +typedef void (*machine__process_t)(struct machine *self, void *data); + +void machines__process(struct rb_root *self, machine__process_t process, void *data); +struct machine *machines__add(struct rb_root *self, pid_t pid, + const char *root_dir); +struct machine *machines__find_host(struct rb_root *self); +struct machine *machines__find(struct rb_root *self, pid_t pid); +struct machine *machines__findnew(struct rb_root *self, pid_t pid); +char *machine__mmap_name(struct machine *self, char *buff); /* * Default guest kernel is defined by parameter --guestkallsyms * and --guestmodules */ -static inline int is_default_guest(struct kernel_info *kerninfo) +static inline bool machine__is_default_guest(struct machine *self) { - if (!kerninfo) - return 0; - return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID; + return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false; } -static inline int is_host_kernel(struct kernel_info *kerninfo) +static inline bool machine__is_host(struct machine *self) { - if (!kerninfo) - return 0; - return kerninfo->pid == HOST_KERNEL_ID; + return self ? self->pid == HOST_KERNEL_ID : false; } -typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data); -void kerninfo__process_allkernels(struct rb_root *kerninfo_root, - process_kernel_info process, - void *data); - static inline void map_groups__insert(struct map_groups *self, struct map *map) { maps__insert(&self->maps[map->type], map); @@ -197,10 +192,8 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); -struct map *map_groups__new_module(struct map_groups *self, - u64 start, - const char *filename, - struct kernel_info *kerninfo); +struct map *map_groups__new_module(struct map_groups *self, u64 start, + const char *filename, struct machine *machine); void map_groups__flush(struct map_groups *self); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b7aade2..b745c1c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -70,7 +70,7 @@ void perf_session__update_sample_type(struct perf_session *self) int perf_session__create_kernel_maps(struct perf_session *self) { int ret; - struct rb_root *root = &self->kerninfo_root; + struct rb_root *root = &self->machines; ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); if (ret >= 0) @@ -97,7 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwd = NULL; self->cwdlen = 0; self->unknown_events = 0; - self->kerninfo_root = RB_ROOT; + self->machines = RB_ROOT; self->ordered_samples.flush_limit = ULLONG_MAX; INIT_LIST_HEAD(&self->ordered_samples.samples_head); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 796e229..7125272 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -25,7 +25,7 @@ struct perf_session { unsigned long mmap_window; struct rb_root threads; struct thread *last_match; - struct rb_root kerninfo_root; + struct rb_root machines; struct events_stats events_stats; struct rb_root stats_by_id; unsigned long event_total[PERF_RECORD_MAX]; @@ -102,4 +102,29 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, u64 session_total, const char *helpline, const char *input_name); #endif + +static inline +struct machine *perf_session__find_host_machine(struct perf_session *self) +{ + return machines__find_host(&self->machines); +} + +static inline +struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +{ + return machines__find(&self->machines, pid); +} + +static inline +struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +{ + return machines__findnew(&self->machines, pid); +} + +static inline +void perf_session__process_machines(struct perf_session *self, + machine__process_t process) +{ + return machines__process(&self->machines, process, self); +} #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e77c33a..dc04636 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -485,7 +485,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; - struct kernel_info *kerninfo = kmaps->this_kerninfo; + struct machine *machine = kmaps->machine; struct map *curr_map = map; struct symbol *pos; int count = 0; @@ -508,8 +508,8 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, if (strcmp(curr_map->dso->short_name, module)) { if (curr_map != map && - self->kernel == DSO_TYPE_GUEST_KERNEL && - is_default_guest(kerninfo)) { + self->kernel == DSO_TYPE_GUEST_KERNEL && + machine__is_default_guest(machine)) { /* * We assume all symbols of a module are * continuous in * kallsyms, so curr_map @@ -527,13 +527,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, pr_err("%s/proc/{kallsyms,modules} " "inconsistency while looking " "for \"%s\" module!\n", - kerninfo->root_dir, module); + machine->root_dir, module); curr_map = map; goto discard_symbol; } if (curr_map->dso->loaded && - !is_default_guest(kmaps->this_kerninfo)) + !machine__is_default_guest(machine)) goto discard_symbol; } /* @@ -586,7 +586,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); if (curr_map != map && self->kernel == DSO_TYPE_GUEST_KERNEL && - is_default_guest(kmaps->this_kerninfo)) { + machine__is_default_guest(kmaps->machine)) { dso__set_loaded(curr_map->dso, curr_map->type); } @@ -1291,7 +1291,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = -1; int fd; - struct kernel_info *kerninfo; + struct machine *machine; const char *root_dir; dso__set_loaded(self, map->type); @@ -1301,10 +1301,10 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) else if (self->kernel == DSO_TYPE_GUEST_KERNEL) return dso__load_guest_kernel_sym(self, map, filter); - if (map->groups && map->groups->this_kerninfo) - kerninfo = map->groups->this_kerninfo; + if (map->groups && map->groups->machine) + machine = map->groups->machine; else - kerninfo = NULL; + machine = NULL; name = malloc(size); if (!name) @@ -1359,8 +1359,8 @@ more: snprintf(name, size, "%s", self->long_name); break; case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->this_kerninfo) - root_dir = map->groups->this_kerninfo->root_dir; + if (map->groups && map->groups->machine) + root_dir = map->groups->machine->root_dir; else root_dir = ""; snprintf(name, size, "%s%s", root_dir, self->long_name); @@ -1566,12 +1566,12 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) struct map *map_groups__new_module(struct map_groups *self, u64 start, const char *filename, - struct kernel_info *kerninfo) + struct machine *machine) { struct map *map; struct dso *dso; - dso = __dsos__findnew(&kerninfo->dsos__kernel, filename); + dso = __dsos__findnew(&machine->kernel_dsos, filename); if (dso == NULL) return NULL; @@ -1579,7 +1579,7 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, if (map == NULL) return NULL; - if (is_host_kernel(kerninfo)) + if (machine__is_host(machine)) dso->origin = DSO__ORIG_KMODULE; else dso->origin = DSO__ORIG_GUEST_KMODULE; @@ -1587,7 +1587,7 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, return map; } -static int map_groups__create_modules(struct kernel_info *kerninfo) +static int map_groups__create_modules(struct machine *machine) { char *line = NULL; size_t n; @@ -1597,10 +1597,10 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) const char *modules; char path[PATH_MAX]; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) modules = symbol_conf.default_guest_modules; else { - sprintf(path, "%s/proc/modules", kerninfo->root_dir); + sprintf(path, "%s/proc/modules", machine->root_dir); modules = path; } @@ -1608,7 +1608,7 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) if (file == NULL) return -1; - root_dir = kerninfo->root_dir; + root_dir = machine->root_dir; while (!feof(file)) { char name[PATH_MAX]; @@ -1638,8 +1638,8 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = map_groups__new_module(&kerninfo->kmaps, - start, name, kerninfo); + map = map_groups__new_module(&machine->kmaps, start, + name, machine); if (map == NULL) goto out_delete_line; dso__kernel_module_get_build_id(map->dso, root_dir); @@ -1648,7 +1648,7 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) free(line); fclose(file); - return map_groups__set_modules_path(&kerninfo->kmaps, root_dir); + return map_groups__set_modules_path(&machine->kmaps, root_dir); out_delete_line: free(line); @@ -1820,16 +1820,16 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, { int err; const char *kallsyms_filename = NULL; - struct kernel_info *kerninfo; + struct machine *machine; char path[PATH_MAX]; if (!map->groups) { pr_debug("Guest kernel map hasn't the point to groups\n"); return -1; } - kerninfo = map->groups->this_kerninfo; + machine = map->groups->machine; - if (is_default_guest(kerninfo)) { + if (machine__is_default_guest(machine)) { /* * if the user specified a vmlinux filename, use it and only * it, reporting errors to the user if it cannot be used. @@ -1845,7 +1845,7 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + sprintf(path, "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } @@ -1856,9 +1856,8 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, out_try_fixup: if (err > 0) { if (kallsyms_filename != NULL) { - kern_mmap_name(kerninfo, path); - dso__set_long_name(self, - strdup(path)); + machine__mmap_name(machine, path); + dso__set_long_name(self, strdup(path)); } map__fixup_start(map); map__fixup_end(map); @@ -1908,15 +1907,14 @@ static void __dsos__fprintf(struct list_head *head, FILE *fp) } } -void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp) +void dsos__fprintf(struct rb_root *machines, FILE *fp) { struct rb_node *nd; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - __dsos__fprintf(&pos->dsos__kernel, fp); - __dsos__fprintf(&pos->dsos__user, fp); + for (nd = rb_first(machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + __dsos__fprintf(&pos->kernel_dsos, fp); + __dsos__fprintf(&pos->user_dsos, fp); } } @@ -1935,19 +1933,15 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, - FILE *fp, bool with_hits) +size_t dsos__fprintf_buildid(struct rb_root *machines, FILE *fp, bool with_hits) { struct rb_node *nd; size_t ret = 0; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret += __dsos__fprintf_buildid(&pos->dsos__kernel, - fp, with_hits); - ret += __dsos__fprintf_buildid(&pos->dsos__user, - fp, with_hits); + for (nd = rb_first(machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits); + ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits); } return ret; } @@ -1964,14 +1958,12 @@ struct dso *dso__new_kernel(const char *name) return self; } -static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, +static struct dso *dso__new_guest_kernel(struct machine *machine, const char *name) { char buff[PATH_MAX]; - struct dso *self; + struct dso *self = dso__new(name ?: machine__mmap_name(machine, buff)); - kern_mmap_name(kerninfo, buff); - self = dso__new(name ?: buff); if (self != NULL) { dso__set_short_name(self, "[guest.kernel]"); self->kernel = DSO_TYPE_GUEST_KERNEL; @@ -1980,36 +1972,35 @@ static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, return self; } -void dso__read_running_kernel_build_id(struct dso *self, - struct kernel_info *kerninfo) +void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine) { char path[PATH_MAX]; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir); + sprintf(path, "%s/sys/kernel/notes", machine->root_dir); if (sysfs__read_build_id(path, self->build_id, sizeof(self->build_id)) == 0) self->has_build_id = true; } -static struct dso *dsos__create_kernel(struct kernel_info *kerninfo) +static struct dso *dsos__create_kernel(struct machine *machine) { const char *vmlinux_name = NULL; struct dso *kernel; - if (is_host_kernel(kerninfo)) { + if (machine__is_host(machine)) { vmlinux_name = symbol_conf.vmlinux_name; kernel = dso__new_kernel(vmlinux_name); } else { - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) vmlinux_name = symbol_conf.default_guest_vmlinux_name; - kernel = dso__new_guest_kernel(kerninfo, vmlinux_name); + kernel = dso__new_guest_kernel(machine, vmlinux_name); } if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel, kerninfo); - dsos__add(&kerninfo->dsos__kernel, kernel); + dso__read_running_kernel_build_id(kernel, machine); + dsos__add(&machine->kernel_dsos, kernel); } return kernel; } @@ -2154,29 +2145,28 @@ out_free_comm_list: return -1; } -int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid) +int map_groups__create_kernel_maps(struct rb_root *machines, pid_t pid) { - struct kernel_info *kerninfo; struct dso *kernel; + struct machine *machine = machines__findnew(machines, pid); - kerninfo = kerninfo__findnew(kerninfo_root, pid); - if (kerninfo == NULL) + if (machine == NULL) return -1; - kernel = dsos__create_kernel(kerninfo); + kernel = dsos__create_kernel(machine); if (kernel == NULL) return -1; - if (__map_groups__create_kernel_maps(&kerninfo->kmaps, - kerninfo->vmlinux_maps, kernel) < 0) + if (__map_groups__create_kernel_maps(&machine->kmaps, + machine->vmlinux_maps, kernel) < 0) return -1; if (symbol_conf.use_modules && - map_groups__create_modules(kerninfo) < 0) + map_groups__create_modules(machine) < 0) pr_debug("Problems creating module maps, continuing anyway...\n"); /* * Now that we have all the maps created, just set the ->end of them: */ - map_groups__fixup_end(&kerninfo->kmaps); + map_groups__fixup_end(&machine->kmaps); return 0; } @@ -2223,7 +2213,7 @@ char *strxfrchar(char *s, char from, char to) return s; } -int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) +int map_groups__create_guest_kernel_maps(struct rb_root *machines) { int ret = 0; struct dirent **namelist = NULL; @@ -2234,8 +2224,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) if (symbol_conf.default_guest_vmlinux_name || symbol_conf.default_guest_modules || symbol_conf.default_guest_kallsyms) { - map_groups__create_kernel_maps(kerninfo_root, - DEFAULT_GUEST_KERNEL_ID); + map_groups__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); } if (symbol_conf.guestmount) { @@ -2256,8 +2245,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) pr_debug("Can't access file %s\n", path); goto failure; } - map_groups__create_kernel_maps(kerninfo_root, - pid); + map_groups__create_kernel_maps(machines, pid); } failure: free(namelist); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 478f5ab..ed885b0 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -186,8 +186,7 @@ enum dso_origin { char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); -void dso__read_running_kernel_build_id(struct dso *self, - struct kernel_info *kerninfo); +void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, const char *name); -- cgit v0.10.2 From 48ea8f5470aa6f35244d1b218316705ea88c0259 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Apr 2010 21:19:05 -0300 Subject: perf machine: Pass buffer size to machine__mmap_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't blindly assume that the size of the buffer is enough, use snprintf. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Zhang, Yanmin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2f33ca9..7400e51 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -288,7 +288,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - mmap_name = machine__mmap_name(machine, name_buff); + mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); if (machine__is_host(machine)) { /* * kernel uses PERF_RECORD_MISC_USER for user space maps, @@ -399,7 +399,7 @@ static int event__process_kernel_mmap(event_t *self, goto out_problem; } - machine__mmap_name(machine, kmmap_prefix); + machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); if (machine__is_host(machine)) kernel_type = DSO_TYPE_KERNEL; else diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index da3d4e8..ee25ee9 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -629,14 +629,14 @@ void machines__process(struct rb_root *self, machine__process_t process, void *d } } -char *machine__mmap_name(struct machine *self, char *buff) +char *machine__mmap_name(struct machine *self, char *bf, size_t size) { if (machine__is_host(self)) - sprintf(buff, "[%s]", "kernel.kallsyms"); + snprintf(bf, size, "[%s]", "kernel.kallsyms"); else if (machine__is_default_guest(self)) - sprintf(buff, "[%s]", "guest.kernel.kallsyms"); + snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); else - sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", self->pid); + snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); - return buff; + return bf; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4c1c2da..6fabad1 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -133,7 +133,7 @@ struct machine *machines__add(struct rb_root *self, pid_t pid, struct machine *machines__find_host(struct rb_root *self); struct machine *machines__find(struct rb_root *self, pid_t pid); struct machine *machines__findnew(struct rb_root *self, pid_t pid); -char *machine__mmap_name(struct machine *self, char *buff); +char *machine__mmap_name(struct machine *self, char *bf, size_t size); /* * Default guest kernel is defined by parameter --guestkallsyms diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index dc04636..c9c0bdd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1856,7 +1856,7 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, out_try_fixup: if (err > 0) { if (kallsyms_filename != NULL) { - machine__mmap_name(machine, path); + machine__mmap_name(machine, path, sizeof(path)); dso__set_long_name(self, strdup(path)); } map__fixup_start(map); @@ -1961,8 +1961,8 @@ struct dso *dso__new_kernel(const char *name) static struct dso *dso__new_guest_kernel(struct machine *machine, const char *name) { - char buff[PATH_MAX]; - struct dso *self = dso__new(name ?: machine__mmap_name(machine, buff)); + char bf[PATH_MAX]; + struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf))); if (self != NULL) { dso__set_short_name(self, "[guest.kernel]"); -- cgit v0.10.2 From d28c62232e50eab202bcd3f19b5c7a25b8b900b6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Apr 2010 21:20:43 -0300 Subject: perf machine: Adopt some map_groups functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those functions operated on members now grouped in 'struct machine', so move those methods to this new class. The changes made to 'perf probe' shows that using this abstraction inserting probes on guests almost got supported for free. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Masami Hiramatsu Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Zhang, Yanmin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 2067475..1563561 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -370,7 +370,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session, struct alloc_stat *data = rb_entry(next, struct alloc_stat, node); struct symbol *sym = NULL; - struct map_groups *kmaps = &machine->kmaps; struct map *map; char buf[BUFSIZ]; u64 addr; @@ -378,8 +377,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = map_groups__find_function(kmaps, addr, - &map, NULL); + sym = machine__find_function(machine, addr, &map, NULL); } else addr = data->ptr; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 7400e51..1757b0f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -429,9 +429,8 @@ static int event__process_kernel_mmap(event_t *self, } else strcpy(short_module_name, self->mmap.filename); - map = map_groups__new_module(&machine->kmaps, - self->mmap.start, - self->mmap.filename, machine); + map = machine__new_module(machine, self->mmap.start, + self->mmap.filename); if (map == NULL) goto out_problem; @@ -454,9 +453,7 @@ static int event__process_kernel_mmap(event_t *self, goto out_problem; kernel->kernel = kernel_type; - if (__map_groups__create_kernel_maps(&machine->kmaps, - machine->vmlinux_maps, - kernel) < 0) + if (__machine__create_kernel_maps(machine, kernel) < 0) goto out_problem; event_set_kernel_mmap_len(machine->vmlinux_maps, self); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ee25ee9..44a4df6 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -513,6 +513,19 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } +int machine__init(struct machine *self, const char *root_dir, pid_t pid) +{ + map_groups__init(&self->kmaps); + RB_CLEAR_NODE(&self->rb_node); + INIT_LIST_HEAD(&self->user_dsos); + INIT_LIST_HEAD(&self->kernel_dsos); + + self->kmaps.machine = self; + self->pid = pid; + self->root_dir = strdup(root_dir); + return self->root_dir == NULL ? -ENOMEM : 0; +} + struct machine *machines__add(struct rb_root *self, pid_t pid, const char *root_dir) { @@ -523,13 +536,10 @@ struct machine *machines__add(struct rb_root *self, pid_t pid, if (!machine) return NULL; - machine->pid = pid; - map_groups__init(&machine->kmaps); - machine->root_dir = strdup(root_dir); - RB_CLEAR_NODE(&machine->rb_node); - INIT_LIST_HEAD(&machine->user_dsos); - INIT_LIST_HEAD(&machine->kernel_dsos); - machine->kmaps.machine = machine; + if (machine__init(machine, root_dir, pid) != 0) { + free(machine); + return NULL; + } while (*p != NULL) { parent = *p; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 6fabad1..881dba4 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -134,6 +134,7 @@ struct machine *machines__find_host(struct rb_root *self); struct machine *machines__find(struct rb_root *self, pid_t pid); struct machine *machines__findnew(struct rb_root *self, pid_t pid); char *machine__mmap_name(struct machine *self, char *bf, size_t size); +int machine__init(struct machine *self, const char *root_dir, pid_t pid); /* * Default guest kernel is defined by parameter --guestkallsyms @@ -172,11 +173,11 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, struct map **mapp, symbol_filter_t filter); -static inline -struct symbol *map_groups__find_function(struct map_groups *self, u64 addr, - struct map **mapp, symbol_filter_t filter) +static inline struct symbol *machine__find_function(struct machine *self, + u64 addr, struct map **mapp, + symbol_filter_t filter) { - return map_groups__find_symbol(self, MAP__FUNCTION, addr, mapp, filter); + return map_groups__find_symbol(&self->kmaps, MAP__FUNCTION, addr, mapp, filter); } static inline @@ -192,8 +193,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename, struct machine *machine); +struct map *machine__new_module(struct machine *self, u64 start, const char *filename); void map_groups__flush(struct map_groups *self); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9ded38c..914c670 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -72,8 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) } static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static struct map_groups kmap_groups; -static struct map *kmaps[MAP__NR_TYPES]; +static struct machine machine; /* Initialize symbol maps and path of vmlinux */ static int init_vmlinux(void) @@ -92,12 +91,15 @@ static int init_vmlinux(void) goto out; } + ret = machine__init(&machine, "/", 0); + if (ret < 0) + goto out; + kernel = dso__new_kernel(symbol_conf.vmlinux_name); if (kernel == NULL) die("Failed to create kernel dso."); - map_groups__init(&kmap_groups); - ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); + ret = __machine__create_kernel_maps(&machine, kernel); if (ret < 0) pr_debug("Failed to create kernel maps.\n"); @@ -110,12 +112,12 @@ out: #ifdef DWARF_SUPPORT static int open_vmlinux(void) { - if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { + if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { pr_debug("Failed to load kernel map.\n"); return -EINVAL; } - pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); - return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); + pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); + return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } /* Convert trace point to probe point with debuginfo */ @@ -125,7 +127,7 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct symbol *sym; int fd, ret = -ENOENT; - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], tp->symbol, NULL); if (sym) { fd = open_vmlinux(); @@ -1466,7 +1468,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, } /* Currently just checking function name from symbol map */ - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], tev->point.symbol, NULL); if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b745c1c..a8dd73e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -69,12 +69,11 @@ void perf_session__update_sample_type(struct perf_session *self) int perf_session__create_kernel_maps(struct perf_session *self) { - int ret; - struct rb_root *root = &self->machines; + struct rb_root *machines = &self->machines; + int ret = machines__create_kernel_maps(machines, HOST_KERNEL_ID); - ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); if (ret >= 0) - ret = map_groups__create_guest_kernel_maps(root); + ret = machines__create_guest_kernel_maps(machines); return ret; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c9c0bdd..12359c3 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1528,21 +1528,20 @@ static char *get_kernel_version(const char *root_dir) return strdup(name); } -static int map_groups__set_modules_path(struct map_groups *self, - const char *root_dir) +static int machine__set_modules_path(struct machine *self) { char *version; char modules_path[PATH_MAX]; - version = get_kernel_version(root_dir); + version = get_kernel_version(self->root_dir); if (!version) return -1; snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", - root_dir, version); + self->root_dir, version); free(version); - return map_groups__set_modules_path_dir(self, modules_path); + return map_groups__set_modules_path_dir(&self->kmaps, modules_path); } /* @@ -1564,14 +1563,12 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) return self; } -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename, - struct machine *machine) +struct map *machine__new_module(struct machine *self, u64 start, + const char *filename) { struct map *map; - struct dso *dso; + struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename); - dso = __dsos__findnew(&machine->kernel_dsos, filename); if (dso == NULL) return NULL; @@ -1579,28 +1576,27 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, if (map == NULL) return NULL; - if (machine__is_host(machine)) + if (machine__is_host(self)) dso->origin = DSO__ORIG_KMODULE; else dso->origin = DSO__ORIG_GUEST_KMODULE; - map_groups__insert(self, map); + map_groups__insert(&self->kmaps, map); return map; } -static int map_groups__create_modules(struct machine *machine) +static int machine__create_modules(struct machine *self) { char *line = NULL; size_t n; FILE *file; struct map *map; - const char *root_dir; const char *modules; char path[PATH_MAX]; - if (machine__is_default_guest(machine)) + if (machine__is_default_guest(self)) modules = symbol_conf.default_guest_modules; else { - sprintf(path, "%s/proc/modules", machine->root_dir); + sprintf(path, "%s/proc/modules", self->root_dir); modules = path; } @@ -1608,8 +1604,6 @@ static int map_groups__create_modules(struct machine *machine) if (file == NULL) return -1; - root_dir = machine->root_dir; - while (!feof(file)) { char name[PATH_MAX]; u64 start; @@ -1638,17 +1632,16 @@ static int map_groups__create_modules(struct machine *machine) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = map_groups__new_module(&machine->kmaps, start, - name, machine); + map = machine__new_module(self, start, name); if (map == NULL) goto out_delete_line; - dso__kernel_module_get_build_id(map->dso, root_dir); + dso__kernel_module_get_build_id(map->dso, self->root_dir); } free(line); fclose(file); - return map_groups__set_modules_path(&machine->kmaps, root_dir); + return machine__set_modules_path(self); out_delete_line: free(line); @@ -2005,25 +1998,23 @@ static struct dso *dsos__create_kernel(struct machine *machine) return kernel; } -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel) +int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) { enum map_type type; for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; - vmlinux_maps[type] = map__new2(0, kernel, type); - if (vmlinux_maps[type] == NULL) + self->vmlinux_maps[type] = map__new2(0, kernel, type); + if (self->vmlinux_maps[type] == NULL) return -1; - vmlinux_maps[type]->map_ip = - vmlinux_maps[type]->unmap_ip = identity__map_ip; + self->vmlinux_maps[type]->map_ip = + self->vmlinux_maps[type]->unmap_ip = identity__map_ip; - kmap = map__kmap(vmlinux_maps[type]); - kmap->kmaps = self; - map_groups__insert(self, vmlinux_maps[type]); + kmap = map__kmap(self->vmlinux_maps[type]); + kmap->kmaps = &self->kmaps; + map_groups__insert(&self->kmaps, self->vmlinux_maps[type]); } return 0; @@ -2145,10 +2136,10 @@ out_free_comm_list: return -1; } -int map_groups__create_kernel_maps(struct rb_root *machines, pid_t pid) +int machines__create_kernel_maps(struct rb_root *self, pid_t pid) { struct dso *kernel; - struct machine *machine = machines__findnew(machines, pid); + struct machine *machine = machines__findnew(self, pid); if (machine == NULL) return -1; @@ -2156,12 +2147,10 @@ int map_groups__create_kernel_maps(struct rb_root *machines, pid_t pid) if (kernel == NULL) return -1; - if (__map_groups__create_kernel_maps(&machine->kmaps, - machine->vmlinux_maps, kernel) < 0) + if (__machine__create_kernel_maps(machine, kernel) < 0) return -1; - if (symbol_conf.use_modules && - map_groups__create_modules(machine) < 0) + if (symbol_conf.use_modules && machine__create_modules(machine) < 0) pr_debug("Problems creating module maps, continuing anyway...\n"); /* * Now that we have all the maps created, just set the ->end of them: @@ -2213,7 +2202,7 @@ char *strxfrchar(char *s, char from, char to) return s; } -int map_groups__create_guest_kernel_maps(struct rb_root *machines) +int machines__create_guest_kernel_maps(struct rb_root *self) { int ret = 0; struct dirent **namelist = NULL; @@ -2224,7 +2213,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *machines) if (symbol_conf.default_guest_vmlinux_name || symbol_conf.default_guest_modules || symbol_conf.default_guest_kallsyms) { - map_groups__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); + machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID); } if (symbol_conf.guestmount) { @@ -2245,7 +2234,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *machines) pr_debug("Can't access file %s\n", path); goto failure; } - map_groups__create_kernel_maps(machines, pid); + machines__create_kernel_maps(self, pid); } failure: free(namelist); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ed885b0..37b717b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -199,11 +199,9 @@ int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel); -int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid); -int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root); +int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); +int machines__create_kernel_maps(struct rb_root *self, pid_t pid); +int machines__create_guest_kernel_maps(struct rb_root *self); int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); -- cgit v0.10.2 From cbf6968098f89d3216d074f06544b5032b344da4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Apr 2010 21:22:44 -0300 Subject: perf machines: Make the machines class adopt the dsos__fprintf methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now those methods don't operate on a global list of dsos, but on lists of machines, so make this clear by renaming the functions. Cc: Avi Kivity Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Zhang, Yanmin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 986b2ef..b57dbcf 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -571,7 +571,7 @@ static int __cmd_annotate(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(&session->machines, stdout); + perf_session__fprintf_dsos(session, stdout); perf_session__collapse_resort(&session->hists); perf_session__output_resort(&session->hists, session->event_total[0]); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index b4a265a..7dc3b2e 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -46,7 +46,7 @@ static int __cmd_buildid_list(void) if (with_hits) perf_session__process_events(session, &build_id__mark_dso_hit_ops); - dsos__fprintf_buildid(&session->machines, stdout, with_hits); + perf_session__fprintf_dsos_buildid(session, stdout, with_hits); perf_session__delete(session); return err; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 49cc367..f1b46eb 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -313,7 +313,7 @@ static int __cmd_report(void) perf_session__fprintf(session, stdout); if (verbose > 2) - dsos__fprintf(&session->machines, stdout); + perf_session__fprintf_dsos(session, stdout); next = rb_first(&session->stats_by_id); while (next) { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c390f34..d95281f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -854,7 +854,7 @@ static void handle_keypress(struct perf_session *session, int c) case 'Q': printf("exiting.\n"); if (dump_symtab) - dsos__fprintf(&session->machines, stderr); + perf_session__fprintf_dsos(session, stderr); exit(0); case 's': prompt_symbol(&sym_filter_entry, "Enter details symbol"); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 7125272..61ca92e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -127,4 +127,17 @@ void perf_session__process_machines(struct perf_session *self, { return machines__process(&self->machines, process, self); } + +static inline +size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +{ + return machines__fprintf_dsos(&self->machines, fp); +} + +static inline +size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, + bool with_hits) +{ + return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); +} #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 12359c3..caa890f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1889,26 +1889,32 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name) return dso; } -static void __dsos__fprintf(struct list_head *head, FILE *fp) +static size_t __dsos__fprintf(struct list_head *head, FILE *fp) { struct dso *pos; + size_t ret = 0; list_for_each_entry(pos, head, node) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) - dso__fprintf(pos, i, fp); + ret += dso__fprintf(pos, i, fp); } + + return ret; } -void dsos__fprintf(struct rb_root *machines, FILE *fp) +size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp) { struct rb_node *nd; + size_t ret = 0; - for (nd = rb_first(machines); nd; nd = rb_next(nd)) { + for (nd = rb_first(self); nd; nd = rb_next(nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); - __dsos__fprintf(&pos->kernel_dsos, fp); - __dsos__fprintf(&pos->user_dsos, fp); + ret += __dsos__fprintf(&pos->kernel_dsos, fp); + ret += __dsos__fprintf(&pos->user_dsos, fp); } + + return ret; } static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, @@ -1926,12 +1932,12 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t dsos__fprintf_buildid(struct rb_root *machines, FILE *fp, bool with_hits) +size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits) { struct rb_node *nd; size_t ret = 0; - for (nd = rb_first(machines); nd; nd = rb_next(nd)) { + for (nd = rb_first(self); nd; nd = rb_next(nd)) { struct machine *pos = rb_entry(nd, struct machine, rb_node); ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits); ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 37b717b..2cec6a1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -162,9 +162,8 @@ int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); -void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp); -size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, - FILE *fp, bool with_hits); +size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp); +size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); -- cgit v0.10.2 From 18acde52b83bd1c8e1d007db519f46d344aa13ed Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Apr 2010 22:26:51 -0300 Subject: perf tools: Create $(OUTPUT)arch/$(ARCH)/util/ directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that "make -C tools/perf O=/tmp/some/path" works again. Problem introduced in: cd932c5 "perf: Move arch specific code into separate arch director" Cc: Ian Munsie Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3ac6b67..b86f2ad 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -187,6 +187,8 @@ ifeq ($(ARCH),x86_64) ARCH := x86 endif +$(shell sh -c 'mkdir -p $(OUTPUT)arch/$(ARCH)/util/' 2> /dev/null) + # CFLAGS and LDFLAGS are for the users to override from the command line. # -- cgit v0.10.2 From 5c0541d53ef3897494768decb09eb8f1087953a5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Apr 2010 15:25:23 -0300 Subject: perf symbols: Add machine helper routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created when writing the first 'perf test' regression testing routine. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 1563561..ee05dba 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -377,7 +377,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, if (is_caller) { addr = data->call_site; if (!raw_ip) - sym = machine__find_function(machine, addr, &map, NULL); + sym = machine__find_kernel_function(machine, addr, &map, NULL); } else addr = data->ptr; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 881dba4..f391345 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -30,6 +30,7 @@ struct map { u64 start; u64 end; enum map_type type; + u32 priv; u64 pgoff; /* ip -> dso rip */ @@ -66,6 +67,12 @@ struct machine { struct map *vmlinux_maps[MAP__NR_TYPES]; }; +static inline +struct map *machine__kernel_map(struct machine *self, enum map_type type) +{ + return self->vmlinux_maps[type]; +} + static inline struct kmap *map__kmap(struct map *self) { return (struct kmap *)(self + 1); @@ -173,11 +180,21 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, struct map **mapp, symbol_filter_t filter); -static inline struct symbol *machine__find_function(struct machine *self, - u64 addr, struct map **mapp, - symbol_filter_t filter) +static inline +struct symbol *machine__find_kernel_symbol(struct machine *self, + enum map_type type, u64 addr, + struct map **mapp, + symbol_filter_t filter) +{ + return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter); +} + +static inline +struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, + struct map **mapp, + symbol_filter_t filter) { - return map_groups__find_symbol(&self->kmaps, MAP__FUNCTION, addr, mapp, filter); + return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter); } static inline diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index caa890f..4c0146a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1983,23 +1983,23 @@ void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine self->has_build_id = true; } -static struct dso *dsos__create_kernel(struct machine *machine) +static struct dso *machine__create_kernel(struct machine *self) { const char *vmlinux_name = NULL; struct dso *kernel; - if (machine__is_host(machine)) { + if (machine__is_host(self)) { vmlinux_name = symbol_conf.vmlinux_name; kernel = dso__new_kernel(vmlinux_name); } else { - if (machine__is_default_guest(machine)) + if (machine__is_default_guest(self)) vmlinux_name = symbol_conf.default_guest_vmlinux_name; - kernel = dso__new_guest_kernel(machine, vmlinux_name); + kernel = dso__new_guest_kernel(self, vmlinux_name); } if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel, machine); - dsos__add(&machine->kernel_dsos, kernel); + dso__read_running_kernel_build_id(kernel, self); + dsos__add(&self->kernel_dsos, kernel); } return kernel; } @@ -2026,6 +2026,23 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) return 0; } +int machine__create_kernel_maps(struct machine *self) +{ + struct dso *kernel = machine__create_kernel(self); + + if (kernel == NULL || + __machine__create_kernel_maps(self, kernel) < 0) + return -1; + + if (symbol_conf.use_modules && machine__create_modules(self) < 0) + pr_debug("Problems creating module maps, continuing anyway...\n"); + /* + * Now that we have all the maps created, just set the ->end of them: + */ + map_groups__fixup_end(&self->kmaps); + return 0; +} + static void vmlinux_path__exit(void) { while (--vmlinux_path__nr_entries >= 0) { @@ -2144,25 +2161,12 @@ out_free_comm_list: int machines__create_kernel_maps(struct rb_root *self, pid_t pid) { - struct dso *kernel; struct machine *machine = machines__findnew(self, pid); if (machine == NULL) return -1; - kernel = dsos__create_kernel(machine); - if (kernel == NULL) - return -1; - - if (__machine__create_kernel_maps(machine, kernel) < 0) - return -1; - if (symbol_conf.use_modules && machine__create_modules(machine) < 0) - pr_debug("Problems creating module maps, continuing anyway...\n"); - /* - * Now that we have all the maps created, just set the ->end of them: - */ - map_groups__fixup_end(&machine->kmaps); - return 0; + return machine__create_kernel_maps(machine); } static int hex(char ch) @@ -2248,3 +2252,36 @@ failure: return ret; } + +int machine__load_kallsyms(struct machine *self, const char *filename, + enum map_type type, symbol_filter_t filter) +{ + struct map *map = self->vmlinux_maps[type]; + int ret = dso__load_kallsyms(map->dso, filename, map, filter); + + if (ret > 0) { + dso__set_loaded(map->dso, type); + /* + * Since /proc/kallsyms will have multiple sessions for the + * kernel, with modules between them, fixup the end of all + * sections. + */ + __map_groups__fixup_end(&self->kmaps, type); + } + + return ret; +} + +int machine__load_vmlinux_path(struct machine *self, enum map_type type, + symbol_filter_t filter) +{ + struct map *map = self->vmlinux_maps[type]; + int ret = dso__load_vmlinux_path(map->dso, map, filter); + + if (ret > 0) { + dso__set_loaded(map->dso, type); + map__reloc_vmlinux(map); + } + + return ret; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 2cec6a1..a517c17 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -162,6 +162,11 @@ int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); +int machine__load_kallsyms(struct machine *self, const char *filename, + enum map_type type, symbol_filter_t filter); +int machine__load_vmlinux_path(struct machine *self, enum map_type type, + symbol_filter_t filter); + size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp); size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); @@ -199,6 +204,8 @@ int kallsyms__parse(const char *filename, void *arg, char type, u64 start)); int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); +int machine__create_kernel_maps(struct machine *self); + int machines__create_kernel_maps(struct rb_root *self, pid_t pid); int machines__create_guest_kernel_maps(struct rb_root *self); -- cgit v0.10.2 From 1c6a800cde3b818fd8320b5d402f2d77d2948c00 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Apr 2010 18:58:32 -0300 Subject: perf test: Initial regression testing command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First an example with the first internal test: [acme@doppio linux-2.6-tip]$ perf test 1: vmlinux symtab matches kallsyms: Ok So it run just one test, that is "vmlinux symtab matches kallsyms", and it was successful. If we run it in verbose mode, we'll see details about errors and extra warnings for non-fatal problems: [acme@doppio linux-2.6-tip]$ perf test -v 1: vmlinux symtab matches kallsyms: --- start --- Looking at the vmlinux_path (5 entries long) No build_id in vmlinux, ignoring it No build_id in /boot/vmlinux, ignoring it No build_id in /boot/vmlinux-2.6.34-rc4-tip+, ignoring it Using /lib/modules/2.6.34-rc4-tip+/build/vmlinux for symbols Maps only in vmlinux: ffffffff81cb81b1-ffffffff81e1149b 0 [kernel].init.text ffffffff81e1149c-ffffffff9fffffff 0 [kernel].exit.text ffffffffff600000-ffffffffff6000ff 0 [kernel].vsyscall_0 ffffffffff600100-ffffffffff6003ff 0 [kernel].vsyscall_fn ffffffffff600400-ffffffffff6007ff 0 [kernel].vsyscall_1 ffffffffff600800-ffffffffffffffff 0 [kernel].vsyscall_2 Maps in vmlinux with a different name in kallsyms: ffffffffff600000-ffffffffff6000ff 0 [kernel].vsyscall_0 in kallsyms as [kernel].0 ffffffffff600100-ffffffffff6003ff 0 [kernel].vsyscall_fn in kallsyms as: *ffffffffff600100-ffffffffff60012f 0 [kernel].2 ffffffffff600400-ffffffffff6007ff 0 [kernel].vsyscall_1 in kallsyms as [kernel].6 ffffffffff600800-ffffffffffffffff 0 [kernel].vsyscall_2 in kallsyms as [kernel].8 Maps only in kallsyms: ffffffffff600130-ffffffffff6003ff 0 [kernel].4 ---- end ---- vmlinux symtab matches kallsyms: Ok [acme@doppio linux-2.6-tip]$ In the above case we only know the name of the non contiguous kernel ranges in the address space when reading the symbol information from the ELF symtab in vmlinux. The /proc/kallsyms file lack this, we only notice they are separate because there are modules after the kernel and after that more kernel functions, so we need to have a module rbtree backed by the module .ko path to get symtabs in the vmlinux case. The tool uses it to match by address to emit appropriate warning, but don't considers this fatal. The .init.text and .exit.text ines, of course, aren't in kallsyms, so I left these cases just as extra info in verbose mode. The end of the sections also aren't in kallsyms, so we the symbols layer does another pass and sets the end addresses as the next map start minus one, which sometimes pads, causing harmless mismatches. But at least the symbols match, tested it by copying /proc/kallsyms to /tmp/kallsyms and doing changes to see if they were detected. This first test also should serve as a first stab at documenting the symbol library by providing a self contained example that exercises it together with comments about what is being done. More tests to check if actions done on a monitored app, like doing mmaps, etc, makes the kernel generate the expected events should be added next. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt new file mode 100644 index 0000000..1c4b5f5 --- /dev/null +++ b/tools/perf/Documentation/perf-test.txt @@ -0,0 +1,22 @@ +perf-test(1) +============ + +NAME +---- +perf-test - Runs sanity tests. + +SYNOPSIS +-------- +[verse] +'perf test ' + +DESCRIPTION +----------- +This command does assorted sanity tests, initially thru linked routines but +also will look for a directory with more tests in the form of scripts. + +OPTIONS +------- +-v:: +--verbose:: + Be more verbose. diff --git a/tools/perf/Makefile b/tools/perf/Makefile index b86f2ad..739c441 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -490,6 +490,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-probe.o BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o BUILTIN_OBJS += $(OUTPUT)builtin-lock.o BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o +BUILTIN_OBJS += $(OUTPUT)builtin-test.o PERFLIBS = $(LIB_FILE) diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c new file mode 100644 index 0000000..0339612 --- /dev/null +++ b/tools/perf/builtin-test.c @@ -0,0 +1,281 @@ +/* + * builtin-test.c + * + * Builtin regression testing command: ever growing number of sanity tests + */ +#include "builtin.h" + +#include "util/cache.h" +#include "util/debug.h" +#include "util/parse-options.h" +#include "util/session.h" +#include "util/symbol.h" +#include "util/thread.h" + +static long page_size; + +static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) +{ + bool *visited = symbol__priv(sym); + *visited = true; + return 0; +} + +static int test__vmlinux_matches_kallsyms(void) +{ + int err = -1; + struct rb_node *nd; + struct symbol *sym; + struct map *kallsyms_map, *vmlinux_map; + struct machine kallsyms, vmlinux; + enum map_type type = MAP__FUNCTION; + struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; + + /* + * Step 1: + * + * Init the machines that will hold kernel, modules obtained from + * both vmlinux + .ko files and from /proc/kallsyms split by modules. + */ + machine__init(&kallsyms, "", HOST_KERNEL_ID); + machine__init(&vmlinux, "", HOST_KERNEL_ID); + + /* + * Step 2: + * + * Create the kernel maps for kallsyms and the DSO where we will then + * load /proc/kallsyms. Also create the modules maps from /proc/modules + * and find the .ko files that match them in /lib/modules/`uname -r`/. + */ + if (machine__create_kernel_maps(&kallsyms) < 0) { + pr_debug("machine__create_kernel_maps "); + return -1; + } + + /* + * Step 3: + * + * Load and split /proc/kallsyms into multiple maps, one per module. + */ + if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { + pr_debug("dso__load_kallsyms "); + goto out; + } + + /* + * Step 4: + * + * kallsyms will be internally on demand sorted by name so that we can + * find the reference relocation * symbol, i.e. the symbol we will use + * to see if the running kernel was relocated by checking if it has the + * same value in the vmlinux file we load. + */ + kallsyms_map = machine__kernel_map(&kallsyms, type); + + sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); + if (sym == NULL) { + pr_debug("dso__find_symbol_by_name "); + goto out; + } + + ref_reloc_sym.addr = sym->start; + + /* + * Step 5: + * + * Now repeat step 2, this time for the vmlinux file we'll auto-locate. + */ + if (machine__create_kernel_maps(&vmlinux) < 0) { + pr_debug("machine__create_kernel_maps "); + goto out; + } + + vmlinux_map = machine__kernel_map(&vmlinux, type); + map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; + + /* + * Step 6: + * + * Locate a vmlinux file in the vmlinux path that has a buildid that + * matches the one of the running kernel. + * + * While doing that look if we find the ref reloc symbol, if we find it + * we'll have its ref_reloc_symbol.unrelocated_addr and then + * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines + * to fixup the symbols. + */ + if (machine__load_vmlinux_path(&vmlinux, type, + vmlinux_matches_kallsyms_filter) <= 0) { + pr_debug("machine__load_vmlinux_path "); + goto out; + } + + err = 0; + /* + * Step 7: + * + * Now look at the symbols in the vmlinux DSO and check if we find all of them + * in the kallsyms dso. For the ones that are in both, check its names and + * end addresses too. + */ + for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { + struct symbol *pair; + + sym = rb_entry(nd, struct symbol, rb_node); + pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); + + if (pair && pair->start == sym->start) { +next_pair: + if (strcmp(sym->name, pair->name) == 0) { + /* + * kallsyms don't have the symbol end, so we + * set that by using the next symbol start - 1, + * in some cases we get this up to a page + * wrong, trace_kmalloc when I was developing + * this code was one such example, 2106 bytes + * off the real size. More than that and we + * _really_ have a problem. + */ + s64 skew = sym->end - pair->end; + if (llabs(skew) < page_size) + continue; + + pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n", + sym->start, sym->name, sym->end, pair->end); + } else { + struct rb_node *nnd = rb_prev(&pair->rb_node); + + if (nnd) { + struct symbol *next = rb_entry(nnd, struct symbol, rb_node); + + if (next->start == sym->start) { + pair = next; + goto next_pair; + } + } + pr_debug("%#Lx: diff name v: %s k: %s\n", + sym->start, sym->name, pair->name); + } + } else + pr_debug("%#Lx: %s not on kallsyms\n", sym->start, sym->name); + + err = -1; + } + + if (!verbose) + goto out; + + pr_info("Maps only in vmlinux:\n"); + + for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node), *pair; + /* + * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while + * the kernel will have the path for the vmlinux file being used, + * so use the short name, less descriptive but the same ("[kernel]" in + * both cases. + */ + pair = map_groups__find_by_name(&kallsyms.kmaps, type, + (pos->dso->kernel ? + pos->dso->short_name : + pos->dso->name)); + if (pair) + pair->priv = 1; + else + map__fprintf(pos, stderr); + } + + pr_info("Maps in vmlinux with a different name in kallsyms:\n"); + + for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node), *pair; + + pair = map_groups__find(&kallsyms.kmaps, type, pos->start); + if (pair == NULL || pair->priv) + continue; + + if (pair->start == pos->start) { + pair->priv = 1; + pr_info(" %Lx-%Lx %Lx %s in kallsyms as", + pos->start, pos->end, pos->pgoff, pos->dso->name); + if (pos->pgoff != pair->pgoff || pos->end != pair->end) + pr_info(": \n*%Lx-%Lx %Lx", + pair->start, pair->end, pair->pgoff); + pr_info(" %s\n", pair->dso->name); + pair->priv = 1; + } + } + + pr_info("Maps only in kallsyms:\n"); + + for (nd = rb_first(&kallsyms.kmaps.maps[type]); + nd; nd = rb_next(nd)) { + struct map *pos = rb_entry(nd, struct map, rb_node); + + if (!pos->priv) + map__fprintf(pos, stderr); + } +out: + return err; +} + +static struct test { + const char *desc; + int (*func)(void); +} tests[] = { + { + .desc = "vmlinux symtab matches kallsyms", + .func = test__vmlinux_matches_kallsyms, + }, + { + .func = NULL, + }, +}; + +static int __cmd_test(void) +{ + int i = 0; + + page_size = sysconf(_SC_PAGE_SIZE); + + while (tests[i].func) { + int err; + pr_info("%2d: %s:", i + 1, tests[i].desc); + pr_debug("\n--- start ---\n"); + err = tests[i].func(); + pr_debug("---- end ----\n%s:", tests[i].desc); + pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); + ++i; + } + + return 0; +} + +static const char * const test_usage[] = { + "perf test []", + NULL, +}; + +static const struct option test_options[] = { + OPT_BOOLEAN('v', "verbose", &verbose, + "be more verbose (show symbol address, etc)"), + OPT_END() +}; + +int cmd_test(int argc, const char **argv, const char *prefix __used) +{ + argc = parse_options(argc, argv, test_options, test_usage, 0); + if (argc) + usage_with_options(test_usage, test_options); + + symbol_conf.priv_size = sizeof(int); + symbol_conf.sort_by_name = true; + symbol_conf.try_vmlinux_path = true; + + if (symbol__init() < 0) + return -1; + + setup_pager(); + + return __cmd_test(); +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index ab28bca..34a8a9a 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -33,5 +33,6 @@ extern int cmd_probe(int argc, const char **argv, const char *prefix); extern int cmd_kmem(int argc, const char **argv, const char *prefix); extern int cmd_lock(int argc, const char **argv, const char *prefix); extern int cmd_kvm(int argc, const char **argv, const char *prefix); +extern int cmd_test(int argc, const char **argv, const char *prefix); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 2a1162d..80a1a44 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -20,3 +20,4 @@ perf-probe mainporcelain common perf-kmem mainporcelain common perf-lock mainporcelain common perf-kvm mainporcelain common +perf-test mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 985cdb4..5ff9b5b 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv) { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, { "kvm", cmd_kvm, 0 }, + { "test", cmd_test, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; -- cgit v0.10.2 From e5a5f1f015cf435eb3d2f5712ba51ffdbb92cbef Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 30 Apr 2010 19:55:00 +0200 Subject: perf: Remove leftover useless options to record trace events from scripts -f, -c 1, -R are now useless for trace events recording, moreover -M is useless and event hurts. Remove them from the documentation examples and from record scripts. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi diff --git a/tools/perf/Documentation/perf-trace-perl.txt b/tools/perf/Documentation/perf-trace-perl.txt index d729cee..ee6525e 100644 --- a/tools/perf/Documentation/perf-trace-perl.txt +++ b/tools/perf/Documentation/perf-trace-perl.txt @@ -49,12 +49,10 @@ available as calls back into the perf executable (see below). As an example, the following perf record command can be used to record all sched_wakeup events in the system: - # perf record -c 1 -f -a -M -R -e sched:sched_wakeup + # perf record -a -e sched:sched_wakeup Traces meant to be processed using a script should be recorded with -the above options: -c 1 says to sample every event, -a to enable -system-wide collection, -M to multiplex the output, and -R to collect -raw samples. +the above option: -a to enable system-wide collection. The format file for the sched_wakep event defines the following fields (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-trace-python.txt index a241aca..16a8650 100644 --- a/tools/perf/Documentation/perf-trace-python.txt +++ b/tools/perf/Documentation/perf-trace-python.txt @@ -93,7 +93,7 @@ don't care how it exited, so we'll use 'perf record' to record only the sys_enter events: ---- -# perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter +# perf record -a -e raw_syscalls:sys_enter ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ] @@ -359,7 +359,7 @@ your script: # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter +perf record -a -e raw_syscalls:sys_enter ---- The 'report' script is also a shell script with the same base name as @@ -449,12 +449,10 @@ available as calls back into the perf executable (see below). As an example, the following perf record command can be used to record all sched_wakeup events in the system: - # perf record -c 1 -f -a -M -R -e sched:sched_wakeup + # perf record -a -e sched:sched_wakeup Traces meant to be processed using a script should be recorded with -the above options: -c 1 says to sample every event, -a to enable -system-wide collection, -M to multiplex the output, and -R to collect -raw samples. +the above option: -a to enable system-wide collection. The format file for the sched_wakep event defines the following fields (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-record b/tools/perf/scripts/perl/bin/check-perf-trace-record index e6cb147..423ad6a 100644 --- a/tools/perf/scripts/perl/bin/check-perf-trace-record +++ b/tools/perf/scripts/perl/bin/check-perf-trace-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree +perf record -a -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record index 6ad9b8f..eb5846b 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-record +++ b/tools/perf/scripts/perl/bin/failed-syscalls-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@ +perf record -a -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record index a828679..5bfaae5 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-record +++ b/tools/perf/scripts/perl/bin/rw-by-file-record @@ -1,3 +1,3 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ +perf record -a -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@ diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record index 63976bf..6e0b2f7 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-record +++ b/tools/perf/scripts/perl/bin/rw-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ +perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record index 63976bf..6e0b2f7 100644 --- a/tools/perf/scripts/perl/bin/rwtop-record +++ b/tools/perf/scripts/perl/bin/rwtop-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ +perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@ diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record index 9c0cf58..9f2acaa 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-record +++ b/tools/perf/scripts/perl/bin/wakeup-latency-record @@ -1,5 +1,5 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup $@ +perf record -a -e sched:sched_switch -e sched:sched_wakeup $@ diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record index c2a1a94..85301f2 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-record +++ b/tools/perf/scripts/perl/bin/workqueue-stats-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ +perf record -a -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record index 6ad9b8f..eb5846b 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@ +perf record -a -e raw_syscalls:sys_exit $@ diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/scripts/python/bin/sctop-record index 27ccffa..1fc5998 100644 --- a/tools/perf/scripts/python/bin/sctop-record +++ b/tools/perf/scripts/python/bin/sctop-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ +perf record -a -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record index 27ccffa..1fc5998 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ +perf record -a -e raw_syscalls:sys_enter $@ diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/perf/scripts/python/bin/syscall-counts-record index 27ccffa..1fc5998 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-record +++ b/tools/perf/scripts/python/bin/syscall-counts-record @@ -1,2 +1,2 @@ #!/bin/bash -perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@ +perf record -a -e raw_syscalls:sys_enter $@ -- cgit v0.10.2 From d00a47cce569a3e660a8c9de5d57af28d6a9f0f7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 1 May 2010 03:08:46 +0200 Subject: perf: Fix warning while reading ring buffer headers commit e9e94e3bd862d31777335722e747e97d9821bc1d "perf trace: Ignore "overwrite" field if present in /events/header_page" makes perf trace launching spurious warnings about unexpected tokens read: Warning: Error: expected type 6 but read 4 This change tries to handle the overcommit field in the header_page file whenever this field is present or not. The problem is that if this field is not present, we try to find it and give up in the middle of the line when we realize we are actually dealing with another field, which is the "data" one. And this failure abandons the file pointer in the middle of the "data" description line: field: u64 timestamp; offset:0; size:8; signed:0; field: local_t commit; offset:8; size:8; signed:1; field: char data; offset:16; size:4080; signed:1; ^^^ Here What happens next is that we want to read this line to parse the data field, but we fail because the pointer is not in the beginning of the line. We could probably fix that by rewinding the pointer. But in fact we don't care much about these headers that only concern the ftrace ring-buffer. We don't use them from perf. Just skip this part of perf.data, but don't remove it from recording to stay compatible with olders perf.data Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian Cc: Steven Rostedt diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index d6ef414..069f261 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -691,11 +691,6 @@ static int __read_expected(enum event_type expect, const char *str, return ret; } -static int read_expected_warn(enum event_type expect, const char *str, bool warn) -{ - return __read_expected(expect, str, 1, warn); -} - static int read_expected(enum event_type expect, const char *str) { return __read_expected(expect, str, 1, true); @@ -3104,90 +3099,6 @@ static void print_args(struct print_arg *args) } } -static void parse_header_field(const char *field, - int *offset, int *size, bool warn) -{ - char *token; - int type; - - if (read_expected(EVENT_ITEM, "field") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - - /* type */ - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - free_token(token); - - if (read_expected_warn(EVENT_ITEM, field, warn) < 0) - return; - if (read_expected(EVENT_OP, ";") < 0) - return; - if (read_expected(EVENT_ITEM, "offset") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - *offset = atoi(token); - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - if (read_expected(EVENT_ITEM, "size") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - *size = atoi(token); - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - type = read_token(&token); - if (type != EVENT_NEWLINE) { - /* newer versions of the kernel have a "signed" type */ - if (type != EVENT_ITEM) - goto fail; - - if (strcmp(token, "signed") != 0) - goto fail; - - free_token(token); - - if (read_expected(EVENT_OP, ":") < 0) - return; - - if (read_expect_type(EVENT_ITEM, &token)) - goto fail; - - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - - if (read_expect_type(EVENT_NEWLINE, &token)) - goto fail; - } - fail: - free_token(token); -} - -int parse_header_page(char *buf, unsigned long size) -{ - init_input_buf(buf, size); - - parse_header_field("timestamp", &header_page_ts_offset, - &header_page_ts_size, true); - parse_header_field("commit", &header_page_size_offset, - &header_page_size_size, true); - parse_header_field("overwrite", &header_page_overwrite_offset, - &header_page_overwrite_size, false); - parse_header_field("data", &header_page_data_offset, - &header_page_data_size, true); - - return 0; -} - int parse_ftrace_file(char *buf, unsigned long size) { struct format_field *field; diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 44889c9..4606639 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -52,6 +52,12 @@ static unsigned long page_size; static ssize_t calc_data_size; +/* If it fails, the next read will report it */ +static void skip(int size) +{ + lseek(input_fd, size, SEEK_CUR); +} + static int do_read(int fd, void *buf, int size) { int rsize = size; @@ -169,7 +175,6 @@ static void read_ftrace_printk(void) static void read_header_files(void) { unsigned long long size; - char *header_page; char *header_event; char buf[BUFSIZ]; @@ -179,10 +184,7 @@ static void read_header_files(void) die("did not read header page"); size = read8(); - header_page = malloc_or_die(size); - read_or_die(header_page, size); - parse_header_page(header_page, size); - free(header_page); + skip(size); /* * The size field in the page is of type long, diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 1f45d46..4e1acc3 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -244,7 +244,6 @@ extern int header_page_data_size; extern bool latency_format; -int parse_header_page(char *buf, unsigned long size); int trace_parse_common_type(void *data); int trace_parse_common_pid(void *data); int parse_common_pc(void *data); -- cgit v0.10.2 From 73266fc1df2f94cf72b3beba3eee3b88ed0b0664 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 22 Apr 2010 05:05:45 +0200 Subject: hw-breakpoints: Tag ptrace breakpoint as exclude_kernel Tag ptrace breakpoints with the exclude_kernel attribute set. This will make it easier to set generic policies on breakpoints, when it comes to ensure nobody unpriviliged try to breakpoint on the kernel. Signed-off-by: Frederic Weisbecker Acked-by: Paul Mundt Cc: Will Deacon Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 7759a9a..d4104ce 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -85,7 +85,7 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr) bp = thread->ptrace_bps[0]; if (!bp) { - hw_breakpoint_init(&attr); + ptrace_breakpoint_init(&attr); attr.bp_addr = addr; attr.bp_len = HW_BREAKPOINT_LEN_2; diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 055be0a..70c4872 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -688,7 +688,7 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, struct perf_event_attr attr; if (!t->ptrace_bps[nr]) { - hw_breakpoint_init(&attr); + ptrace_breakpoint_init(&attr); /* * Put stub len and type to register (reserve) an inactive but * correct bp diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index c70d27a..a0aa5a9 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h @@ -34,6 +34,12 @@ static inline void hw_breakpoint_init(struct perf_event_attr *attr) attr->sample_period = 1; } +static inline void ptrace_breakpoint_init(struct perf_event_attr *attr) +{ + hw_breakpoint_init(attr); + attr->exclude_kernel = 1; +} + static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) { return bp->attr.bp_addr; -- cgit v0.10.2 From 87e9b2024659c614a876ce359a57e98a47b5ef37 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 17 Apr 2010 18:11:59 +0200 Subject: hw-breakpoints: Check disabled breakpoints again We stopped checking disabled breakpoints because we weren't allowing breakpoints on NULL addresses. And gdb tends to set NULL addresses on inactive breakpoints. But refusing NULL addresses was actually a regression that has been fixed now. There is no reason anymore to not validate inactive breakpoint settings. Signed-off-by: Frederic Weisbecker Acked-by: Paul Mundt Cc: Will Deacon Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Jason Wessel Cc: Ingo Molnar diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 03808ed..9ed9ae3a 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -316,17 +316,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp) if (ret) return ret; - /* - * Ptrace breakpoints can be temporary perf events only - * meant to reserve a slot. In this case, it is created disabled and - * we don't want to check the params right now (as we put a null addr) - * But perf tools create events as disabled and we want to check - * the params for them. - * This is a quick hack that will be removed soon, once we remove - * the tmp breakpoints from ptrace - */ - if (!bp->attr.disabled || !bp->overflow_handler) - ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); + ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); /* if arch_validate_hwbkpt_settings() fails then release bp slot */ if (ret) -- cgit v0.10.2 From b2812d031dea86926e9c10f7714af33ac2f6b43d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 18 Apr 2010 18:11:53 +0200 Subject: hw-breakpoints: Change/Enforce some breakpoints policies The current policies of breakpoints in x86 and SH are the following: - task bound breakpoints can only break on userspace addresses - cpu wide breakpoints can only break on kernel addresses The former rule prevents ptrace breakpoints to be set to trigger on kernel addresses, which is good. But as a side effect, we can't breakpoint on kernel addresses for task bound breakpoints. The latter rule simply makes no sense, there is no reason why we can't set breakpoints on userspace while performing cpu bound profiles. We want the following new policies: - task bound breakpoint can set userspace address breakpoints, with no particular privilege required. - task bound breakpoints can set kernelspace address breakpoints but must be privileged to do that. - cpu bound breakpoints can do what they want as they are privileged already. To implement these new policies, this patch checks if we are dealing with a kernel address breakpoint, if so and if the exclude_kernel parameter is set, we tell the user that the breakpoint is invalid, which makes a good generic ptrace protection. If we don't have exclude_kernel, ensure the user has the right privileges as kernel breakpoints are quite sensitive (risk of trap recursion attacks and global performance impacts). [ Paul Mundt: keep addr space check for sh signal delivery and fix double function declaration] Signed-off-by: Frederic Weisbecker Cc: Will Deacon Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Jason Wessel Cc: Ingo Molnar Signed-off-by: Paul Mundt diff --git a/arch/sh/include/asm/hw_breakpoint.h b/arch/sh/include/asm/hw_breakpoint.h index 965dd78..382bad9 100644 --- a/arch/sh/include/asm/hw_breakpoint.h +++ b/arch/sh/include/asm/hw_breakpoint.h @@ -47,9 +47,8 @@ struct pmu; #define HBP_NUM 2 /* arch/sh/kernel/hw_breakpoint.c */ -extern int arch_check_va_in_userspace(unsigned long va, u16 hbp_len); -extern int arch_validate_hwbkpt_settings(struct perf_event *bp, - struct task_struct *tsk); +extern int arch_check_bp_in_kernelspace(struct perf_event *bp); +extern int arch_validate_hwbkpt_settings(struct perf_event *bp); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data); diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index 675eea7..1f2cf62 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -120,25 +120,16 @@ static int get_hbp_len(u16 hbp_len) } /* - * Check for virtual address in user space. - */ -int arch_check_va_in_userspace(unsigned long va, u16 hbp_len) -{ - unsigned int len; - - len = get_hbp_len(hbp_len); - - return (va <= TASK_SIZE - len); -} - -/* * Check for virtual address in kernel space. */ -static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) +int arch_check_bp_in_kernelspace(struct perf_event *bp) { unsigned int len; + unsigned long va; + struct arch_hw_breakpoint *info = counter_arch_bp(bp); - len = get_hbp_len(hbp_len); + va = info->address; + len = get_hbp_len(info->len); return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); } @@ -226,8 +217,7 @@ static int arch_build_bp_info(struct perf_event *bp) /* * Validate the arch-specific HW Breakpoint register settings */ -int arch_validate_hwbkpt_settings(struct perf_event *bp, - struct task_struct *tsk) +int arch_validate_hwbkpt_settings(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); unsigned int align; @@ -270,15 +260,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp, if (info->address & align) return -EINVAL; - /* Check that the virtual address is in the proper range */ - if (tsk) { - if (!arch_check_va_in_userspace(info->address, info->len)) - return -EFAULT; - } else { - if (!arch_check_va_in_kernelspace(info->address, info->len)) - return -EFAULT; - } - return 0; } @@ -363,8 +344,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) perf_bp_event(bp, args->regs); /* Deliver the signal to userspace */ - if (arch_check_va_in_userspace(bp->attr.bp_addr, - bp->attr.bp_len)) { + if (!arch_check_bp_in_kernelspace(bp)) { siginfo_t info; info.si_signo = args->signr; diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index 2a1bd8f..c77a5a6 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -44,9 +44,8 @@ struct arch_hw_breakpoint { struct perf_event; struct pmu; -extern int arch_check_va_in_userspace(unsigned long va, u8 hbp_len); -extern int arch_validate_hwbkpt_settings(struct perf_event *bp, - struct task_struct *tsk); +extern int arch_check_bp_in_kernelspace(struct perf_event *bp); +extern int arch_validate_hwbkpt_settings(struct perf_event *bp); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data); diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index d6cc065..a8f1b80 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -189,25 +189,16 @@ static int get_hbp_len(u8 hbp_len) } /* - * Check for virtual address in user space. - */ -int arch_check_va_in_userspace(unsigned long va, u8 hbp_len) -{ - unsigned int len; - - len = get_hbp_len(hbp_len); - - return (va <= TASK_SIZE - len); -} - -/* * Check for virtual address in kernel space. */ -static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) +int arch_check_bp_in_kernelspace(struct perf_event *bp) { unsigned int len; + unsigned long va; + struct arch_hw_breakpoint *info = counter_arch_bp(bp); - len = get_hbp_len(hbp_len); + va = info->address; + len = get_hbp_len(info->len); return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); } @@ -300,8 +291,7 @@ static int arch_build_bp_info(struct perf_event *bp) /* * Validate the arch-specific HW Breakpoint register settings */ -int arch_validate_hwbkpt_settings(struct perf_event *bp, - struct task_struct *tsk) +int arch_validate_hwbkpt_settings(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); unsigned int align; @@ -314,16 +304,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp, ret = -EINVAL; - if (info->type == X86_BREAKPOINT_EXECUTE) - /* - * Ptrace-refactoring code - * For now, we'll allow instruction breakpoint only for user-space - * addresses - */ - if ((!arch_check_va_in_userspace(info->address, info->len)) && - info->len != X86_BREAKPOINT_EXECUTE) - return ret; - switch (info->len) { case X86_BREAKPOINT_LEN_1: align = 0; @@ -350,15 +330,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp, if (info->address & align) return -EINVAL; - /* Check that the virtual address is in the proper range */ - if (tsk) { - if (!arch_check_va_in_userspace(info->address, info->len)) - return -EFAULT; - } else { - if (!arch_check_va_in_kernelspace(info->address, info->len)) - return -EFAULT; - } - return 0; } diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 9ed9ae3a..89e8a05 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -308,6 +308,28 @@ int dbg_release_bp_slot(struct perf_event *bp) return 0; } +static int validate_hw_breakpoint(struct perf_event *bp) +{ + int ret; + + ret = arch_validate_hwbkpt_settings(bp); + if (ret) + return ret; + + if (arch_check_bp_in_kernelspace(bp)) { + if (bp->attr.exclude_kernel) + return -EINVAL; + /* + * Don't let unprivileged users set a breakpoint in the trap + * path to avoid trap recursion attacks. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + } + + return 0; +} + int register_perf_hw_breakpoint(struct perf_event *bp) { int ret; @@ -316,7 +338,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp) if (ret) return ret; - ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); + ret = validate_hw_breakpoint(bp); /* if arch_validate_hwbkpt_settings() fails then release bp slot */ if (ret) @@ -363,7 +385,7 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att if (attr->disabled) goto end; - err = arch_validate_hwbkpt_settings(bp, bp->ctx->task); + err = validate_hw_breakpoint(bp); if (!err) perf_event_enable(bp); -- cgit v0.10.2 From 0102752e4c9e0655b39734550d4c35327954f7f9 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 11 Apr 2010 18:55:56 +0200 Subject: hw-breakpoints: Separate constraint space for data and instruction breakpoints There are two outstanding fashions for archs to implement hardware breakpoints. The first is to separate breakpoint address pattern definition space between data and instruction breakpoints. We then have typically distinct instruction address breakpoint registers and data address breakpoint registers, delivered with separate control registers for data and instruction breakpoints as well. This is the case of PowerPc and ARM for example. The second consists in having merged breakpoint address space definition between data and instruction breakpoint. Address registers can host either instruction or data address and the access mode for the breakpoint is defined in a control register. This is the case of x86 and Super H. This patch adds a new CONFIG_HAVE_MIXED_BREAKPOINTS_REGS config that archs can select if they belong to the second case. Those will have their slot allocation merged for instructions and data breakpoints. The others will have a separate slot tracking between data and instruction breakpoints. Signed-off-by: Frederic Weisbecker Acked-by: Paul Mundt Cc: Will Deacon Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/arch/Kconfig b/arch/Kconfig index f06010f..acda512 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -137,6 +137,17 @@ config HAVE_HW_BREAKPOINT bool depends on PERF_EVENTS +config HAVE_MIXED_BREAKPOINTS_REGS + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them have separate registers for data and instruction + breakpoints addresses, others have mixed registers to store + them but define the access type in a control register. + Select this option if your arch implements breakpoints under the + latter fashion. + config HAVE_USER_RETURN_NOTIFIER bool diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 8d90564..e6d8ab5 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -44,6 +44,7 @@ config SUPERH32 select HAVE_FUNCTION_GRAPH_TRACER select HAVE_ARCH_KGDB select HAVE_HW_BREAKPOINT + select HAVE_MIXED_BREAKPOINTS_REGS select PERF_EVENTS if HAVE_HW_BREAKPOINT select ARCH_HIBERNATION_POSSIBLE if MMU diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 97a95df..01177dc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -53,6 +53,7 @@ config X86 select HAVE_KERNEL_LZMA select HAVE_KERNEL_LZO select HAVE_HW_BREAKPOINT + select HAVE_MIXED_BREAKPOINTS_REGS select PERF_EVENTS select ANON_INODES select HAVE_ARCH_KMEMCHECK diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index a0aa5a9..7e88990 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h @@ -9,9 +9,12 @@ enum { }; enum { - HW_BREAKPOINT_R = 1, - HW_BREAKPOINT_W = 2, - HW_BREAKPOINT_X = 4, + HW_BREAKPOINT_EMPTY = 0, + HW_BREAKPOINT_R = 1, + HW_BREAKPOINT_W = 2, + HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W, + HW_BREAKPOINT_X = 4, + HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X, }; #ifdef __KERNEL__ diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 89e8a05..8ead134 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -45,18 +45,28 @@ #include +enum bp_type_idx { + TYPE_INST = 0, +#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS + TYPE_DATA = 0, +#else + TYPE_DATA = 1, +#endif + TYPE_MAX +}; + /* * Constraints data */ /* Number of pinned cpu breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned); +static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); /* Number of pinned task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[HBP_NUM]); +static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[TYPE_MAX][HBP_NUM]); /* Number of non-pinned cpu/task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_bp_flexible); +static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); /* Gather the number of total pinned and un-pinned bp in a cpuset */ struct bp_busy_slots { @@ -67,14 +77,22 @@ struct bp_busy_slots { /* Serialize accesses to the above constraints */ static DEFINE_MUTEX(nr_bp_mutex); +static inline enum bp_type_idx find_slot_idx(struct perf_event *bp) +{ + if (bp->attr.bp_type & HW_BREAKPOINT_RW) + return TYPE_DATA; + + return TYPE_INST; +} + /* * Report the maximum number of pinned breakpoints a task * have in this cpu */ -static unsigned int max_task_bp_pinned(int cpu) +static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) { int i; - unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); + unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); for (i = HBP_NUM -1; i >= 0; i--) { if (tsk_pinned[i] > 0) @@ -84,7 +102,7 @@ static unsigned int max_task_bp_pinned(int cpu) return 0; } -static int task_bp_pinned(struct task_struct *tsk) +static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) { struct perf_event_context *ctx = tsk->perf_event_ctxp; struct list_head *list; @@ -105,7 +123,8 @@ static int task_bp_pinned(struct task_struct *tsk) */ list_for_each_entry(bp, list, event_entry) { if (bp->attr.type == PERF_TYPE_BREAKPOINT) - count++; + if (find_slot_idx(bp) == type) + count++; } raw_spin_unlock_irqrestore(&ctx->lock, flags); @@ -118,18 +137,19 @@ static int task_bp_pinned(struct task_struct *tsk) * a given cpu (cpu > -1) or in all of them (cpu = -1). */ static void -fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) +fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, + enum bp_type_idx type) { int cpu = bp->cpu; struct task_struct *tsk = bp->ctx->task; if (cpu >= 0) { - slots->pinned = per_cpu(nr_cpu_bp_pinned, cpu); + slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu); if (!tsk) - slots->pinned += max_task_bp_pinned(cpu); + slots->pinned += max_task_bp_pinned(cpu, type); else - slots->pinned += task_bp_pinned(tsk); - slots->flexible = per_cpu(nr_bp_flexible, cpu); + slots->pinned += task_bp_pinned(tsk, type); + slots->flexible = per_cpu(nr_bp_flexible[type], cpu); return; } @@ -137,16 +157,16 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) for_each_online_cpu(cpu) { unsigned int nr; - nr = per_cpu(nr_cpu_bp_pinned, cpu); + nr = per_cpu(nr_cpu_bp_pinned[type], cpu); if (!tsk) - nr += max_task_bp_pinned(cpu); + nr += max_task_bp_pinned(cpu, type); else - nr += task_bp_pinned(tsk); + nr += task_bp_pinned(tsk, type); if (nr > slots->pinned) slots->pinned = nr; - nr = per_cpu(nr_bp_flexible, cpu); + nr = per_cpu(nr_bp_flexible[type], cpu); if (nr > slots->flexible) slots->flexible = nr; @@ -156,14 +176,15 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp) /* * Add a pinned breakpoint for the given task in our constraint table */ -static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) +static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, + enum bp_type_idx type) { unsigned int *tsk_pinned; int count = 0; - count = task_bp_pinned(tsk); + count = task_bp_pinned(tsk, type); - tsk_pinned = per_cpu(nr_task_bp_pinned, cpu); + tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); if (enable) { tsk_pinned[count]++; if (count > 0) @@ -178,7 +199,8 @@ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable) /* * Add/remove the given breakpoint in our constraint table */ -static void toggle_bp_slot(struct perf_event *bp, bool enable) +static void +toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type) { int cpu = bp->cpu; struct task_struct *tsk = bp->ctx->task; @@ -186,20 +208,20 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) /* Pinned counter task profiling */ if (tsk) { if (cpu >= 0) { - toggle_bp_task_slot(tsk, cpu, enable); + toggle_bp_task_slot(tsk, cpu, enable, type); return; } for_each_online_cpu(cpu) - toggle_bp_task_slot(tsk, cpu, enable); + toggle_bp_task_slot(tsk, cpu, enable, type); return; } /* Pinned counter cpu profiling */ if (enable) - per_cpu(nr_cpu_bp_pinned, bp->cpu)++; + per_cpu(nr_cpu_bp_pinned[type], bp->cpu)++; else - per_cpu(nr_cpu_bp_pinned, bp->cpu)--; + per_cpu(nr_cpu_bp_pinned[type], bp->cpu)--; } /* @@ -246,14 +268,21 @@ static void toggle_bp_slot(struct perf_event *bp, bool enable) static int __reserve_bp_slot(struct perf_event *bp) { struct bp_busy_slots slots = {0}; + enum bp_type_idx type; - fetch_bp_busy_slots(&slots, bp); + /* Basic checks */ + if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || + bp->attr.bp_type == HW_BREAKPOINT_INVALID) + return -EINVAL; + + type = find_slot_idx(bp); + fetch_bp_busy_slots(&slots, bp, type); /* Flexible counters need to keep at least one slot */ if (slots.pinned + (!!slots.flexible) == HBP_NUM) return -ENOSPC; - toggle_bp_slot(bp, true); + toggle_bp_slot(bp, true, type); return 0; } @@ -273,7 +302,10 @@ int reserve_bp_slot(struct perf_event *bp) static void __release_bp_slot(struct perf_event *bp) { - toggle_bp_slot(bp, false); + enum bp_type_idx type; + + type = find_slot_idx(bp); + toggle_bp_slot(bp, false, type); } void release_bp_slot(struct perf_event *bp) -- cgit v0.10.2 From f93a20541134fa767e8dc4eb32e956d30b9f6b92 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 13 Apr 2010 00:32:30 +0200 Subject: hw-breakpoints: Handle breakpoint weight in allocation constraints Depending on their nature and on what an arch supports, breakpoints may consume more than one address register. For example a simple absolute address match usually only requires one address register. But an address range match may consume two registers. Currently our slot allocation constraints, that tend to reflect the limited arch's resources, always consider that a breakpoint consumes one slot. Then provide a way for archs to tell us the weight of a breakpoint through a new hw_breakpoint_weight() helper. This weight will be computed against the generic allocation constraints instead of a constant value. Signed-off-by: Frederic Weisbecker Acked-by: Paul Mundt Cc: Will Deacon Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Ingo Molnar diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 8ead134..974498b8 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -77,6 +77,11 @@ struct bp_busy_slots { /* Serialize accesses to the above constraints */ static DEFINE_MUTEX(nr_bp_mutex); +__weak int hw_breakpoint_weight(struct perf_event *bp) +{ + return 1; +} + static inline enum bp_type_idx find_slot_idx(struct perf_event *bp) { if (bp->attr.bp_type & HW_BREAKPOINT_RW) @@ -124,7 +129,7 @@ static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type) list_for_each_entry(bp, list, event_entry) { if (bp->attr.type == PERF_TYPE_BREAKPOINT) if (find_slot_idx(bp) == type) - count++; + count += hw_breakpoint_weight(bp); } raw_spin_unlock_irqrestore(&ctx->lock, flags); @@ -174,25 +179,40 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, } /* + * For now, continue to consider flexible as pinned, until we can + * ensure no flexible event can ever be scheduled before a pinned event + * in a same cpu. + */ +static void +fetch_this_slot(struct bp_busy_slots *slots, int weight) +{ + slots->pinned += weight; +} + +/* * Add a pinned breakpoint for the given task in our constraint table */ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, - enum bp_type_idx type) + enum bp_type_idx type, int weight) { unsigned int *tsk_pinned; - int count = 0; + int old_count = 0; + int old_idx = 0; + int idx = 0; - count = task_bp_pinned(tsk, type); + old_count = task_bp_pinned(tsk, type); + old_idx = old_count - 1; + idx = old_idx + weight; tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); if (enable) { - tsk_pinned[count]++; - if (count > 0) - tsk_pinned[count-1]--; + tsk_pinned[idx]++; + if (old_count > 0) + tsk_pinned[old_idx]--; } else { - tsk_pinned[count]--; - if (count > 0) - tsk_pinned[count-1]++; + tsk_pinned[idx]--; + if (old_count > 0) + tsk_pinned[old_idx]++; } } @@ -200,7 +220,8 @@ static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable, * Add/remove the given breakpoint in our constraint table */ static void -toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type) +toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, + int weight) { int cpu = bp->cpu; struct task_struct *tsk = bp->ctx->task; @@ -208,20 +229,20 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type) /* Pinned counter task profiling */ if (tsk) { if (cpu >= 0) { - toggle_bp_task_slot(tsk, cpu, enable, type); + toggle_bp_task_slot(tsk, cpu, enable, type, weight); return; } for_each_online_cpu(cpu) - toggle_bp_task_slot(tsk, cpu, enable, type); + toggle_bp_task_slot(tsk, cpu, enable, type, weight); return; } /* Pinned counter cpu profiling */ if (enable) - per_cpu(nr_cpu_bp_pinned[type], bp->cpu)++; + per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight; else - per_cpu(nr_cpu_bp_pinned[type], bp->cpu)--; + per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight; } /* @@ -269,6 +290,7 @@ static int __reserve_bp_slot(struct perf_event *bp) { struct bp_busy_slots slots = {0}; enum bp_type_idx type; + int weight; /* Basic checks */ if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || @@ -276,13 +298,16 @@ static int __reserve_bp_slot(struct perf_event *bp) return -EINVAL; type = find_slot_idx(bp); + weight = hw_breakpoint_weight(bp); + fetch_bp_busy_slots(&slots, bp, type); + fetch_this_slot(&slots, weight); /* Flexible counters need to keep at least one slot */ - if (slots.pinned + (!!slots.flexible) == HBP_NUM) + if (slots.pinned + (!!slots.flexible) > HBP_NUM) return -ENOSPC; - toggle_bp_slot(bp, true, type); + toggle_bp_slot(bp, true, type, weight); return 0; } @@ -303,9 +328,11 @@ int reserve_bp_slot(struct perf_event *bp) static void __release_bp_slot(struct perf_event *bp) { enum bp_type_idx type; + int weight; type = find_slot_idx(bp); - toggle_bp_slot(bp, false, type); + weight = hw_breakpoint_weight(bp); + toggle_bp_slot(bp, false, type, weight); } void release_bp_slot(struct perf_event *bp) -- cgit v0.10.2 From feef47d0cb530e8419dfa0b48141b538b89b1b1a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 23 Apr 2010 05:59:55 +0200 Subject: hw-breakpoints: Get the number of available registers on boot dynamically The breakpoint generic layer assumes that archs always know in advance the static number of address registers available to host breakpoints through the HBP_NUM macro. However this is not true for every archs. For example Arm needs to get this information dynamically to handle the compatiblity between different versions. To solve this, this patch proposes to drop the static HBP_NUM macro and let the arch provide the number of available slots through a new hw_breakpoint_slots() function. For archs that have CONFIG_HAVE_MIXED_BREAKPOINTS_REGS selected, it will be called once as the number of registers fits for instruction and data breakpoints together. For the others it will be called first to get the number of instruction breakpoint registers and another time to get the data breakpoint registers, the targeted type is given as a parameter of hw_breakpoint_slots(). Reported-by: Will Deacon Signed-off-by: Frederic Weisbecker Acked-by: Paul Mundt Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Jason Wessel Cc: Ingo Molnar diff --git a/arch/sh/include/asm/hw_breakpoint.h b/arch/sh/include/asm/hw_breakpoint.h index 382bad9..e14cad9 100644 --- a/arch/sh/include/asm/hw_breakpoint.h +++ b/arch/sh/include/asm/hw_breakpoint.h @@ -46,6 +46,11 @@ struct pmu; /* Maximum number of UBC channels */ #define HBP_NUM 2 +static inline int hw_breakpoint_slots(int type) +{ + return HBP_NUM; +} + /* arch/sh/kernel/hw_breakpoint.c */ extern int arch_check_bp_in_kernelspace(struct perf_event *bp); extern int arch_validate_hwbkpt_settings(struct perf_event *bp); diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index c77a5a6..9422553 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h @@ -41,6 +41,11 @@ struct arch_hw_breakpoint { /* Total number of available HW breakpoint registers */ #define HBP_NUM 4 +static inline int hw_breakpoint_slots(int type) +{ + return HBP_NUM; +} + struct perf_event; struct pmu; diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index 7e88990..a2d6ea4 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h @@ -17,6 +17,16 @@ enum { HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X, }; +enum bp_type_idx { + TYPE_INST = 0, +#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS + TYPE_DATA = 0, +#else + TYPE_DATA = 1, +#endif + TYPE_MAX +}; + #ifdef __KERNEL__ #include diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 974498b8..684b710 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -40,20 +40,12 @@ #include #include #include +#include #include #include #include -enum bp_type_idx { - TYPE_INST = 0, -#ifdef CONFIG_HAVE_MIXED_BREAKPOINTS_REGS - TYPE_DATA = 0, -#else - TYPE_DATA = 1, -#endif - TYPE_MAX -}; /* * Constraints data @@ -63,11 +55,15 @@ enum bp_type_idx { static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); /* Number of pinned task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, nr_task_bp_pinned[TYPE_MAX][HBP_NUM]); +static DEFINE_PER_CPU(unsigned int, *nr_task_bp_pinned[TYPE_MAX]); /* Number of non-pinned cpu/task breakpoints in a cpu */ static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); +static int nr_slots[TYPE_MAX]; + +static int constraints_initialized; + /* Gather the number of total pinned and un-pinned bp in a cpuset */ struct bp_busy_slots { unsigned int pinned; @@ -99,7 +95,7 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) int i; unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); - for (i = HBP_NUM -1; i >= 0; i--) { + for (i = nr_slots[type] - 1; i >= 0; i--) { if (tsk_pinned[i] > 0) return i + 1; } @@ -292,6 +288,10 @@ static int __reserve_bp_slot(struct perf_event *bp) enum bp_type_idx type; int weight; + /* We couldn't initialize breakpoint constraints on boot */ + if (!constraints_initialized) + return -ENOMEM; + /* Basic checks */ if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY || bp->attr.bp_type == HW_BREAKPOINT_INVALID) @@ -304,7 +304,7 @@ static int __reserve_bp_slot(struct perf_event *bp) fetch_this_slot(&slots, weight); /* Flexible counters need to keep at least one slot */ - if (slots.pinned + (!!slots.flexible) > HBP_NUM) + if (slots.pinned + (!!slots.flexible) > nr_slots[type]) return -ENOSPC; toggle_bp_slot(bp, true, type, weight); @@ -551,7 +551,36 @@ static struct notifier_block hw_breakpoint_exceptions_nb = { static int __init init_hw_breakpoint(void) { + unsigned int **task_bp_pinned; + int cpu, err_cpu; + int i; + + for (i = 0; i < TYPE_MAX; i++) + nr_slots[i] = hw_breakpoint_slots(i); + + for_each_possible_cpu(cpu) { + for (i = 0; i < TYPE_MAX; i++) { + task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu); + *task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i], + GFP_KERNEL); + if (!*task_bp_pinned) + goto err_alloc; + } + } + + constraints_initialized = 1; + return register_die_notifier(&hw_breakpoint_exceptions_nb); + + err_alloc: + for_each_possible_cpu(err_cpu) { + if (err_cpu == cpu) + break; + for (i = 0; i < TYPE_MAX; i++) + kfree(per_cpu(nr_task_bp_pinned[i], cpu)); + } + + return -ENOMEM; } core_initcall(init_hw_breakpoint); diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c index d59cd68..8eaf007 100644 --- a/kernel/trace/trace_ksym.c +++ b/kernel/trace/trace_ksym.c @@ -34,12 +34,6 @@ #include -/* - * For now, let us restrict the no. of symbols traced simultaneously to number - * of available hardware breakpoint registers. - */ -#define KSYM_TRACER_MAX HBP_NUM - #define KSYM_TRACER_OP_LEN 3 /* rw- */ struct trace_ksym { @@ -53,7 +47,6 @@ struct trace_ksym { static struct trace_array *ksym_trace_array; -static unsigned int ksym_filter_entry_count; static unsigned int ksym_tracing_enabled; static HLIST_HEAD(ksym_filter_head); @@ -181,13 +174,6 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) struct trace_ksym *entry; int ret = -ENOMEM; - if (ksym_filter_entry_count >= KSYM_TRACER_MAX) { - printk(KERN_ERR "ksym_tracer: Maximum limit:(%d) reached. No" - " new requests for tracing can be accepted now.\n", - KSYM_TRACER_MAX); - return -ENOSPC; - } - entry = kzalloc(sizeof(struct trace_ksym), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -203,13 +189,17 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) if (IS_ERR(entry->ksym_hbp)) { ret = PTR_ERR(entry->ksym_hbp); - printk(KERN_INFO "ksym_tracer request failed. Try again" - " later!!\n"); + if (ret == -ENOSPC) { + printk(KERN_ERR "ksym_tracer: Maximum limit reached." + " No new requests for tracing can be accepted now.\n"); + } else { + printk(KERN_INFO "ksym_tracer request failed. Try again" + " later!!\n"); + } goto err; } hlist_add_head_rcu(&(entry->ksym_hlist), &ksym_filter_head); - ksym_filter_entry_count++; return 0; @@ -265,7 +255,6 @@ static void __ksym_trace_reset(void) hlist_for_each_entry_safe(entry, node, node1, &ksym_filter_head, ksym_hlist) { unregister_wide_hw_breakpoint(entry->ksym_hbp); - ksym_filter_entry_count--; hlist_del_rcu(&(entry->ksym_hlist)); synchronize_rcu(); kfree(entry); @@ -338,7 +327,6 @@ static ssize_t ksym_trace_filter_write(struct file *file, goto out_unlock; } /* Error or "symbol:---" case: drop it */ - ksym_filter_entry_count--; hlist_del_rcu(&(entry->ksym_hlist)); synchronize_rcu(); kfree(entry); -- cgit v0.10.2 From fb72014d98afd51e85aab9c061344ef32d615606 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Apr 2010 19:31:12 -0300 Subject: perf tools: Don't use code surrounded by __KERNEL__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to refactor code to be explicitely shared by the kernel and at least the tools/ userspace programs, so, till we do that, copy the bare minimum bitmap/bitops code needed by tools/perf. Reported-by: "H. Peter Anvin" Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 739c441..c5ac0a9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -377,9 +377,9 @@ LIB_H += util/include/linux/rbtree.h LIB_H += util/include/linux/string.h LIB_H += util/include/linux/types.h LIB_H += util/include/asm/asm-offsets.h -LIB_H += util/include/asm/bitops.h LIB_H += util/include/asm/bug.h LIB_H += util/include/asm/byteorder.h +LIB_H += util/include/asm/hweight.h LIB_H += util/include/asm/swab.h LIB_H += util/include/asm/system.h LIB_H += util/include/asm/uaccess.h @@ -435,7 +435,6 @@ LIB_OBJS += $(OUTPUT)util/path.o LIB_OBJS += $(OUTPUT)util/rbtree.o LIB_OBJS += $(OUTPUT)util/bitmap.o LIB_OBJS += $(OUTPUT)util/hweight.o -LIB_OBJS += $(OUTPUT)util/find_next_bit.o LIB_OBJS += $(OUTPUT)util/run-command.o LIB_OBJS += $(OUTPUT)util/quote.o LIB_OBJS += $(OUTPUT)util/strbuf.o @@ -948,19 +947,6 @@ $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -# some perf warning policies can't fit to lib/bitmap.c, eg: it warns about variable shadowing -# from that comes from kernel headers wrapping. -KBITMAP_FLAGS=`echo $(ALL_CFLAGS) | sed s/-Wshadow// | sed s/-Wswitch-default// | sed s/-Wextra//` - -$(OUTPUT)util/bitmap.o: ../../lib/bitmap.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< - -$(OUTPUT)util/hweight.o: ../../lib/hweight.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< - -$(OUTPUT)util/find_next_bit.o: ../../lib/find_next_bit.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< - $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c new file mode 100644 index 0000000..5e230ac --- /dev/null +++ b/tools/perf/util/bitmap.c @@ -0,0 +1,21 @@ +/* + * From lib/bitmap.c + * Helper functions for bitmap.h. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +#include + +int __bitmap_weight(const unsigned long *bitmap, int bits) +{ + int k, w = 0, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; k++) + w += hweight_long(bitmap[k]); + + if (bits % BITS_PER_LONG) + w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); + + return w; +} diff --git a/tools/perf/util/hweight.c b/tools/perf/util/hweight.c new file mode 100644 index 0000000..5c1d0d0 --- /dev/null +++ b/tools/perf/util/hweight.c @@ -0,0 +1,31 @@ +#include + +/** + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +unsigned int hweight32(unsigned int w) +{ + unsigned int res = w - ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res + (res >> 4)) & 0x0F0F0F0F; + res = res + (res >> 8); + return (res + (res >> 16)) & 0x000000FF; +} + +unsigned long hweight64(__u64 w) +{ +#if BITS_PER_LONG == 32 + return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w); +#elif BITS_PER_LONG == 64 + __u64 res = w - ((w >> 1) & 0x5555555555555555ul); + res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); + res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful; + res = res + (res >> 8); + res = res + (res >> 16); + return (res + (res >> 32)) & 0x00000000000000FFul; +#endif +} diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h deleted file mode 100644 index 58e9817..0000000 --- a/tools/perf/util/include/asm/bitops.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _PERF_ASM_BITOPS_H_ -#define _PERF_ASM_BITOPS_H_ - -#include -#include "../../types.h" -#include - -/* CHECKME: Not sure both always match */ -#define BITS_PER_LONG __WORDSIZE - -#include "../../../../include/asm-generic/bitops/__fls.h" -#include "../../../../include/asm-generic/bitops/fls.h" -#include "../../../../include/asm-generic/bitops/fls64.h" -#include "../../../../include/asm-generic/bitops/__ffs.h" -#include "../../../../include/asm-generic/bitops/ffz.h" -#include "../../../../include/asm-generic/bitops/hweight.h" - -#endif diff --git a/tools/perf/util/include/asm/hweight.h b/tools/perf/util/include/asm/hweight.h new file mode 100644 index 0000000..36cf26d --- /dev/null +++ b/tools/perf/util/include/asm/hweight.h @@ -0,0 +1,8 @@ +#ifndef PERF_HWEIGHT_H +#define PERF_HWEIGHT_H + +#include +unsigned int hweight32(unsigned int w); +unsigned long hweight64(__u64 w); + +#endif /* PERF_HWEIGHT_H */ diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 9450763..eda4416 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -1,3 +1,35 @@ -#include "../../../../include/linux/bitmap.h" -#include "../../../../include/asm-generic/bitops/find.h" -#include +#ifndef _PERF_BITOPS_H +#define _PERF_BITOPS_H + +#include +#include + +int __bitmap_weight(const unsigned long *bitmap, int bits); + +#define BITMAP_LAST_WORD_MASK(nbits) \ +( \ + ((nbits) % BITS_PER_LONG) ? \ + (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ +) + +#define small_const_nbits(nbits) \ + (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) + +static inline void bitmap_zero(unsigned long *dst, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = 0UL; + else { + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + memset(dst, 0, len); + } +} + +static inline int bitmap_weight(const unsigned long *src, int nbits) +{ + if (small_const_nbits(nbits)) + return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); + return __bitmap_weight(src, nbits); +} + +#endif /* _PERF_BITOPS_H */ diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 8d63116..bb4ac2e 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -1,13 +1,12 @@ #ifndef _PERF_LINUX_BITOPS_H_ #define _PERF_LINUX_BITOPS_H_ -#define __KERNEL__ +#include +#include -#define CONFIG_GENERIC_FIND_NEXT_BIT -#define CONFIG_GENERIC_FIND_FIRST_BIT -#include "../../../../include/linux/bitops.h" - -#undef __KERNEL__ +#define BITS_PER_LONG __WORDSIZE +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) static inline void set_bit(int nr, unsigned long *addr) { @@ -20,10 +19,9 @@ static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; } -unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); - -unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); +static inline unsigned long hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} #endif -- cgit v0.10.2 From 789688faef5b3ba78065beaf2f3d6f1c839f74a3 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sat, 1 May 2010 01:41:19 -0500 Subject: perf/live: don't synthesize build ids at the end of a live mode trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't really make sense to record the build ids at the end of a live mode session - live mode samples need that information during the trace rather than at the end. Leave event__synthesize_build_id() in place, however; we'll still be using that to synthesize build ids in a more timely fashion in a future patch. Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Steven Rostedt LKML-Reference: <1272696080-16435-2-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 83b308a..1a73796 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -446,13 +446,6 @@ static void atexit_header(void) process_buildids(); perf_header__write(&session->header, output, true); - } else { - int err; - - err = event__synthesize_build_ids(process_synthesized_event, - session); - if (err < 0) - pr_err("Couldn't synthesize build ids.\n"); } } diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6227dc4..2d1d97e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1136,67 +1136,6 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, return err; } -static int __event_synthesize_build_ids(struct list_head *head, u16 misc, - event__handler_t process, - struct machine *machine, - struct perf_session *session) -{ - struct dso *pos; - - dsos__for_each_with_build_id(pos, head) { - int err; - if (!pos->hit) - continue; - - err = event__synthesize_build_id(pos, misc, process, - machine, session); - if (err < 0) - return err; - } - - return 0; -} - -int event__synthesize_build_ids(event__handler_t process, - struct perf_session *session) -{ - int err = 0; - u16 kmisc, umisc; - struct machine *pos; - struct rb_node *nd; - - if (!dsos__read_build_ids(&session->header, true)) - return 0; - - for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { - pos = rb_entry(nd, struct machine, rb_node); - if (machine__is_host(pos)) { - kmisc = PERF_RECORD_MISC_KERNEL; - umisc = PERF_RECORD_MISC_USER; - } else { - kmisc = PERF_RECORD_MISC_GUEST_KERNEL; - umisc = PERF_RECORD_MISC_GUEST_USER; - } - - err = __event_synthesize_build_ids(&pos->kernel_dsos, kmisc, - process, pos, session); - if (err == 0) - err = __event_synthesize_build_ids(&pos->user_dsos, umisc, - process, pos, session); - if (err) - break; - } - - if (err < 0) { - pr_debug("failed to synthesize build ids\n"); - return err; - } - - dsos__cache_build_ids(&session->header); - - return 0; -} - int event__process_build_id(event_t *self, struct perf_session *session) { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index f39443d..402ac24 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -122,8 +122,6 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, struct machine *machine, struct perf_session *session); -int event__synthesize_build_ids(event__handler_t process, - struct perf_session *session); int event__process_build_id(event_t *self, struct perf_session *session); #endif /* __PERF_HEADER_H */ -- cgit v0.10.2 From 454c407ec17a0c63e4023ac0877d687945a7df4a Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sat, 1 May 2010 01:41:20 -0500 Subject: perf: add perf-inject builtin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, perf 'live mode' writes build-ids at the end of the session, which isn't actually useful for processing live mode events. What would be better would be to have the build-ids sent before any of the samples that reference them, which can be done by processing the event stream and retrieving the build-ids on the first hit. Doing that in perf-record itself, however, is off-limits. This patch introduces perf-inject, which does the same job while leaving perf-record untouched. Normal mode perf still records the build-ids at the end of the session as it should, but for live mode, perf-inject can be injected in between the record and report steps e.g.: perf record -o - ./hackbench 10 | perf inject -v -b | perf report -v -i - perf-inject reads a perf-record event stream and repipes it to stdout. At any point the processing code can inject other events into the event stream - in this case build-ids (-b option) are read and injected as needed into the event stream. Build-ids are just the first user of perf-inject - potentially anything that needs userspace processing to augment the trace stream with additional information could make use of this facility. Cc: Ingo Molnar Cc: Steven Rostedt Cc: Frédéric Weisbecker LKML-Reference: <1272696080-16435-3-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index c5ac0a9..0ef5cfe 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -490,6 +490,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o BUILTIN_OBJS += $(OUTPUT)builtin-lock.o BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o BUILTIN_OBJS += $(OUTPUT)builtin-test.o +BUILTIN_OBJS += $(OUTPUT)builtin-inject.o PERFLIBS = $(LIB_FILE) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b57dbcf..ee154b5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -554,7 +554,7 @@ static int __cmd_annotate(void) int ret; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force); + session = perf_session__new(input_name, O_RDONLY, force, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 7dc3b2e..44a47e1 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -39,7 +39,7 @@ static int __cmd_buildid_list(void) int err = -1; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force); + session = perf_session__new(input_name, O_RDONLY, force, false); if (session == NULL) return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 207e860..4cce68f 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -156,8 +156,8 @@ static int __cmd_diff(void) int ret, i; struct perf_session *session[2]; - session[0] = perf_session__new(input_old, O_RDONLY, force); - session[1] = perf_session__new(input_new, O_RDONLY, force); + session[0] = perf_session__new(input_old, O_RDONLY, force, false); + session[1] = perf_session__new(input_new, O_RDONLY, force, false); if (session[0] == NULL || session[1] == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c new file mode 100644 index 0000000..a5902a3 --- /dev/null +++ b/tools/perf/builtin-inject.c @@ -0,0 +1,228 @@ +/* + * builtin-inject.c + * + * Builtin inject command: Examine the live mode (stdin) event stream + * and repipe it to stdout while optionally injecting additional + * events into it. + */ +#include "builtin.h" + +#include "perf.h" +#include "util/session.h" +#include "util/debug.h" + +#include "util/parse-options.h" + +static char const *input_name = "-"; +static bool inject_build_ids; + +static int event__repipe(event_t *event __used, + struct perf_session *session __used) +{ + uint32_t size; + void *buf = event; + + size = event->header.size; + + while (size) { + int ret = write(STDOUT_FILENO, buf, size); + if (ret < 0) + return -errno; + + size -= ret; + buf += ret; + } + + return 0; +} + +static int event__repipe_mmap(event_t *self, struct perf_session *session) +{ + int err; + + err = event__process_mmap(self, session); + event__repipe(self, session); + + return err; +} + +static int event__repipe_task(event_t *self, struct perf_session *session) +{ + int err; + + err = event__process_task(self, session); + event__repipe(self, session); + + return err; +} + +static int event__repipe_tracing_data(event_t *self, + struct perf_session *session) +{ + int err; + + event__repipe(self, session); + err = event__process_tracing_data(self, session); + + return err; +} + +static int read_buildid(struct map *self, struct perf_session *session) +{ + const char *name = self->dso->long_name; + int err; + + if (filename__read_build_id(self->dso->long_name, self->dso->build_id, + sizeof(self->dso->build_id)) > 0) { + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + + self->dso->has_build_id = true; + + build_id__sprintf(self->dso->build_id, + sizeof(self->dso->build_id), + sbuild_id); + pr_debug("build id found for %s: %s\n", self->dso->long_name, + sbuild_id); + } + + if (self->dso->has_build_id) { + u16 misc = PERF_RECORD_MISC_USER; + struct machine *machine; + + misc = self->dso->kernel ? PERF_RECORD_MISC_KERNEL : misc; + + machine = perf_session__find_host_machine(session); + if (!machine) { + pr_err("Can't find machine for session\n"); + return -1; + } + + err = event__synthesize_build_id(self->dso, misc, + event__repipe, machine, + session); + if (err) { + pr_err("Can't synthesize build_id event for %s\n", + name); + return -1; + } + } else { + pr_debug("no build_id found for %s\n", name); + return -1; + } + + return 0; +} + +static int event__inject_buildid(event_t *event, struct perf_session *session) +{ + struct addr_location al; + struct thread *thread; + u8 cpumode; + int err = 0; + + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + thread = perf_session__findnew(session, event->ip.pid); + if (thread == NULL) { + pr_err("problem processing %d event, skipping it.\n", + event->header.type); + err = -1; + goto repipe; + } + + thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, + event->ip.pid, event->ip.ip, &al); + + if (al.map != NULL) { + if (!al.map->dso->hit) { + al.map->dso->hit = 1; + if (map__load(al.map, NULL) >= 0) + read_buildid(al.map, session); + else + pr_warning("no symbols found in %s, maybe " + "install a debug package?\n", + al.map->dso->long_name); + } + } + +repipe: + event__repipe(event, session); + return err; +} + +struct perf_event_ops inject_ops = { + .sample = event__repipe, + .mmap = event__repipe, + .comm = event__repipe, + .fork = event__repipe, + .exit = event__repipe, + .lost = event__repipe, + .read = event__repipe, + .throttle = event__repipe, + .unthrottle = event__repipe, + .attr = event__repipe, + .event_type = event__repipe, + .tracing_data = event__repipe, + .build_id = event__repipe, +}; + +extern volatile int session_done; + +static void sig_handler(int sig __attribute__((__unused__))) +{ + session_done = 1; +} + +static int __cmd_inject(void) +{ + struct perf_session *session; + int ret = -EINVAL; + + signal(SIGINT, sig_handler); + + if (inject_build_ids) { + inject_ops.sample = event__inject_buildid; + inject_ops.mmap = event__repipe_mmap; + inject_ops.fork = event__repipe_task; + inject_ops.tracing_data = event__repipe_tracing_data; + } + + session = perf_session__new(input_name, O_RDONLY, false, true); + if (session == NULL) + return -ENOMEM; + + ret = perf_session__process_events(session, &inject_ops); + + perf_session__delete(session); + + return ret; +} + +static const char * const report_usage[] = { + "perf inject []", + NULL +}; + +static const struct option options[] = { + OPT_BOOLEAN('b', "inject build-ids", &inject_build_ids, + "Inject build-ids into the output stream"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show build ids, etc)"), + OPT_END() +}; + +int cmd_inject(int argc, const char **argv, const char *prefix __used) +{ + argc = parse_options(argc, argv, options, report_usage, 0); + + /* + * Any (unrecognized) arguments left? + */ + if (argc) + usage_with_options(report_usage, options); + + if (symbol__init() < 0) + return -1; + + return __cmd_inject(); +} diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index ee05dba..31f60a2 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -492,7 +492,7 @@ static void sort_result(void) static int __cmd_kmem(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index ce27675..6605000 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -818,7 +818,7 @@ static struct perf_event_ops eops = { static int read_events(void) { - session = perf_session__new(input_name, O_RDONLY, 0); + session = perf_session__new(input_name, O_RDONLY, 0, false); if (!session) die("Initializing perf session failed\n"); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 1a73796..e382d93 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -548,7 +548,7 @@ static int __cmd_record(int argc, const char **argv) } session = perf_session__new(output_name, O_WRONLY, - write_mode == WRITE_FORCE); + write_mode == WRITE_FORCE, false); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f1b46eb..0152b54 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -289,7 +289,7 @@ static int __cmd_report(void) signal(SIGINT, sig_handler); - session = perf_session__new(input_name, O_RDONLY, force); + session = perf_session__new(input_name, O_RDONLY, force, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 94453f1..aef6ed0 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1660,7 +1660,7 @@ static struct perf_event_ops event_ops = { static int read_events(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index c35aa44..5a52ed9 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -936,7 +936,7 @@ static struct perf_event_ops event_ops = { static int __cmd_timechart(void) { - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); int ret = -EINVAL; if (session == NULL) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d95281f..3de3977 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1287,7 +1287,7 @@ static int __cmd_top(void) * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. */ - struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); + struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 77f556f..9c483e9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -661,7 +661,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) if (!script_name) setup_pager(); - session = perf_session__new(input_name, O_RDONLY, 0); + session = perf_session__new(input_name, O_RDONLY, 0, false); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 34a8a9a..921245b 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -34,5 +34,6 @@ extern int cmd_kmem(int argc, const char **argv, const char *prefix); extern int cmd_lock(int argc, const char **argv, const char *prefix); extern int cmd_kvm(int argc, const char **argv, const char *prefix); extern int cmd_test(int argc, const char **argv, const char *prefix); +extern int cmd_inject(int argc, const char **argv, const char *prefix); #endif diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 5ff9b5b..08e0e5d 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -309,6 +309,7 @@ static void handle_internal_command(int argc, const char **argv) { "lock", cmd_lock, 0 }, { "kvm", cmd_kvm, 0 }, { "test", cmd_test, 0 }, + { "inject", cmd_inject, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2d1d97e..79da0e5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -713,10 +713,18 @@ static int __event_process_build_id(struct build_id_event *bev, dso = __dsos__findnew(head, filename); if (dso != NULL) { + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + dso__set_build_id(dso, &bev->build_id); - if (filename[0] == '[') - dso->kernel = dso_type; - } + + if (filename[0] == '[') + dso->kernel = dso_type; + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), + sbuild_id); + pr_debug("build id event received for %s: %s\n", + dso->long_name, sbuild_id); + } err = 0; out: @@ -767,7 +775,7 @@ static int perf_file_section__process(struct perf_file_section *self, switch (feat) { case HEADER_TRACE_INFO: - trace_report(fd); + trace_report(fd, false); break; case HEADER_BUILD_ID: @@ -782,12 +790,16 @@ static int perf_file_section__process(struct perf_file_section *self, } static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, - struct perf_header *ph, int fd) + struct perf_header *ph, int fd, + bool repipe) { if (do_read(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; + if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) + return -1; + if (self->size != sizeof(*self)) { u64 size = bswap_64(self->size); @@ -805,7 +817,8 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) struct perf_header *self = &session->header; struct perf_pipe_file_header f_header; - if (perf_file_header__read_pipe(&f_header, self, fd) < 0) { + if (perf_file_header__read_pipe(&f_header, self, fd, + session->repipe) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; } @@ -1096,12 +1109,17 @@ int event__process_tracing_data(event_t *self, lseek(session->fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(session->fd); + size_read = trace_report(session->fd, session->repipe); padding = ALIGN(size_read, sizeof(u64)) - size_read; if (read(session->fd, buf, padding) < 0) die("reading input file"); + if (session->repipe) { + int retw = write(STDOUT_FILENO, buf, padding); + if (retw <= 0 || retw != padding) + die("repiping tracing data padding"); + } if (size_read + padding != size) die("tracing data size mismatch"); @@ -1110,7 +1128,8 @@ int event__process_tracing_data(event_t *self, } int event__synthesize_build_id(struct dso *pos, u16 misc, - event__handler_t process, struct machine *machine, + event__handler_t process, + struct machine *machine, struct perf_session *session) { event_t ev; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a8dd73e..5d353e7 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -77,7 +77,7 @@ int perf_session__create_kernel_maps(struct perf_session *self) return ret; } -struct perf_session *perf_session__new(const char *filename, int mode, bool force) +struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) { size_t len = filename ? strlen(filename) + 1 : 0; struct perf_session *self = zalloc(sizeof(*self) + len); @@ -97,6 +97,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->cwdlen = 0; self->unknown_events = 0; self->machines = RB_ROOT; + self->repipe = repipe; self->ordered_samples.flush_limit = ULLONG_MAX; INIT_LIST_HEAD(&self->ordered_samples.samples_head); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 61ca92e..f2b2c6a 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -34,6 +34,7 @@ struct perf_session { u64 sample_type; int fd; bool fd_pipe; + bool repipe; int cwdlen; char *cwd; struct ordered_samples ordered_samples; @@ -59,7 +60,7 @@ struct perf_event_ops { bool ordered_samples; }; -struct perf_session *perf_session__new(const char *filename, int mode, bool force); +struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); void perf_session__delete(struct perf_session *self); void perf_event_header__bswap(struct perf_event_header *self); diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 44889c9..43f19c1 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -51,6 +51,7 @@ static int long_size; static unsigned long page_size; static ssize_t calc_data_size; +static bool repipe; static int do_read(int fd, void *buf, int size) { @@ -62,6 +63,13 @@ static int do_read(int fd, void *buf, int size) if (ret <= 0) return -1; + if (repipe) { + int retw = write(STDOUT_FILENO, buf, ret); + + if (retw <= 0 || retw != ret) + die("repiping input file"); + } + size -= ret; buf += ret; } @@ -116,6 +124,13 @@ static char *read_string(void) if (!r) die("no data"); + if (repipe) { + int retw = write(STDOUT_FILENO, &c, 1); + + if (retw <= 0 || retw != r) + die("repiping input file string"); + } + buf[size++] = c; if (!c) @@ -454,7 +469,7 @@ struct record *trace_read_data(int cpu) return data; } -ssize_t trace_report(int fd) +ssize_t trace_report(int fd, bool __repipe) { char buf[BUFSIZ]; char test[] = { 23, 8, 68 }; @@ -465,6 +480,7 @@ ssize_t trace_report(int fd) ssize_t size; calc_data_size = 1; + repipe = __repipe; input_fd = fd; @@ -499,6 +515,7 @@ ssize_t trace_report(int fd) size = calc_data_size - 1; calc_data_size = 0; + repipe = false; if (show_funcs) { print_funcs(); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 1f45d46..ebfee80 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -163,7 +163,7 @@ struct record *trace_read_data(int cpu); void parse_set_info(int nr_cpus, int long_sz); -ssize_t trace_report(int fd); +ssize_t trace_report(int fd, bool repipe); void *malloc_or_die(unsigned int size); -- cgit v0.10.2 From 2c9faa060064343a4a0b16f5b77f3c61d1d17e23 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 2 May 2010 13:37:24 -0300 Subject: perf record: Don't exit in live mode when no tracepoints are enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this I was able to actually test Tom Zanussi's two previous patches in my usual perf testing ways, i.e. without any tracepoints activated. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e382d93..ac989e9 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -666,12 +666,15 @@ static int __cmd_record(int argc, const char **argv) nr_counters, process_synthesized_event, session); - if (err <= 0) { - pr_err("Couldn't record tracing data.\n"); - return err; - } - - advance_output(err); + /* + * FIXME err <= 0 here actually means that there were no tracepoints + * so its not really an error, just that we don't need to synthesize + * anything. + * We really have to return this more properly and also propagate + * errors that now are calling die() + */ + if (err > 0) + advance_output(err); } machine = perf_session__find_host_machine(session); -- cgit v0.10.2 From 090f7204dfdb5d7f18208ea81dfdba845897cedd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 2 May 2010 19:46:36 -0300 Subject: perf inject: Refactor read_buildid function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Into two functions, one that actually reads the build_id for the dso if it wasn't already read, and another taht will inject the event if the build_id is available. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a5902a3..59e981a 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -67,46 +67,44 @@ static int event__repipe_tracing_data(event_t *self, return err; } -static int read_buildid(struct map *self, struct perf_session *session) +static int dso__read_build_id(struct dso *self) { - const char *name = self->dso->long_name; - int err; + if (self->has_build_id) + return 0; - if (filename__read_build_id(self->dso->long_name, self->dso->build_id, - sizeof(self->dso->build_id)) > 0) { - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + if (filename__read_build_id(self->long_name, self->build_id, + sizeof(self->build_id)) > 0) { + self->has_build_id = true; + return 0; + } - self->dso->has_build_id = true; + return -1; +} - build_id__sprintf(self->dso->build_id, - sizeof(self->dso->build_id), - sbuild_id); - pr_debug("build id found for %s: %s\n", self->dso->long_name, - sbuild_id); - } +static int dso__inject_build_id(struct dso *self, struct perf_session *session) +{ + u16 misc = PERF_RECORD_MISC_USER; + struct machine *machine; + int err; - if (self->dso->has_build_id) { - u16 misc = PERF_RECORD_MISC_USER; - struct machine *machine; + if (dso__read_build_id(self) < 0) { + pr_debug("no build_id found for %s\n", self->long_name); + return -1; + } - misc = self->dso->kernel ? PERF_RECORD_MISC_KERNEL : misc; + machine = perf_session__find_host_machine(session); + if (machine == NULL) { + pr_err("Can't find machine for session\n"); + return -1; + } - machine = perf_session__find_host_machine(session); - if (!machine) { - pr_err("Can't find machine for session\n"); - return -1; - } + if (self->kernel) + misc = PERF_RECORD_MISC_KERNEL; - err = event__synthesize_build_id(self->dso, misc, - event__repipe, machine, - session); - if (err) { - pr_err("Can't synthesize build_id event for %s\n", - name); - return -1; - } - } else { - pr_debug("no build_id found for %s\n", name); + err = event__synthesize_build_id(self, misc, event__repipe, + machine, session); + if (err) { + pr_err("Can't synthesize build_id event for %s\n", self->long_name); return -1; } @@ -118,7 +116,6 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) struct addr_location al; struct thread *thread; u8 cpumode; - int err = 0; cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -126,7 +123,6 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) if (thread == NULL) { pr_err("problem processing %d event, skipping it.\n", event->header.type); - err = -1; goto repipe; } @@ -136,9 +132,13 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) if (al.map != NULL) { if (!al.map->dso->hit) { al.map->dso->hit = 1; - if (map__load(al.map, NULL) >= 0) - read_buildid(al.map, session); - else + if (map__load(al.map, NULL) >= 0) { + dso__inject_build_id(al.map->dso, session); + /* + * If this fails, too bad, let the other side + * account this as unresolved. + */ + } else pr_warning("no symbols found in %s, maybe " "install a debug package?\n", al.map->dso->long_name); @@ -147,7 +147,7 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) repipe: event__repipe(event, session); - return err; + return 0; } struct perf_event_ops inject_ops = { -- cgit v0.10.2 From 63e0c7715aab6085faa487d498889f4361dc6542 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Mon, 3 May 2010 00:14:48 -0500 Subject: perf: record TRACE_INFO only if using tracepoints and SAMPLE_RAW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current perf code implicitly assumes SAMPLE_RAW means tracepoints are being used, but doesn't check for that. It happily records the TRACE_INFO even if SAMPLE_RAW is used without tracepoints, but when the perf data is read it won't go any further when it finds TRACE_INFO but no tracepoints, and displays misleading errors. This adds a check for both in perf-record, and won't record TRACE_INFO unless both are true. This at least allows perf report -D to dump raw events, and avoids triggering a misleading error condition in perf trace. It doesn't actually enable the non-tracepoint raw events to be displayed in perf trace, since perf trace currently only deals with tracepoint events. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <1272865861.7932.16.camel@tropicana> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ac989e9..0ff67d1 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -560,11 +560,12 @@ static int __cmd_record(int argc, const char **argv) return err; } - if (raw_samples) { + if (raw_samples && have_tracepoints(attrs, nr_counters)) { perf_header__set_feat(&session->header, HEADER_TRACE_INFO); } else { for (i = 0; i < nr_counters; i++) { - if (attrs[i].sample_type & PERF_SAMPLE_RAW) { + if (attrs[i].sample_type & PERF_SAMPLE_RAW && + attrs[i].type == PERF_TYPE_TRACEPOINT) { perf_header__set_feat(&session->header, HEADER_TRACE_INFO); break; } @@ -662,19 +663,25 @@ static int __cmd_record(int argc, const char **argv) return err; } - err = event__synthesize_tracing_data(output, attrs, - nr_counters, - process_synthesized_event, - session); - /* - * FIXME err <= 0 here actually means that there were no tracepoints - * so its not really an error, just that we don't need to synthesize - * anything. - * We really have to return this more properly and also propagate - * errors that now are calling die() - */ - if (err > 0) + if (have_tracepoints(attrs, nr_counters)) { + /* + * FIXME err <= 0 here actually means that + * there were no tracepoints so its not really + * an error, just that we don't need to + * synthesize anything. We really have to + * return this more properly and also + * propagate errors that now are calling die() + */ + err = event__synthesize_tracing_data(output, attrs, + nr_counters, + process_synthesized_event, + session); + if (err <= 0) { + pr_err("Couldn't record tracing data.\n"); + return err; + } advance_output(err); + } } machine = perf_session__find_host_machine(session); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 79da0e5..2b9f898 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -436,7 +436,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd) trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; } - if (perf_header__has_feat(self, HEADER_BUILD_ID)) { struct perf_file_section *buildid_sec; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b8c1f64..fc4ab3f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -13,6 +13,7 @@ struct tracepoint_path { }; extern struct tracepoint_path *tracepoint_id_to_path(u64 config); +extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events); extern int nr_counters; diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 30cd9b5..0a1fb9d 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -487,6 +487,11 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) return nr_tracepoints > 0 ? path.next : NULL; } +bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events) +{ + return get_tracepoints_path(pattrs, nb_events) ? true : false; +} + int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) { char buf[BUFSIZ]; -- cgit v0.10.2 From 777d0411cd1e384115985dac5ccd42031e3eee2b Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 3 May 2010 15:39:45 +0200 Subject: hw_breakpoints: Fix percpu build failure Fix this build error: kernel/hw_breakpoint.c:58:1: error: pasting "__pcpu_scope_" and "*" does not give a valid preprocessing token It happens if CONFIG_DEBUG_FORCE_WEAK_PER_CPU, because we concatenate someting with the name and we have the "*" in the name. Cc: Frederic Weisbecker Cc: Will Deacon Cc: Paul Mundt Cc: Mahesh Salgaonkar Cc: K. Prasad Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Jason Wessel LKML-Reference: <20100503133942.GA5497@nowhere> Signed-off-by: Ingo Molnar diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 684b710..7a56b22 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c @@ -55,7 +55,7 @@ static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); /* Number of pinned task breakpoints in a cpu */ -static DEFINE_PER_CPU(unsigned int, *nr_task_bp_pinned[TYPE_MAX]); +static DEFINE_PER_CPU(unsigned int *, nr_task_bp_pinned[TYPE_MAX]); /* Number of non-pinned cpu/task breakpoints in a cpu */ static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); -- cgit v0.10.2 From 11d232ec285b07860670277c8ab3f6076f7bce1e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 4 May 2010 10:48:22 -0300 Subject: perf inject: Add missing bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New commands need to have Documentation and be added to command-list.txt so that they can appear when 'perf' is called withouth any subcommand: [root@doppio linux-2.6-tip]# perf usage: perf [--version] [--help] COMMAND [ARGS] The most commonly used perf commands are: annotate Read perf.data (created by perf record) and display annotated code archive Create archive with object files with build-ids found in perf.data file bench General framework for benchmark suites buildid-cache Manage build-id cache. buildid-list List the buildids in a perf.data file diff Read two perf.data files and display the differential profile inject Filter to augment the events stream with additional information kmem Tool to trace/measure kernel memory(slab) properties kvm Tool to trace/measure kvm guest os list List all symbolic event types lock Analyze lock events probe Define new dynamic tracepoints record Run a command and record its profile into perf.data report Read perf.data (created by perf record) and display the profile sched Tool to trace/measure scheduler properties (latencies) stat Run a command and gather performance counter statistics test Runs sanity tests. timechart Tool to visualize total system behavior during a workload top System profiling tool. trace Read perf.data (created by perf record) and display trace output See 'perf help COMMAND' for more information on a specific command. [root@doppio linux-2.6-tip]# The new 'perf inject' command hadn't so it wasn't appearing on that list. Also fix the long option, that should have no spaces in it, rename the faulty one to be '--build-ids', instead of '--inject build-ids'. Reported-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index 88bc3b5..5d1a950 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt @@ -8,7 +8,7 @@ perf-buildid-cache - Manage build-id cache. SYNOPSIS -------- [verse] -'perf buildid-list ' +'perf buildid-cache ' DESCRIPTION ----------- @@ -30,4 +30,4 @@ OPTIONS SEE ALSO -------- -linkperf:perf-record[1], linkperf:perf-report[1] +linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1] diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt new file mode 100644 index 0000000..025630d --- /dev/null +++ b/tools/perf/Documentation/perf-inject.txt @@ -0,0 +1,35 @@ +perf-inject(1) +============== + +NAME +---- +perf-inject - Filter to augment the events stream with additional information + +SYNOPSIS +-------- +[verse] +'perf inject ' + +DESCRIPTION +----------- +perf-inject reads a perf-record event stream and repipes it to stdout. At any +point the processing code can inject other events into the event stream - in +this case build-ids (-b option) are read and injected as needed into the event +stream. + +Build-ids are just the first user of perf-inject - potentially anything that +needs userspace processing to augment the events stream with additional +information could make use of this facility. + +OPTIONS +------- +-b:: +--build-ids=:: + Inject build-ids into the output stream +-v:: +--verbose:: + Be more verbose. + +SEE ALSO +-------- +linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 59e981a..8e3e47b 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -204,7 +204,7 @@ static const char * const report_usage[] = { }; static const struct option options[] = { - OPT_BOOLEAN('b', "inject build-ids", &inject_build_ids, + OPT_BOOLEAN('b', "build-ids", &inject_build_ids, "Inject build-ids into the output stream"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show build ids, etc)"), diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 80a1a44..949d77f 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -8,6 +8,7 @@ perf-bench mainporcelain common perf-buildid-cache mainporcelain common perf-buildid-list mainporcelain common perf-diff mainporcelain common +perf-inject mainporcelain common perf-list mainporcelain common perf-sched mainporcelain common perf-record mainporcelain common -- cgit v0.10.2 From 02bf60aad7d5912dfcdbe0154f1bd67ea7a8301e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 4 May 2010 21:19:15 +1000 Subject: perf: Fix performance issue with perf report On a large machine we spend a lot of time in perf_header__find_attr when running perf report. If we are parsing a file without PERF_SAMPLE_ID then for each sample we call perf_header__find_attr and loop through all counter IDs, never finding a match. As the machine gets larger there are more per cpu counters and we spend an awful lot of time in there. The patch below initialises each sample id to -1ULL and checks for this in perf_header__find_attr. We may need to do something more intelligent eventually (eg a hash lookup from counter id to attr) but this at least fixes the most common usage of perf report. Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Frederic Weisbecker Cc: Eric B Munson Acked-by: Eric B Munson LKML-Reference: <20100504111915.GB14636@kryten> Signed-off-by: Anton Blanchard -- Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1757b0f..2477270 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -713,6 +713,7 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) array++; } + data->id = -1ULL; if (type & PERF_SAMPLE_ID) { data->id = *array; array++; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2b9f898..8847bec 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -922,6 +922,14 @@ perf_header__find_attr(u64 id, struct perf_header *header) { int i; + /* + * We set id to -1 if the data file doesn't contain sample + * ids. Check for this and avoid walking through the entire + * list of ids which may be large. + */ + if (id == -1ULL) + return NULL; + for (i = 0; i < header->attrs; i++) { struct perf_header_attr *attr = header->attr[i]; int j; -- cgit v0.10.2 From 9890948d857c2120c234b0ca91a80416e8f747fb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 4 May 2010 20:58:51 -0300 Subject: perf report: Make dso__calc_col_width agree with hist_entry__dso_snprintf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first was always using the ->long_name, while the later used ->short_name if verbose was not set, resulting in the dso column to be much wider than needed most of the time. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2477270..23d5dfd 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -626,8 +626,10 @@ static void dso__calc_col_width(struct dso *self) if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - unsigned int slen = strlen(self->name); - if (slen > dsos__col_width) + u16 slen = self->short_name_len; + if (verbose) + slen = self->long_name_len; + if (dsos__col_width < slen) dsos__col_width = slen; } -- cgit v0.10.2 From db620b1c2fb172346dc54eb62bba9b4a117d173b Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Tue, 4 May 2010 22:20:16 -0500 Subject: perf/record: simplify TRACE_INFO tracepoint check Fix a couple of inefficiencies and redundancies related to have_tracepoints() and its use when checking whether to write TRACE_INFO. First, there's no need to use get_tracepoints_path() in have_tracepoints() - we really just want the part that checks whether any attributes correspondo to tracepoints. Second, we really don't care about raw_samples per se - tracepoints are always raw_samples. In any case, the have_tracepoints() check should be sufficient to decide whether or not to write TRACE_INFO. Cc: Frederic Weisbecker Cc: Ingo Molnar , Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Acked-by: Frederic Weisbecker LKML-Reference: <1273030770.6383.6.camel@tropicana> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0ff67d1..d3981ac 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -560,17 +560,8 @@ static int __cmd_record(int argc, const char **argv) return err; } - if (raw_samples && have_tracepoints(attrs, nr_counters)) { + if (have_tracepoints(attrs, nr_counters)) perf_header__set_feat(&session->header, HEADER_TRACE_INFO); - } else { - for (i = 0; i < nr_counters; i++) { - if (attrs[i].sample_type & PERF_SAMPLE_RAW && - attrs[i].type == PERF_TYPE_TRACEPOINT) { - perf_header__set_feat(&session->header, HEADER_TRACE_INFO); - break; - } - } - } atexit(atexit_header); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 0a1fb9d..b157260 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -489,7 +489,13 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events) { - return get_tracepoints_path(pattrs, nb_events) ? true : false; + int i; + + for (i = 0; i < nb_events; i++) + if (pattrs[i].type == PERF_TYPE_TRACEPOINT) + return true; + + return false; } int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) -- cgit v0.10.2 From 9e32a3cb0661a6a30e0fd2b77ce85293805e6337 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 5 May 2010 11:20:05 -0300 Subject: perf list: Add explanation about raw hardware event descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using explanation given by Ingo Molnar in the oprofile mailing list. Suggested-by: Nick Black Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Nick Black Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 8290b94..ad765e0 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -15,6 +15,24 @@ DESCRIPTION This command displays the symbolic event types which can be selected in the various perf commands with the -e option. +RAW HARDWARE EVENT DESCRIPTOR +----------------------------- +Even when an event is not available in a symbolic form within perf right now, +it can be encoded as , for instance, if the Intel docs +describe an event as: + + Event Umask Event Mask + Num. Value Mnemonic Description Comment + + A8H 01H LSD.UOPS Counts the number of micro-ops Use cmask=1 and + delivered by loop stream detector invert to count + cycles + +raw encoding of 0x1A8 can be used: + + perf stat -e r1a8 -a sleep 1 + perf record -e r1a8 ... + OPTIONS ------- None diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 600d327..bc8b7e6 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -936,7 +936,7 @@ void print_events(void) printf("\n"); printf(" %-42s [%s]\n", - "rNNN", event_type_descriptors[PERF_TYPE_RAW]); + "rNNN (NNN=)", event_type_descriptors[PERF_TYPE_RAW]); printf("\n"); printf(" %-42s [%s]\n", -- cgit v0.10.2 From 4778e0e8c64f683a71632dba1cff1f85f76f83c4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 5 May 2010 11:23:27 -0300 Subject: perf tools: Fixup minor doc formatting issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index c9dcade..5164a65 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -1,5 +1,5 @@ perf-annotate(1) -============== +================ NAME ---- diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index 0181ddd..a3dbadb 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -1,5 +1,5 @@ perf-bench(1) -============ +============= NAME ---- diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 8974e20..20d97d8 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -1,5 +1,5 @@ perf-diff(1) -============== +============ NAME ---- diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt index eac4d852e..a52fcde 100644 --- a/tools/perf/Documentation/perf-kmem.txt +++ b/tools/perf/Documentation/perf-kmem.txt @@ -1,5 +1,5 @@ perf-kmem(1) -============== +============ NAME ---- diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-trace-python.txt index 16a8650..864aac2 100644 --- a/tools/perf/Documentation/perf-trace-python.txt +++ b/tools/perf/Documentation/perf-trace-python.txt @@ -1,5 +1,5 @@ perf-trace-python(1) -================== +==================== NAME ---- diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 8879299..122ec9d 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -1,5 +1,5 @@ perf-trace(1) -============== +============= NAME ---- -- cgit v0.10.2 From d9f599e1e6d019968b35d2dc63074b9e8964fa69 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 20 Mar 2010 17:39:11 +0300 Subject: perf: Fix check at end of event search The original code doesn't work because "call" is never NULL there. Signed-off-by: Dan Carpenter LKML-Reference: <20100320143911.GF5331@bicker> Signed-off-by: Steven Rostedt diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 88c0b6d..58092d8 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1398,7 +1398,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, } err = -EINVAL; - if (!call) + if (&call->list == &ftrace_events) goto out_unlock; err = -EEXIST; -- cgit v0.10.2 From 4fd38e4595e2f6c9d27732c042a0e16b2753049c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 6 May 2010 17:31:38 +0200 Subject: perf: Fix exit() vs PERF_FORMAT_GROUP Both Stephane and Corey reported that PERF_FORMAT_GROUP didn't work as expected if the task the counters were attached to quit before the read() call. The cause is that we unconditionally destroy the grouping when we remove counters from their context. Fix this by only doing this when we free the counter itself. Reported-by: Corey Ashford Reported-by: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <1273160566.5605.404.camel@twins> Signed-off-by: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c8e3754..bf8f3c0 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -522,6 +522,7 @@ struct pmu { * enum perf_event_active_state - the states of a event */ enum perf_event_active_state { + PERF_EVENT_STATE_FREE = -3, PERF_EVENT_STATE_ERROR = -2, PERF_EVENT_STATE_OFF = -1, PERF_EVENT_STATE_INACTIVE = 0, diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 3d1552d..f13c3db 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -341,6 +341,9 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) if (event->state > PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_OFF; + if (event->state > PERF_EVENT_STATE_FREE) + return; + /* * If this was a group event with sibling events then * upgrade the siblings to singleton events by adding them @@ -1856,6 +1859,8 @@ int perf_event_release_kernel(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; + event->state = PERF_EVENT_STATE_FREE; + WARN_ON_ONCE(ctx->parent_ctx); mutex_lock(&ctx->mutex); perf_event_remove_from_context(event); -- cgit v0.10.2 From a0507c84bf47dfd204299774f45fd16da33f0619 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 6 May 2010 15:42:53 +0200 Subject: perf: Annotate perf_event_read_group() vs perf_event_release_kernel() Stephane reported a lockdep warning while using PERF_FORMAT_GROUP. The issue is that perf_event_read_group() takes faults while holding the ctx->mutex, while perf_event_release_kernel() can be called from munmap(). Which makes for an AB-BA deadlock. Except we can never establish the deadlock because we'll only ever call perf_event_release_kernel() after all file descriptors are dead so there is no concurrency possible. Reported-by: Stephane Eranian Cc: Paul Mackerras Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 49d8be5..34659d4 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -1867,7 +1867,19 @@ int perf_event_release_kernel(struct perf_event *event) event->state = PERF_EVENT_STATE_FREE; WARN_ON_ONCE(ctx->parent_ctx); - mutex_lock(&ctx->mutex); + /* + * There are two ways this annotation is useful: + * + * 1) there is a lock recursion from perf_event_exit_task + * see the comment there. + * + * 2) there is a lock-inversion with mmap_sem through + * perf_event_read_group(), which takes faults while + * holding ctx->mutex, however this is called after + * the last filedesc died, so there is no possibility + * to trigger the AB-BA case. + */ + mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); perf_event_remove_from_context(event); mutex_unlock(&ctx->mutex); @@ -5305,7 +5317,7 @@ void perf_event_exit_task(struct task_struct *child) * * But since its the parent context it won't be the same instance. */ - mutex_lock_nested(&child_ctx->mutex, SINGLE_DEPTH_NESTING); + mutex_lock(&child_ctx->mutex); again: list_for_each_entry_safe(child_event, tmp, &child_ctx->pinned_groups, -- cgit v0.10.2 From 4261e0e0efd9e04b6c69e0773c3cf4d6f337c416 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 13 Apr 2010 22:23:10 +0200 Subject: perf, x86: Move perfctr init code to x86_setup_perfctr() Split __hw_perf_event_init() to configure pmu events other than perfctrs. Perfctr code is moved to a separate function x86_setup_perfctr(). This and the following patches refactor the code. Split in multiple patches for better review. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1271190201-25705-2-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 7de7061..801441a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -426,6 +426,8 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) return 0; } +static int x86_setup_perfctr(struct perf_event *event); + static int x86_pmu_hw_config(struct perf_event *event) { /* @@ -453,9 +455,6 @@ static int x86_pmu_hw_config(struct perf_event *event) */ static int __hw_perf_event_init(struct perf_event *event) { - struct perf_event_attr *attr = &event->attr; - struct hw_perf_event *hwc = &event->hw; - u64 config; int err; if (!x86_pmu_initialized()) @@ -482,15 +481,24 @@ static int __hw_perf_event_init(struct perf_event *event) event->destroy = hw_perf_event_destroy; - hwc->idx = -1; - hwc->last_cpu = -1; - hwc->last_tag = ~0ULL; + event->hw.idx = -1; + event->hw.last_cpu = -1; + event->hw.last_tag = ~0ULL; /* Processor specifics */ err = x86_pmu.hw_config(event); if (err) return err; + return x86_setup_perfctr(event); +} + +static int x86_setup_perfctr(struct perf_event *event) +{ + struct perf_event_attr *attr = &event->attr; + struct hw_perf_event *hwc = &event->hw; + u64 config; + if (!hwc->sample_period) { hwc->sample_period = x86_pmu.max_period; hwc->last_period = hwc->sample_period; -- cgit v0.10.2 From c1726f343b3bfc2ee037e191907c632a31903021 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 13 Apr 2010 22:23:11 +0200 Subject: perf, x86: Move x86_setup_perfctr() Move x86_setup_perfctr(), no other changes made. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1271190201-25705-3-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 801441a..3d3bceb 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -426,7 +426,65 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) return 0; } -static int x86_setup_perfctr(struct perf_event *event); +static int x86_setup_perfctr(struct perf_event *event) +{ + struct perf_event_attr *attr = &event->attr; + struct hw_perf_event *hwc = &event->hw; + u64 config; + + if (!hwc->sample_period) { + hwc->sample_period = x86_pmu.max_period; + hwc->last_period = hwc->sample_period; + atomic64_set(&hwc->period_left, hwc->sample_period); + } else { + /* + * If we have a PMU initialized but no APIC + * interrupts, we cannot sample hardware + * events (user-space has to fall back and + * sample via a hrtimer based software event): + */ + if (!x86_pmu.apic) + return -EOPNOTSUPP; + } + + if (attr->type == PERF_TYPE_RAW) + return 0; + + if (attr->type == PERF_TYPE_HW_CACHE) + return set_ext_hw_attr(hwc, attr); + + if (attr->config >= x86_pmu.max_events) + return -EINVAL; + + /* + * The generic map: + */ + config = x86_pmu.event_map(attr->config); + + if (config == 0) + return -ENOENT; + + if (config == -1LL) + return -EINVAL; + + /* + * Branch tracing: + */ + if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && + (hwc->sample_period == 1)) { + /* BTS is not supported by this architecture. */ + if (!x86_pmu.bts) + return -EOPNOTSUPP; + + /* BTS is currently only allowed for user-mode. */ + if (!attr->exclude_kernel) + return -EOPNOTSUPP; + } + + hwc->config |= config; + + return 0; +} static int x86_pmu_hw_config(struct perf_event *event) { @@ -493,66 +551,6 @@ static int __hw_perf_event_init(struct perf_event *event) return x86_setup_perfctr(event); } -static int x86_setup_perfctr(struct perf_event *event) -{ - struct perf_event_attr *attr = &event->attr; - struct hw_perf_event *hwc = &event->hw; - u64 config; - - if (!hwc->sample_period) { - hwc->sample_period = x86_pmu.max_period; - hwc->last_period = hwc->sample_period; - atomic64_set(&hwc->period_left, hwc->sample_period); - } else { - /* - * If we have a PMU initialized but no APIC - * interrupts, we cannot sample hardware - * events (user-space has to fall back and - * sample via a hrtimer based software event): - */ - if (!x86_pmu.apic) - return -EOPNOTSUPP; - } - - if (attr->type == PERF_TYPE_RAW) - return 0; - - if (attr->type == PERF_TYPE_HW_CACHE) - return set_ext_hw_attr(hwc, attr); - - if (attr->config >= x86_pmu.max_events) - return -EINVAL; - - /* - * The generic map: - */ - config = x86_pmu.event_map(attr->config); - - if (config == 0) - return -ENOENT; - - if (config == -1LL) - return -EINVAL; - - /* - * Branch tracing: - */ - if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && - (hwc->sample_period == 1)) { - /* BTS is not supported by this architecture. */ - if (!x86_pmu.bts) - return -EOPNOTSUPP; - - /* BTS is currently only allowed for user-mode. */ - if (!attr->exclude_kernel) - return -EOPNOTSUPP; - } - - hwc->config |= config; - - return 0; -} - static void x86_pmu_disable_all(void) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); -- cgit v0.10.2 From 9d0fcba67e47ff398a6fa86476d4884d472dc98a Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 13 Apr 2010 22:23:12 +0200 Subject: perf, x86: Call x86_setup_perfctr() from .hw_config() The perfctr setup calls are in the corresponding .hw_config() functions now. This makes it possible to introduce config functions for other pmu events that are not perfctr specific. Also, all of a sudden the code looks much nicer. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1271190201-25705-4-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 3d3bceb..c2c1e10 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -505,7 +505,7 @@ static int x86_pmu_hw_config(struct perf_event *event) if (event->attr.type == PERF_TYPE_RAW) event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK; - return 0; + return x86_setup_perfctr(event); } /* @@ -543,12 +543,7 @@ static int __hw_perf_event_init(struct perf_event *event) event->hw.last_cpu = -1; event->hw.last_tag = ~0ULL; - /* Processor specifics */ - err = x86_pmu.hw_config(event); - if (err) - return err; - - return x86_setup_perfctr(event); + return x86_pmu.hw_config(event); } static void x86_pmu_disable_all(void) diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 15367cc..9e00205 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -455,7 +455,7 @@ static int p4_hw_config(struct perf_event *event) (p4_config_pack_escr(P4_ESCR_MASK_HT) | p4_config_pack_cccr(P4_CCCR_MASK_HT)); - return 0; + return x86_setup_perfctr(event); } static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc) -- cgit v0.10.2 From 31fa58af57c41d2912debf62d47d5811062411f1 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 13 Apr 2010 22:23:14 +0200 Subject: perf, x86: Pass enable bit mask to __x86_pmu_enable_event() To reuse this function for events with different enable bit masks, this mask is part of the function's argument list now. The function will be used later to control ibs events too. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1271190201-25705-6-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index c2c1e10..4e218d7 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -844,10 +844,10 @@ void hw_perf_enable(void) x86_pmu.enable_all(added); } -static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc) +static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, + u64 enable_mask) { - wrmsrl(hwc->config_base + hwc->idx, - hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE); + wrmsrl(hwc->config_base + hwc->idx, hwc->config | enable_mask); } static inline void x86_pmu_disable_event(struct perf_event *event) @@ -919,7 +919,8 @@ static void x86_pmu_enable_event(struct perf_event *event) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); if (cpuc->enabled) - __x86_pmu_enable_event(&event->hw); + __x86_pmu_enable_event(&event->hw, + ARCH_PERFMON_EVENTSEL_ENABLE); } /* diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index a099df9..a4b56ac 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -513,7 +513,8 @@ static void intel_pmu_nhm_enable_all(int added) if (!event) continue; - __x86_pmu_enable_event(&event->hw); + __x86_pmu_enable_event(&event->hw, + ARCH_PERFMON_EVENTSEL_ENABLE); } } intel_pmu_enable_all(added); @@ -617,7 +618,7 @@ static void intel_pmu_enable_event(struct perf_event *event) if (unlikely(event->attr.precise)) intel_pmu_pebs_enable(event); - __x86_pmu_enable_event(hwc); + __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); } /* -- cgit v0.10.2 From a1f2b70a942b8d858a0ab02567da3999b60a99b2 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 13 Apr 2010 22:23:15 +0200 Subject: perf, x86: Use weight instead of cmask in for_each_event_constraint() There may exist constraints with a cmask set to zero. In this case for_each_event_constraint() will not work properly. Now weight is used instead of the cmask for loop exit detection. Weight is always a value other than zero since the default contains the HWEIGHT from the counter mask and in other cases a value of zero does not fit too. This is in preparation of ibs event constraints that wont have a cmask. Signed-off-by: Robert Richter Signed-off-by: Peter Zijlstra LKML-Reference: <1271190201-25705-7-git-send-email-robert.richter@amd.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 4e218d7..4a3f1f2 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -170,7 +170,7 @@ struct cpu_hw_events { EVENT_CONSTRAINT(0, 0, 0) #define for_each_event_constraint(e, c) \ - for ((e) = (c); (e)->cmask; (e)++) + for ((e) = (c); (e)->weight; (e)++) union perf_capabilities { struct { -- cgit v0.10.2 From 1e9a6d8d44cb6dcd2799b36ceb23007e6a423bfe Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 4 May 2010 16:30:21 +0200 Subject: perf, x86: Remove PEBS SAMPLE_RAW support Its broken, we really should get PERF_SAMPLE_REGS sorted. Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index ec8b2e1..080b9b0 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -459,7 +459,6 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) struct perf_event *event = cpuc->events[0]; /* PMC0 only */ struct pebs_record_core *at, *top; struct perf_sample_data data; - struct perf_raw_record raw; struct pt_regs regs; int n; @@ -499,12 +498,6 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; - if (event->attr.sample_type & PERF_SAMPLE_RAW) { - raw.size = x86_pmu.pebs_record_size; - raw.data = at; - data.raw = &raw; - } - /* * We use the interrupt regs as a base because the PEBS record * does not contain a full regs set, specifically it seems to @@ -536,7 +529,6 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) struct pebs_record_nhm *at, *top; struct perf_sample_data data; struct perf_event *event = NULL; - struct perf_raw_record raw; struct pt_regs regs; u64 status = 0; int bit, n; @@ -585,12 +577,6 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; - if (event->attr.sample_type & PERF_SAMPLE_RAW) { - raw.size = x86_pmu.pebs_record_size; - raw.data = at; - data.raw = &raw; - } - /* * See the comment in intel_pmu_drain_pebs_core() */ -- cgit v0.10.2 From 2b0b5c6fe9b383f3cf35a0a6371c9d577bd523ff Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 8 Apr 2010 23:03:20 +0200 Subject: perf, x86: Consolidate some code repetition Remove some duplicated logic. Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 080b9b0..35056f7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -452,14 +452,54 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) static int intel_pmu_save_and_restart(struct perf_event *event); +static void __intel_pmu_pebs_event(struct perf_event *event, + struct pt_regs *iregs, void *__pebs) +{ + /* + * We cast to pebs_record_core since that is a subset of + * both formats and we don't use the other fields in this + * routine. + */ + struct pebs_record_core *pebs = __pebs; + struct perf_sample_data data; + struct pt_regs regs; + + if (!intel_pmu_save_and_restart(event)) + return; + + perf_sample_data_init(&data, 0); + data.period = event->hw.last_period; + + /* + * We use the interrupt regs as a base because the PEBS record + * does not contain a full regs set, specifically it seems to + * lack segment descriptors, which get used by things like + * user_mode(). + * + * In the simple case fix up only the IP and BP,SP regs, for + * PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly. + * A possible PERF_SAMPLE_REGS will have to transfer all regs. + */ + regs = *iregs; + regs.ip = pebs->ip; + regs.bp = pebs->bp; + regs.sp = pebs->sp; + + if (intel_pmu_pebs_fixup_ip(regs)) + regs.flags |= PERF_EFLAGS_EXACT; + else + regs.flags &= ~PERF_EFLAGS_EXACT; + + if (perf_event_overflow(event, 1, &data, ®s)) + x86_pmu_stop(event); +} + static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct debug_store *ds = cpuc->ds; struct perf_event *event = cpuc->events[0]; /* PMC0 only */ struct pebs_record_core *at, *top; - struct perf_sample_data data; - struct pt_regs regs; int n; if (!ds || !x86_pmu.pebs) @@ -485,9 +525,6 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) if (n <= 0) return; - if (!intel_pmu_save_and_restart(event)) - return; - /* * Should not happen, we program the threshold at 1 and do not * set a reset value. @@ -495,31 +532,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) WARN_ON_ONCE(n > 1); at += n - 1; - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - - /* - * We use the interrupt regs as a base because the PEBS record - * does not contain a full regs set, specifically it seems to - * lack segment descriptors, which get used by things like - * user_mode(). - * - * In the simple case fix up only the IP and BP,SP regs, for - * PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly. - * A possible PERF_SAMPLE_REGS will have to transfer all regs. - */ - regs = *iregs; - regs.ip = at->ip; - regs.bp = at->bp; - regs.sp = at->sp; - - if (intel_pmu_pebs_fixup_ip(®s)) - regs.flags |= PERF_EFLAGS_EXACT; - else - regs.flags &= ~PERF_EFLAGS_EXACT; - - if (perf_event_overflow(event, 1, &data, ®s)) - x86_pmu_stop(event); + __intel_pmu_pebs_event(event, iregs, at); } static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) @@ -527,9 +540,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); struct debug_store *ds = cpuc->ds; struct pebs_record_nhm *at, *top; - struct perf_sample_data data; struct perf_event *event = NULL; - struct pt_regs regs; u64 status = 0; int bit, n; @@ -571,27 +582,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) if (!event || bit >= MAX_PEBS_EVENTS) continue; - if (!intel_pmu_save_and_restart(event)) - continue; - - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - - /* - * See the comment in intel_pmu_drain_pebs_core() - */ - regs = *iregs; - regs.ip = at->ip; - regs.bp = at->bp; - regs.sp = at->sp; - - if (intel_pmu_pebs_fixup_ip(®s)) - regs.flags |= PERF_EFLAGS_EXACT; - else - regs.flags &= ~PERF_EFLAGS_EXACT; - - if (perf_event_overflow(event, 1, &data, ®s)) - x86_pmu_stop(event); + __intel_pmu_pebs_event(event, iregs, at); } } -- cgit v0.10.2 From ab608344bcbde4f55ec4cd911b686b0ce3eae076 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 8 Apr 2010 23:03:20 +0200 Subject: perf, x86: Improve the PEBS ABI Rename perf_event_attr::precise to perf_event_attr::precise_ip and widen it to 2 bits. This new field describes the required precision of the PERF_SAMPLE_IP field: 0 - SAMPLE_IP can have arbitrary skid 1 - SAMPLE_IP must have constant skid 2 - SAMPLE_IP requested to have 0 skid 3 - SAMPLE_IP must have 0 skid And modify the Intel PEBS code accordingly. The PEBS implementation now supports up to precise_ip == 2, where we perform the IP fixup. Also s/PERF_RECORD_MISC_EXACT/&_IP/ to clarify its meaning, this bit should be set for each PERF_SAMPLE_IP field known to match the actual instruction triggering the event. This new scheme allows for a PEBS mode that uses the buffer for more than a single event. Signed-off-by: Peter Zijlstra Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 4a3f1f2..27fa9ee 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -488,6 +488,21 @@ static int x86_setup_perfctr(struct perf_event *event) static int x86_pmu_hw_config(struct perf_event *event) { + if (event->attr.precise_ip) { + int precise = 0; + + /* Support for constant skid */ + if (x86_pmu.pebs) + precise++; + + /* Support for IP fixup */ + if (x86_pmu.lbr_nr) + precise++; + + if (event->attr.precise_ip > precise) + return -EOPNOTSUPP; + } + /* * Generate PMC IRQs: * (keep 'enabled' bit clear for now) @@ -1780,7 +1795,7 @@ unsigned long perf_misc_flags(struct pt_regs *regs) } if (regs->flags & PERF_EFLAGS_EXACT) - misc |= PERF_RECORD_MISC_EXACT; + misc |= PERF_RECORD_MISC_EXACT_IP; return misc; } diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index a4b56ac..fdbc652 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -563,7 +563,7 @@ static void intel_pmu_disable_event(struct perf_event *event) x86_pmu_disable_event(event); - if (unlikely(event->attr.precise)) + if (unlikely(event->attr.precise_ip)) intel_pmu_pebs_disable(event); } @@ -615,7 +615,7 @@ static void intel_pmu_enable_event(struct perf_event *event) return; } - if (unlikely(event->attr.precise)) + if (unlikely(event->attr.precise_ip)) intel_pmu_pebs_enable(event); __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 35056f7..18018d1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -307,7 +307,7 @@ intel_pebs_constraints(struct perf_event *event) { struct event_constraint *c; - if (!event->attr.precise) + if (!event->attr.precise_ip) return NULL; if (x86_pmu.pebs_constraints) { @@ -330,7 +330,7 @@ static void intel_pmu_pebs_enable(struct perf_event *event) cpuc->pebs_enabled |= 1ULL << hwc->idx; WARN_ON_ONCE(cpuc->enabled); - if (x86_pmu.intel_cap.pebs_trap) + if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1) intel_pmu_lbr_enable(event); } @@ -345,7 +345,7 @@ static void intel_pmu_pebs_disable(struct perf_event *event) hwc->config |= ARCH_PERFMON_EVENTSEL_INT; - if (x86_pmu.intel_cap.pebs_trap) + if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1) intel_pmu_lbr_disable(event); } @@ -485,7 +485,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, regs.bp = pebs->bp; regs.sp = pebs->sp; - if (intel_pmu_pebs_fixup_ip(regs)) + if (event->attr.precise_ip > 1 && intel_pmu_pebs_fixup_ip(®s)) regs.flags |= PERF_EFLAGS_EXACT; else regs.flags &= ~PERF_EFLAGS_EXACT; @@ -518,7 +518,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) WARN_ON_ONCE(!event); - if (!event->attr.precise) + if (!event->attr.precise_ip) return; n = top - at; @@ -570,7 +570,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) WARN_ON_ONCE(!event); - if (!event->attr.precise) + if (!event->attr.precise_ip) continue; if (__test_and_set_bit(bit, (unsigned long *)&status)) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 6be4a0f..23cd005 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -203,9 +203,19 @@ struct perf_event_attr { enable_on_exec : 1, /* next exec enables */ task : 1, /* trace fork/exit */ watermark : 1, /* wakeup_watermark */ - precise : 1, /* OoO invariant counter */ - - __reserved_1 : 48; + /* + * precise_ip: + * + * 0 - SAMPLE_IP can have arbitrary skid + * 1 - SAMPLE_IP must have constant skid + * 2 - SAMPLE_IP requested to have 0 skid + * 3 - SAMPLE_IP must have 0 skid + * + * See also PERF_RECORD_MISC_EXACT_IP + */ + precise_ip : 2, /* skid constraint */ + + __reserved_1 : 47; union { __u32 wakeup_events; /* wakeup every n events */ @@ -296,7 +306,12 @@ struct perf_event_mmap_page { #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) #define PERF_RECORD_MISC_GUEST_USER (5 << 0) -#define PERF_RECORD_MISC_EXACT (1 << 14) +/* + * Indicates that the content of PERF_SAMPLE_IP points to + * the actual instruction that triggered the event. See also + * perf_event_attr::precise_ip. + */ +#define PERF_RECORD_MISC_EXACT_IP (1 << 14) /* * Reserve the last bit to indicate some extended misc field */ diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 3de3977..ed9b5b6 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1021,7 +1021,7 @@ static void event__process_sample(const event_t *self, return; } - if (self->header.misc & PERF_RECORD_MISC_EXACT) + if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) exact_samples++; if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index bc8b7e6..ae7f591 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -654,10 +654,6 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { - if (str[n+1] == 'p') { - attr->precise = 1; - n++; - } *strp = str + n + 1; attr->type = PERF_TYPE_RAW; attr->config = config; @@ -692,19 +688,29 @@ static enum event_result parse_event_modifier(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; - int eu = 1, ek = 1, eh = 1; + int exclude = 0; + int eu = 0, ek = 0, eh = 0, precise = 0; if (*str++ != ':') return 0; while (*str) { - if (*str == 'u') + if (*str == 'u') { + if (!exclude) + exclude = eu = ek = eh = 1; eu = 0; - else if (*str == 'k') + } else if (*str == 'k') { + if (!exclude) + exclude = eu = ek = eh = 1; ek = 0; - else if (*str == 'h') + } else if (*str == 'h') { + if (!exclude) + exclude = eu = ek = eh = 1; eh = 0; - else + } else if (*str == 'p') { + precise++; + } else break; + ++str; } if (str >= *strp + 2) { @@ -712,6 +718,7 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) attr->exclude_user = eu; attr->exclude_kernel = ek; attr->exclude_hv = eh; + attr->precise_ip = precise; return 1; } return 0; -- cgit v0.10.2 From 6bde9b6ce0127e2a56228a2071536d422be31336 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 23 Apr 2010 13:56:00 +0800 Subject: perf: Add group scheduling transactional APIs Add group scheduling transactional APIs to struct pmu. These APIs will be implemented in arch code, based on Peter's idea as below. > the idea behind hw_perf_group_sched_in() is to not perform > schedulability tests on each event in the group, but to add the group > as a whole and then perform one test. > > Of course, when that test fails, you'll have to roll-back the whole > group again. > > So start_txn (or a better name) would simply toggle a flag in the pmu > implementation that will make pmu::enable() not perform the > schedulablilty test. > > Then commit_txn() will perform the schedulability test (so note the > method has to have a !void return value. > > This will allow us to use the regular > kernel/perf_event.c::group_sched_in() and all the rollback code. > Currently each hw_perf_group_sched_in() implementation duplicates all > the rolllback code (with various bugs). ->start_txn: Start group events scheduling transaction, set a flag to make pmu::enable() not perform the schedulability test, it will be performed at commit time. ->commit_txn: Commit group events scheduling transaction, perform the group schedulability as a whole ->cancel_txn: Stop group events scheduling transaction, clear the flag so pmu::enable() will perform the schedulability test. Reviewed-by: Stephane Eranian Reviewed-by: Frederic Weisbecker Signed-off-by: Lin Ming Cc: David Miller Cc: Paul Mackerras Signed-off-by: Peter Zijlstra LKML-Reference: <1272002160.5707.60.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 23cd005..4924c96 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -547,6 +547,8 @@ struct hw_perf_event { struct perf_event; +#define PERF_EVENT_TXN_STARTED 1 + /** * struct pmu - generic performance monitoring unit */ @@ -557,6 +559,16 @@ struct pmu { void (*stop) (struct perf_event *event); void (*read) (struct perf_event *event); void (*unthrottle) (struct perf_event *event); + + /* + * group events scheduling is treated as a transaction, + * add group events as a whole and perform one schedulability test. + * If test fails, roll back the whole group + */ + + void (*start_txn) (const struct pmu *pmu); + void (*cancel_txn) (const struct pmu *pmu); + int (*commit_txn) (const struct pmu *pmu); }; /** @@ -823,9 +835,6 @@ extern void perf_disable(void); extern void perf_enable(void); extern int perf_event_task_disable(void); extern int perf_event_task_enable(void); -extern int hw_perf_group_sched_in(struct perf_event *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx); extern void perf_event_update_userpage(struct perf_event *event); extern int perf_event_release_kernel(struct perf_event *event); extern struct perf_event * diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 34659d4..bb06382 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -83,14 +83,6 @@ extern __weak const struct pmu *hw_perf_event_init(struct perf_event *event) void __weak hw_perf_disable(void) { barrier(); } void __weak hw_perf_enable(void) { barrier(); } -int __weak -hw_perf_group_sched_in(struct perf_event *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx) -{ - return 0; -} - void __weak perf_event_print_debug(void) { } static DEFINE_PER_CPU(int, perf_disable_count); @@ -644,15 +636,20 @@ group_sched_in(struct perf_event *group_event, struct perf_cpu_context *cpuctx, struct perf_event_context *ctx) { - struct perf_event *event, *partial_group; + struct perf_event *event, *partial_group = NULL; + const struct pmu *pmu = group_event->pmu; + bool txn = false; int ret; if (group_event->state == PERF_EVENT_STATE_OFF) return 0; - ret = hw_perf_group_sched_in(group_event, cpuctx, ctx); - if (ret) - return ret < 0 ? ret : 0; + /* Check if group transaction availabe */ + if (pmu->start_txn) + txn = true; + + if (txn) + pmu->start_txn(pmu); if (event_sched_in(group_event, cpuctx, ctx)) return -EAGAIN; @@ -667,9 +664,19 @@ group_sched_in(struct perf_event *group_event, } } - return 0; + if (txn) { + ret = pmu->commit_txn(pmu); + if (!ret) { + pmu->cancel_txn(pmu); + + return 0; + } + } group_error: + if (txn) + pmu->cancel_txn(pmu); + /* * Groups can be scheduled in as one unit only, so undo any * partial group before returning: -- cgit v0.10.2 From 4d1c52b02d977d884abb21d0bbaba6b5d6bc8374 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 23 Apr 2010 13:56:12 +0800 Subject: perf, x86: implement group scheduling transactional APIs Convert to the transactional PMU API and remove the duplication of group_sched_in(). Reviewed-by: Stephane Eranian Signed-off-by: Lin Ming Signed-off-by: Peter Zijlstra Cc: David Miller Cc: Paul Mackerras LKML-Reference: <1272002172.5707.61.camel@minggr.sh.intel.com> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 27fa9ee..fd4db0d 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -110,6 +110,8 @@ struct cpu_hw_events { u64 tags[X86_PMC_IDX_MAX]; struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ + unsigned int group_flag; + /* * Intel DebugStore bits */ @@ -961,6 +963,14 @@ static int x86_pmu_enable(struct perf_event *event) if (n < 0) return n; + /* + * If group events scheduling transaction was started, + * skip the schedulability test here, it will be peformed + * at commit time(->commit_txn) as a whole + */ + if (cpuc->group_flag & PERF_EVENT_TXN_STARTED) + goto out; + ret = x86_pmu.schedule_events(cpuc, n, assign); if (ret) return ret; @@ -970,6 +980,7 @@ static int x86_pmu_enable(struct perf_event *event) */ memcpy(cpuc->assign, assign, n*sizeof(int)); +out: cpuc->n_events = n; cpuc->n_added += n - n0; @@ -1227,119 +1238,6 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) return &unconstrained; } -static int x86_event_sched_in(struct perf_event *event, - struct perf_cpu_context *cpuctx) -{ - int ret = 0; - - event->state = PERF_EVENT_STATE_ACTIVE; - event->oncpu = smp_processor_id(); - event->tstamp_running += event->ctx->time - event->tstamp_stopped; - - if (!is_x86_event(event)) - ret = event->pmu->enable(event); - - if (!ret && !is_software_event(event)) - cpuctx->active_oncpu++; - - if (!ret && event->attr.exclusive) - cpuctx->exclusive = 1; - - return ret; -} - -static void x86_event_sched_out(struct perf_event *event, - struct perf_cpu_context *cpuctx) -{ - event->state = PERF_EVENT_STATE_INACTIVE; - event->oncpu = -1; - - if (!is_x86_event(event)) - event->pmu->disable(event); - - event->tstamp_running -= event->ctx->time - event->tstamp_stopped; - - if (!is_software_event(event)) - cpuctx->active_oncpu--; - - if (event->attr.exclusive || !cpuctx->active_oncpu) - cpuctx->exclusive = 0; -} - -/* - * Called to enable a whole group of events. - * Returns 1 if the group was enabled, or -EAGAIN if it could not be. - * Assumes the caller has disabled interrupts and has - * frozen the PMU with hw_perf_save_disable. - * - * called with PMU disabled. If successful and return value 1, - * then guaranteed to call perf_enable() and hw_perf_enable() - */ -int hw_perf_group_sched_in(struct perf_event *leader, - struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx) -{ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - struct perf_event *sub; - int assign[X86_PMC_IDX_MAX]; - int n0, n1, ret; - - if (!x86_pmu_initialized()) - return 0; - - /* n0 = total number of events */ - n0 = collect_events(cpuc, leader, true); - if (n0 < 0) - return n0; - - ret = x86_pmu.schedule_events(cpuc, n0, assign); - if (ret) - return ret; - - ret = x86_event_sched_in(leader, cpuctx); - if (ret) - return ret; - - n1 = 1; - list_for_each_entry(sub, &leader->sibling_list, group_entry) { - if (sub->state > PERF_EVENT_STATE_OFF) { - ret = x86_event_sched_in(sub, cpuctx); - if (ret) - goto undo; - ++n1; - } - } - /* - * copy new assignment, now we know it is possible - * will be used by hw_perf_enable() - */ - memcpy(cpuc->assign, assign, n0*sizeof(int)); - - cpuc->n_events = n0; - cpuc->n_added += n1; - ctx->nr_active += n1; - - /* - * 1 means successful and events are active - * This is not quite true because we defer - * actual activation until hw_perf_enable() but - * this way we* ensure caller won't try to enable - * individual events - */ - return 1; -undo: - x86_event_sched_out(leader, cpuctx); - n0 = 1; - list_for_each_entry(sub, &leader->sibling_list, group_entry) { - if (sub->state == PERF_EVENT_STATE_ACTIVE) { - x86_event_sched_out(sub, cpuctx); - if (++n0 == n1) - break; - } - } - return ret; -} - #include "perf_event_amd.c" #include "perf_event_p6.c" #include "perf_event_p4.c" @@ -1471,6 +1369,59 @@ static inline void x86_pmu_read(struct perf_event *event) x86_perf_event_update(event); } +/* + * Start group events scheduling transaction + * Set the flag to make pmu::enable() not perform the + * schedulability test, it will be performed at commit time + */ +static void x86_pmu_start_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + cpuc->group_flag |= PERF_EVENT_TXN_STARTED; +} + +/* + * Stop group events scheduling transaction + * Clear the flag and pmu::enable() will perform the + * schedulability test. + */ +static void x86_pmu_cancel_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + cpuc->group_flag &= ~PERF_EVENT_TXN_STARTED; +} + +/* + * Commit group events scheduling transaction + * Perform the group schedulability test as a whole + * Return 0 if success + */ +static int x86_pmu_commit_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int assign[X86_PMC_IDX_MAX]; + int n, ret; + + n = cpuc->n_events; + + if (!x86_pmu_initialized()) + return -EAGAIN; + + ret = x86_pmu.schedule_events(cpuc, n, assign); + if (ret) + return ret; + + /* + * copy new assignment, now we know it is possible + * will be used by hw_perf_enable() + */ + memcpy(cpuc->assign, assign, n*sizeof(int)); + + return 0; +} + static const struct pmu pmu = { .enable = x86_pmu_enable, .disable = x86_pmu_disable, @@ -1478,6 +1429,9 @@ static const struct pmu pmu = { .stop = x86_pmu_stop, .read = x86_pmu_read, .unthrottle = x86_pmu_unthrottle, + .start_txn = x86_pmu_start_txn, + .cancel_txn = x86_pmu_cancel_txn, + .commit_txn = x86_pmu_commit_txn, }; /* -- cgit v0.10.2 From 1cf4a0632c24ea61162ed819bde358bc94c55510 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 7 May 2010 14:07:05 -0300 Subject: perf list: Improve the raw hw event descriptor documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was x86 specific and imcomplete at that, improve the situation by making it clear where the example provided applies and by adding the URLs for the Intel and AMD manuals where this is discussed in depth. Acked-by: Robert Richter Cc: Cyrill Gorcunov Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi Cc: Robert Richter Reported-by: Robert Richter Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index ad765e0..43e3dd2 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -18,8 +18,16 @@ various perf commands with the -e option. RAW HARDWARE EVENT DESCRIPTOR ----------------------------- Even when an event is not available in a symbolic form within perf right now, -it can be encoded as , for instance, if the Intel docs -describe an event as: +it can be encoded in a per processor specific way. + +For instance For x86 CPUs NNN represents the raw register encoding with the +layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide] Figure 30-1 Layout +of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344, +Figure 13-7 Performance Event-Select Register (PerfEvtSeln)). + +Example: + +If the Intel docs for a QM720 Core i7 describe an event as: Event Umask Event Mask Num. Value Mnemonic Description Comment @@ -33,6 +41,9 @@ raw encoding of 0x1A8 can be used: perf stat -e r1a8 -a sleep 1 perf record -e r1a8 ... +You should refer to the processor specific documentation for getting these +details. Some of them are referenced in the SEE ALSO section below. + OPTIONS ------- None @@ -40,4 +51,6 @@ None SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-top[1], -linkperf:perf-record[1] +linkperf:perf-record[1], +http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide], +http://support.amd.com/us/Processor_TechDocs/24593.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming] diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index bc8b7e6..7b24b5d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -936,7 +936,8 @@ void print_events(void) printf("\n"); printf(" %-42s [%s]\n", - "rNNN (NNN=)", event_type_descriptors[PERF_TYPE_RAW]); + "rNNN (see 'perf list --help' on how to encode it)", + event_type_descriptors[PERF_TYPE_RAW]); printf("\n"); printf(" %-42s [%s]\n", -- cgit v0.10.2 From 6e85158cf5a2385264316870256fb6ad681156a0 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 8 May 2010 20:58:00 +1000 Subject: perf_event: Make software events work again Commit 6bde9b6ce0127e2a56228a2071536d422be31336 ("perf: Add group scheduling transactional APIs") added code to allow a group to be scheduled in a single transaction. However, it introduced a bug in handling events whose pmu does not implement transactions -- at the end of scheduling in the events in the group, in the non-transactional case the code now falls through to the group_error label, and proceeds to unschedule all the events in the group and return failure. This fixes it by returning 0 (success) in the non-transactional case. Signed-off-by: Paul Mackerras Cc: Peter Zijlstra Cc: Lin Ming Cc: Frederic Weisbecker Cc: eranian@gmail.com LKML-Reference: <20100508105800.GB10650@brick.ozlabs.ibm.com> Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index bb06382..180151f 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -664,13 +664,13 @@ group_sched_in(struct perf_event *group_event, } } - if (txn) { - ret = pmu->commit_txn(pmu); - if (!ret) { - pmu->cancel_txn(pmu); + if (!txn) + return 0; - return 0; - } + ret = pmu->commit_txn(pmu); + if (!ret) { + pmu->cancel_txn(pmu); + return 0; } group_error: -- cgit v0.10.2 From de902d967feb96f2dfddfbe9dbd69dc22fd5ebcb Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 8 May 2010 15:39:52 +0400 Subject: x86, perf: P4 PMU -- configure predefined events If an event is not RAW we should not exit p4_hw_config early but call x86_setup_perfctr as well. Signed-off-by: Cyrill Gorcunov Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Lin Ming Cc: Robert Richter Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 9e00205..b1f532d 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -439,21 +439,20 @@ static int p4_hw_config(struct perf_event *event) if (p4_ht_active() && p4_ht_thread(cpu)) event->hw.config = p4_set_ht_bit(event->hw.config); - if (event->attr.type != PERF_TYPE_RAW) - return 0; - - /* - * We don't control raw events so it's up to the caller - * to pass sane values (and we don't count the thread number - * on HT machine but allow HT-compatible specifics to be - * passed on) - * - * XXX: HT wide things should check perf_paranoid_cpu() && - * CAP_SYS_ADMIN - */ - event->hw.config |= event->attr.config & - (p4_config_pack_escr(P4_ESCR_MASK_HT) | - p4_config_pack_cccr(P4_CCCR_MASK_HT)); + if (event->attr.type == PERF_TYPE_RAW) { + /* + * We don't control raw events so it's up to the caller + * to pass sane values (and we don't count the thread number + * on HT machine but allow HT-compatible specifics to be + * passed on) + * + * XXX: HT wide things should check perf_paranoid_cpu() && + * CAP_SYS_ADMIN + */ + event->hw.config |= event->attr.config & + (p4_config_pack_escr(P4_ESCR_MASK_HT) | + p4_config_pack_cccr(P4_CCCR_MASK_HT)); + } return x86_setup_perfctr(event); } -- cgit v0.10.2 From 137351e0feeb9f25d99488ee1afc1c79f5499a9a Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 8 May 2010 15:25:52 +0400 Subject: x86, perf: P4 PMU -- protect sensible procedures from preemption Steven reported: | | I'm getting: | | Pid: 3477, comm: perf Not tainted 2.6.34-rc6 #2727 | Call Trace: | [] debug_smp_processor_id+0xd5/0xf0 | [] p4_hw_config+0x2b/0x15c | [] ? trace_hardirqs_on_caller+0x12b/0x14f | [] hw_perf_event_init+0x468/0x7be | [] ? debug_mutex_init+0x31/0x3c | [] T.850+0x273/0x42e | [] sys_perf_event_open+0x23e/0x3f1 | [] ? sysret_check+0x2e/0x69 | [] system_call_fastpath+0x16/0x1b | | When running perf record in latest tip/perf/core | Due to the fact that p4 counters are shared between HT threads we synthetically divide the whole set of counters into two non-intersected subsets. And while we're "borrowing" counters from these subsets we should not be preempted (well, strictly speaking in p4_hw_config we just pre-set reference to the subset which allow to save some cycles in schedule routine if it happens on the same cpu). So use get_cpu/put_cpu pair. Also p4_pmu_schedule_events should use smp_processor_id rather than raw_ version. This allow us to catch up preemption issue (if there will ever be). Reported-by: Steven Rostedt Tested-by: Steven Rostedt Signed-off-by: Cyrill Gorcunov Cc: Steven Rostedt Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Lin Ming LKML-Reference: <20100508112716.963478928@openvz.org> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index b1f532d..ca40180 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -421,7 +421,8 @@ static u64 p4_pmu_event_map(int hw_event) static int p4_hw_config(struct perf_event *event) { - int cpu = raw_smp_processor_id(); + int cpu = get_cpu(); + int rc = 0; u32 escr, cccr; /* @@ -454,7 +455,10 @@ static int p4_hw_config(struct perf_event *event) p4_config_pack_cccr(P4_CCCR_MASK_HT)); } - return x86_setup_perfctr(event); + rc = x86_setup_perfctr(event); + put_cpu(); + + return rc; } static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc) -- cgit v0.10.2 From 3f51b7119d052827dbb0e40c966acdf2bdc6f47f Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 8 May 2010 15:25:53 +0400 Subject: x86, perf: P4 PMU -- Get rid of redundant check for array index The caller already has done such a check. And it was wrong anyway, it had to be '>=' rather than '>' Signed-off-by: Cyrill Gorcunov Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Lin Ming LKML-Reference: <20100508112717.130386882@openvz.org> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index ca40180..b8c2d37 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -406,11 +406,6 @@ static u64 p4_pmu_event_map(int hw_event) unsigned int esel; u64 config; - if (hw_event > ARRAY_SIZE(p4_general_events)) { - printk_once(KERN_ERR "P4 PMU: Bad index: %i\n", hw_event); - return 0; - } - config = p4_general_events[hw_event]; bind = p4_config_get_bind(config); esel = P4_OPCODE_ESEL(bind->opcode); -- cgit v0.10.2 From c7993165ef0c1d636ca05f4787739f8414584e6d Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Sat, 8 May 2010 15:25:54 +0400 Subject: x86, perf: P4 PMU -- check for proper event index in RAW events RAW events are special and we should be ready for user passing in insane event index values. Signed-off-by: Cyrill Gorcunov Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Lin Ming LKML-Reference: <20100508112717.315897547@openvz.org> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index b8c2d37..a603930 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -418,6 +418,7 @@ static int p4_hw_config(struct perf_event *event) { int cpu = get_cpu(); int rc = 0; + unsigned int evnt; u32 escr, cccr; /* @@ -436,6 +437,14 @@ static int p4_hw_config(struct perf_event *event) event->hw.config = p4_set_ht_bit(event->hw.config); if (event->attr.type == PERF_TYPE_RAW) { + + /* user data may have out-of-bound event index */ + evnt = p4_config_unpack_event(event->attr.config); + if (evnt >= ARRAY_SIZE(p4_event_bind_map)) { + rc = -EINVAL; + goto out; + } + /* * We don't control raw events so it's up to the caller * to pass sane values (and we don't count the thread number @@ -451,8 +460,8 @@ static int p4_hw_config(struct perf_event *event) } rc = x86_setup_perfctr(event); +out: put_cpu(); - return rc; } -- cgit v0.10.2 From c0614829c16ab9d31f1b7d40516decfbf3d32102 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 27 Apr 2010 18:33:12 -0400 Subject: kprobes: Move enable/disable_kprobe() out from debugfs code Move enable/disable_kprobe() API out from debugfs related code, because these interfaces are not related to debugfs interface. This fixes a compiler warning. Signed-off-by: Masami Hiramatsu Acked-by: Ananth N Mavinakayanahalli Acked-by: Tony Luck Cc: systemtap Cc: DLE LKML-Reference: <20100427223312.2322.60512.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 0ed46f3..282035f 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1588,6 +1588,72 @@ static void __kprobes kill_kprobe(struct kprobe *p) arch_remove_kprobe(p); } +/* Disable one kprobe */ +int __kprobes disable_kprobe(struct kprobe *kp) +{ + int ret = 0; + struct kprobe *p; + + mutex_lock(&kprobe_mutex); + + /* Check whether specified probe is valid. */ + p = __get_valid_kprobe(kp); + if (unlikely(p == NULL)) { + ret = -EINVAL; + goto out; + } + + /* If the probe is already disabled (or gone), just return */ + if (kprobe_disabled(kp)) + goto out; + + kp->flags |= KPROBE_FLAG_DISABLED; + if (p != kp) + /* When kp != p, p is always enabled. */ + try_to_disable_aggr_kprobe(p); + + if (!kprobes_all_disarmed && kprobe_disabled(p)) + disarm_kprobe(p); +out: + mutex_unlock(&kprobe_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(disable_kprobe); + +/* Enable one kprobe */ +int __kprobes enable_kprobe(struct kprobe *kp) +{ + int ret = 0; + struct kprobe *p; + + mutex_lock(&kprobe_mutex); + + /* Check whether specified probe is valid. */ + p = __get_valid_kprobe(kp); + if (unlikely(p == NULL)) { + ret = -EINVAL; + goto out; + } + + if (kprobe_gone(kp)) { + /* This kprobe has gone, we couldn't enable it. */ + ret = -EINVAL; + goto out; + } + + if (p != kp) + kp->flags &= ~KPROBE_FLAG_DISABLED; + + if (!kprobes_all_disarmed && kprobe_disabled(p)) { + p->flags &= ~KPROBE_FLAG_DISABLED; + arm_kprobe(p); + } +out: + mutex_unlock(&kprobe_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(enable_kprobe); + void __kprobes dump_kprobe(struct kprobe *kp) { printk(KERN_WARNING "Dumping kprobe:\n"); @@ -1805,72 +1871,6 @@ static const struct file_operations debugfs_kprobes_operations = { .release = seq_release, }; -/* Disable one kprobe */ -int __kprobes disable_kprobe(struct kprobe *kp) -{ - int ret = 0; - struct kprobe *p; - - mutex_lock(&kprobe_mutex); - - /* Check whether specified probe is valid. */ - p = __get_valid_kprobe(kp); - if (unlikely(p == NULL)) { - ret = -EINVAL; - goto out; - } - - /* If the probe is already disabled (or gone), just return */ - if (kprobe_disabled(kp)) - goto out; - - kp->flags |= KPROBE_FLAG_DISABLED; - if (p != kp) - /* When kp != p, p is always enabled. */ - try_to_disable_aggr_kprobe(p); - - if (!kprobes_all_disarmed && kprobe_disabled(p)) - disarm_kprobe(p); -out: - mutex_unlock(&kprobe_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(disable_kprobe); - -/* Enable one kprobe */ -int __kprobes enable_kprobe(struct kprobe *kp) -{ - int ret = 0; - struct kprobe *p; - - mutex_lock(&kprobe_mutex); - - /* Check whether specified probe is valid. */ - p = __get_valid_kprobe(kp); - if (unlikely(p == NULL)) { - ret = -EINVAL; - goto out; - } - - if (kprobe_gone(kp)) { - /* This kprobe has gone, we couldn't enable it. */ - ret = -EINVAL; - goto out; - } - - if (p != kp) - kp->flags &= ~KPROBE_FLAG_DISABLED; - - if (!kprobes_all_disarmed && kprobe_disabled(p)) { - p->flags &= ~KPROBE_FLAG_DISABLED; - arm_kprobe(p); - } -out: - mutex_unlock(&kprobe_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(enable_kprobe); - static void __kprobes arm_all_kprobes(void) { struct hlist_head *head; -- cgit v0.10.2 From e157eb8341e7885ff2d9f1620155e3da6e0c8f56 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 8 May 2010 18:33:03 +0300 Subject: perf report: Document '--call-graph' better for usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch improves 'perf report -h' output for the '--call-graph' command line option by enumerating the different output types. Signed-off-by: Pekka Enberg Cc: Arnaldo Carvalho de Melo Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <1273332783-4268-1-git-send-email-penberg@cs.helsinki.fi> Signed-off-by: Ingo Molnar diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 0152b54..196473b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -457,7 +457,7 @@ static const struct option options[] = { OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, "Only display entries with parent-match"), OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", - "Display callchains using output_type and min percent threshold. " + "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. " "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", "only consider symbols in these dsos"), -- cgit v0.10.2 From 984028075794c00cbf4fb1e94bb6233e8be08875 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 2 May 2010 22:05:29 +0200 Subject: perf: Introduce a new "round of buffers read" pseudo event In order to provide a more rubust and deterministic reordering algorithm, we need to know when we reach a point where we just did a pass through over every counter buffers to read every thing they had. This patch introduces a new PERF_RECORD_FINISHED_ROUND pseudo event that only consist in an event header and doesn't need to contain anything. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Cc: Masami Hiramatsu diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d3981ac..6b77b28 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -494,6 +494,29 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) " relocation symbol.\n", machine->pid); } +static struct perf_event_header finished_round_event = { + .size = sizeof(struct perf_event_header), + .type = PERF_RECORD_FINISHED_ROUND, +}; + +static void mmap_read_all(void) +{ + int i, counter, thread; + + for (i = 0; i < nr_cpu; i++) { + for (counter = 0; counter < nr_counters; counter++) { + for (thread = 0; thread < thread_num; thread++) { + if (mmap_array[i][counter][thread].base) + mmap_read(&mmap_array[i][counter][thread]); + } + + } + } + + if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) + write_output(&finished_round_event, sizeof(finished_round_event)); +} + static int __cmd_record(int argc, const char **argv) { int i, counter; @@ -726,16 +749,7 @@ static int __cmd_record(int argc, const char **argv) int hits = samples; int thread; - for (i = 0; i < nr_cpu; i++) { - for (counter = 0; counter < nr_counters; counter++) { - for (thread = 0; - thread < thread_num; thread++) { - if (mmap_array[i][counter][thread].base) - mmap_read(&mmap_array[i][counter][thread]); - } - - } - } + mmap_read_all(); if (hits == samples) { if (done) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b364da5..6cc1b1d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -84,11 +84,12 @@ struct build_id_event { char filename[]; }; -enum perf_header_event_type { /* above any possible kernel type */ +enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_ATTR = 64, PERF_RECORD_HEADER_EVENT_TYPE = 65, PERF_RECORD_HEADER_TRACING_DATA = 66, PERF_RECORD_HEADER_BUILD_ID = 67, + PERF_RECORD_FINISHED_ROUND = 68, PERF_RECORD_HEADER_MAX }; -- cgit v0.10.2 From d6b17bebd79dae2e3577f2ea27a832af4991a5e6 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 3 May 2010 15:14:33 +0200 Subject: perf: Provide a new deterministic events reordering algorithm The current events reordering algorithm is based on a heuristic that gets broken once we deal with a very fast flow of events. Indeed the time period based flushing is not suitable anymore in the following case, assuming we have a flush period of two seconds. CPU 0 | CPU 1 | cnt1 timestamps | cnt1 timestamps | 0 | 0 1 | 1 2 | 2 3 | 3 [...] | [...] 4 seconds later If we spend too much time to read the buffers (case of a lot of events to record in each buffers or when we have a lot of CPU buffers to read), in the next pass the CPU 0 buffer could contain a slice of several seconds of events. We'll read them all and notice we've reached the period to flush. In the above example we flush the first half of the CPU 0 buffer, then we read the CPU 1 buffer where we have events that were on the flush slice and then the reordering fails. It's simple to reproduce with: perf lock record perf bench sched messaging To solve this, we use a new solution that doesn't rely on an heuristical time slice period anymore but on a deterministic basis based on how perf record does its job. perf record saves the buffers through passes. A pass is a tour on every buffers from every CPUs. This is made in order: for each CPU we read the buffers of every counters. So the more buffers we visit, the later will be the timstamps of their events. When perf record finishes a pass it records a PERF_RECORD_FINISHED_ROUND pseudo event. We record the max timestamp t found in the pass n. Assuming these timestamps are monotonic across cpus, we know that if a buffer still has events with timestamps below t, they will be all available and then read in the pass n + 1. Hence when we start to read the pass n + 2, we can safely flush every events with timestamps below t. ============ PASS n ================= CPU 0 | CPU 1 | cnt1 timestamps | cnt2 timestamps 1 | 2 2 | 3 - | 4 <--- max recorded ============ PASS n + 1 ============== CPU 0 | CPU 1 | cnt1 timestamps | cnt2 timestamps 3 | 5 4 | 6 5 | 7 <---- max recorded Flush every events below timestamp 4 ============ PASS n + 2 ============== CPU 0 | CPU 1 | cnt1 timestamps | cnt2 timestamps 6 | 8 7 | 9 - | 10 Flush every events below timestamp 7 etc... It also works on perf.data versions that don't have PERF_RECORD_FINISHED_ROUND pseudo events. The difference is that the events will be only flushed in the end of the perf.data processing. It will then consume more memory and scale less with large perf.data files. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Tom Zanussi Cc: Masami Hiramatsu diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d353e7..9401909 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -98,7 +98,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->unknown_events = 0; self->machines = RB_ROOT; self->repipe = repipe; - self->ordered_samples.flush_limit = ULLONG_MAX; INIT_LIST_HEAD(&self->ordered_samples.samples_head); if (mode == O_RDONLY) { @@ -194,6 +193,18 @@ static int process_event_stub(event_t *event __used, return 0; } +static int process_finished_round_stub(event_t *event __used, + struct perf_session *session __used, + struct perf_event_ops *ops __used) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_finished_round(event_t *event, + struct perf_session *session, + struct perf_event_ops *ops); + static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) { if (handler->sample == NULL) @@ -222,6 +233,12 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->tracing_data = process_event_stub; if (handler->build_id == NULL) handler->build_id = process_event_stub; + if (handler->finished_round == NULL) { + if (handler->ordered_samples) + handler->finished_round = process_finished_round; + else + handler->finished_round = process_finished_round_stub; + } } static const char *event__name[] = { @@ -359,16 +376,14 @@ struct sample_queue { struct list_head list; }; -#define FLUSH_PERIOD (2 * NSEC_PER_SEC) - static void flush_sample_queue(struct perf_session *s, struct perf_event_ops *ops) { struct list_head *head = &s->ordered_samples.samples_head; - u64 limit = s->ordered_samples.flush_limit; + u64 limit = s->ordered_samples.next_flush; struct sample_queue *tmp, *iter; - if (!ops->ordered_samples) + if (!ops->ordered_samples || !limit) return; list_for_each_entry_safe(iter, tmp, head, list) { @@ -387,6 +402,55 @@ static void flush_sample_queue(struct perf_session *s, } } +/* + * When perf record finishes a pass on every buffers, it records this pseudo + * event. + * We record the max timestamp t found in the pass n. + * Assuming these timestamps are monotonic across cpus, we know that if + * a buffer still has events with timestamps below t, they will be all + * available and then read in the pass n + 1. + * Hence when we start to read the pass n + 2, we can safely flush every + * events with timestamps below t. + * + * ============ PASS n ================= + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 1 | 2 + * 2 | 3 + * - | 4 <--- max recorded + * + * ============ PASS n + 1 ============== + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 3 | 5 + * 4 | 6 + * 5 | 7 <---- max recorded + * + * Flush every events below timestamp 4 + * + * ============ PASS n + 2 ============== + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 6 | 8 + * 7 | 9 + * - | 10 + * + * Flush every events below timestamp 7 + * etc... + */ +static int process_finished_round(event_t *event __used, + struct perf_session *session, + struct perf_event_ops *ops) +{ + flush_sample_queue(session, ops); + session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; + + return 0; +} + static void __queue_sample_end(struct sample_queue *new, struct list_head *head) { struct sample_queue *iter; @@ -455,16 +519,11 @@ static void __queue_sample_event(struct sample_queue *new, } static int queue_sample_event(event_t *event, struct sample_data *data, - struct perf_session *s, - struct perf_event_ops *ops) + struct perf_session *s) { u64 timestamp = data->time; struct sample_queue *new; - u64 flush_limit; - - if (s->ordered_samples.flush_limit == ULLONG_MAX) - s->ordered_samples.flush_limit = timestamp + FLUSH_PERIOD; if (timestamp < s->ordered_samples.last_flush) { printf("Warning: Timestamp below last timeslice flush\n"); @@ -488,23 +547,8 @@ static int queue_sample_event(event_t *event, struct sample_data *data, __queue_sample_event(new, s); s->ordered_samples.last_inserted = new; - /* - * We want to have a slice of events covering 2 * FLUSH_PERIOD - * If FLUSH_PERIOD is big enough, it ensures every events that occured - * in the first half of the timeslice have all been buffered and there - * are none remaining (we need that because of the weakly ordered - * event recording we have). Then once we reach the 2 * FLUSH_PERIOD - * timeslice, we flush the first half to be gentle with the memory - * (the second half can still get new events in the middle, so wait - * another period to flush it) - */ - flush_limit = s->ordered_samples.flush_limit; - - if (new->timestamp > flush_limit && - new->timestamp - flush_limit > FLUSH_PERIOD) { - s->ordered_samples.flush_limit += FLUSH_PERIOD; - flush_sample_queue(s, ops); - } + if (new->timestamp > s->ordered_samples.max_timestamp) + s->ordered_samples.max_timestamp = new->timestamp; return 0; } @@ -520,7 +564,7 @@ static int perf_session__process_sample(event_t *event, struct perf_session *s, bzero(&data, sizeof(struct sample_data)); event__parse_sample(event, s->sample_type, &data); - queue_sample_event(event, &data, s, ops); + queue_sample_event(event, &data, s); return 0; } @@ -572,6 +616,8 @@ static int perf_session__process_event(struct perf_session *self, return ops->tracing_data(event, self); case PERF_RECORD_HEADER_BUILD_ID: return ops->build_id(event, self); + case PERF_RECORD_FINISHED_ROUND: + return ops->finished_round(event, self, ops); default: self->unknown_events++; return -1; @@ -786,7 +832,7 @@ more: done: err = 0; /* do the final flush for ordered samples */ - self->ordered_samples.flush_limit = ULLONG_MAX; + self->ordered_samples.next_flush = ULLONG_MAX; flush_sample_queue(self, ops); out_err: ui_progress__delete(progress); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f2b2c6a..568fd08 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -14,7 +14,8 @@ struct thread; struct ordered_samples { u64 last_flush; - u64 flush_limit; + u64 next_flush; + u64 max_timestamp; struct list_head samples_head; struct sample_queue *last_inserted; }; @@ -41,23 +42,28 @@ struct perf_session { char filename[0]; }; +struct perf_event_ops; + typedef int (*event_op)(event_t *self, struct perf_session *session); +typedef int (*event_op2)(event_t *self, struct perf_session *session, + struct perf_event_ops *ops); struct perf_event_ops { - event_op sample, - mmap, - comm, - fork, - exit, - lost, - read, - throttle, - unthrottle, - attr, - event_type, - tracing_data, - build_id; - bool ordered_samples; + event_op sample, + mmap, + comm, + fork, + exit, + lost, + read, + throttle, + unthrottle, + attr, + event_type, + tracing_data, + build_id; + event_op2 finished_round; + bool ordered_samples; }; struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); -- cgit v0.10.2 From 26242d859c9be9eea61f7f19514e9d272ae8ce26 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Mon, 3 May 2010 14:12:00 +0900 Subject: perf lock: Add "info" subcommand for dumping misc information This adds the "info" subcommand to perf lock which can be used to dump metadata like threads or addresses of lock instances. "map" was removed because info should do the work for it. This will be useful not only for debugging but also for ordinary analyzing. v2: adding example of usage % sudo ./perf lock info -t | Thread ID: comm | 0: swapper | 1: init | 18: migration/5 | 29: events/2 | 32: events/5 | 33: events/6 ... % sudo ./perf lock info -m | Address of instance: name of class | 0xffff8800b95adae0: &(&sighand->siglock)->rlock | 0xffff8800bbb41ae0: &(&sighand->siglock)->rlock | 0xffff8800bf165ae0: &(&sighand->siglock)->rlock | 0xffff8800b9576a98: &p->cred_guard_mutex | 0xffff8800bb890a08: &(&p->alloc_lock)->rlock | 0xffff8800b9522a08: &(&p->alloc_lock)->rlock | 0xffff8800bb8aaa08: &(&p->alloc_lock)->rlock | 0xffff8800bba72a08: &(&p->alloc_lock)->rlock | 0xffff8800bf18ea08: &(&p->alloc_lock)->rlock | 0xffff8800b8a0d8a0: &(&ip->i_lock)->mr_lock | 0xffff88009bf818a0: &(&ip->i_lock)->mr_lock | 0xffff88004c66b8a0: &(&ip->i_lock)->mr_lock | 0xffff8800bb6478a0: &(shost->host_lock)->rlock v3: fixed some problems Frederic pointed out * better rbtree tracking in dump_threads() * removed printf() and used pr_info() and pr_debug() Signed-off-by: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jens Axboe Cc: Jason Baron Cc: Xiao Guangrong LKML-Reference: <1272863520-16179-1-git-send-email-mitake@dcl.info.waseda.ac.jp> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6605000..c4eb854 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -720,15 +720,15 @@ static void print_result(void) char cut_name[20]; int bad, total; - printf("%20s ", "Name"); - printf("%10s ", "acquired"); - printf("%10s ", "contended"); + pr_info("%20s ", "Name"); + pr_info("%10s ", "acquired"); + pr_info("%10s ", "contended"); - printf("%15s ", "total wait (ns)"); - printf("%15s ", "max wait (ns)"); - printf("%15s ", "min wait (ns)"); + pr_info("%15s ", "total wait (ns)"); + pr_info("%15s ", "max wait (ns)"); + pr_info("%15s ", "min wait (ns)"); - printf("\n\n"); + pr_info("\n\n"); bad = total = 0; while ((st = pop_from_result())) { @@ -741,7 +741,7 @@ static void print_result(void) if (strlen(st->name) < 16) { /* output raw name */ - printf("%20s ", st->name); + pr_info("%20s ", st->name); } else { strncpy(cut_name, st->name, 16); cut_name[16] = '.'; @@ -749,17 +749,17 @@ static void print_result(void) cut_name[18] = '.'; cut_name[19] = '\0'; /* cut off name for saving output style */ - printf("%20s ", cut_name); + pr_info("%20s ", cut_name); } - printf("%10u ", st->nr_acquired); - printf("%10u ", st->nr_contended); + pr_info("%10u ", st->nr_acquired); + pr_info("%10u ", st->nr_contended); - printf("%15llu ", st->wait_time_total); - printf("%15llu ", st->wait_time_max); - printf("%15llu ", st->wait_time_min == ULLONG_MAX ? + pr_info("%15llu ", st->wait_time_total); + pr_info("%15llu ", st->wait_time_max); + pr_info("%15llu ", st->wait_time_min == ULLONG_MAX ? 0 : st->wait_time_min); - printf("\n"); + pr_info("\n"); } { @@ -768,28 +768,59 @@ static void print_result(void) const char *name[4] = { "acquire", "acquired", "contended", "release" }; - printf("\n=== output for debug===\n\n"); - printf("bad:%d, total:%d\n", bad, total); - printf("bad rate:%f\n", (double)(bad / total)); + pr_debug("\n=== output for debug===\n\n"); + pr_debug("bad:%d, total:%d\n", bad, total); + pr_debug("bad rate:%f\n", (double)(bad / total)); - printf("histogram of events caused bad sequence\n"); + pr_debug("histogram of events caused bad sequence\n"); for (i = 0; i < 4; i++) - printf(" %10s: %d\n", name[i], bad_hist[i]); + pr_debug(" %10s: %d\n", name[i], bad_hist[i]); } } +static int info_threads; +static int info_map; + +static void dump_threads(void) +{ + struct thread_stat *st; + struct rb_node *node; + struct thread *t; + + pr_info("%10s: comm\n", "Thread ID"); + + node = rb_first(&thread_stats); + while (node) { + st = container_of(node, struct thread_stat, rb); + t = perf_session__findnew(session, st->tid); + pr_info("%10d: %s\n", st->tid, t->comm); + node = rb_next(node); + }; +} + static void dump_map(void) { unsigned int i; struct lock_stat *st; + pr_info("Address of instance: name of class\n"); for (i = 0; i < LOCKHASH_SIZE; i++) { list_for_each_entry(st, &lockhash_table[i], hash_entry) { - printf("%p: %s\n", st->addr, st->name); + pr_info(" %p: %s\n", st->addr, st->name); } } } +static void dump_info(void) +{ + if (info_threads) + dump_threads(); + else if (info_map) + dump_map(); + else + die("Unknown type of information\n"); +} + static int process_sample_event(event_t *self, struct perf_session *s) { struct sample_data data; @@ -858,6 +889,19 @@ static const struct option report_options[] = { OPT_END() }; +static const char * const info_usage[] = { + "perf lock info []", + NULL +}; + +static const struct option info_options[] = { + OPT_BOOLEAN('t', "threads", &info_threads, + "dump thread list in perf.data"), + OPT_BOOLEAN('m', "map", &info_map, + "map of lock instances (name:address table)"), + OPT_END() +}; + static const char * const lock_usage[] = { "perf lock [] {record|trace|report}", NULL @@ -929,12 +973,18 @@ int cmd_lock(int argc, const char **argv, const char *prefix __used) } else if (!strcmp(argv[0], "trace")) { /* Aliased to 'perf trace' */ return cmd_trace(argc, argv, prefix); - } else if (!strcmp(argv[0], "map")) { + } else if (!strcmp(argv[0], "info")) { + if (argc) { + argc = parse_options(argc, argv, + info_options, info_usage, 0); + if (argc) + usage_with_options(info_usage, info_options); + } /* recycling report_lock_ops */ trace_handler = &report_lock_ops; setup_pager(); read_events(); - dump_map(); + dump_info(); } else { usage_with_options(lock_usage, lock_options); } -- cgit v0.10.2 From 10350ec362b48f79f3df8447c25813790075e27c Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 5 May 2010 23:47:28 +0200 Subject: perf: Cleanup perf lock broken states Use enum to get a human view of bad_hist indexes and put bad histogram output in its own function. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index c4eb854..1e93179 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -387,7 +387,15 @@ static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) return seq; } -static int bad_hist[4]; +enum broken_state { + BROKEN_ACQUIRE, + BROKEN_ACQUIRED, + BROKEN_CONTENDED, + BROKEN_RELEASE, + BROKEN_MAX, +}; + +static int bad_hist[BROKEN_MAX]; static void report_lock_acquire_event(struct trace_acquire_event *acquire_event, @@ -437,7 +445,7 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event, broken: /* broken lock sequence, discard it */ ls->discard = 1; - bad_hist[0]++; + bad_hist[BROKEN_ACQUIRE]++; list_del(&seq->list); free(seq); goto end; @@ -481,7 +489,6 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event, case SEQ_STATE_CONTENDED: contended_term = timestamp - seq->prev_event_time; ls->wait_time_total += contended_term; - if (contended_term < ls->wait_time_min) ls->wait_time_min = contended_term; else if (ls->wait_time_max < contended_term) @@ -492,7 +499,7 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event, case SEQ_STATE_READ_ACQUIRED: /* broken lock sequence, discard it */ ls->discard = 1; - bad_hist[1]++; + bad_hist[BROKEN_ACQUIRED]++; list_del(&seq->list); free(seq); goto end; @@ -540,7 +547,7 @@ report_lock_contended_event(struct trace_contended_event *contended_event, case SEQ_STATE_CONTENDED: /* broken lock sequence, discard it */ ls->discard = 1; - bad_hist[2]++; + bad_hist[BROKEN_CONTENDED]++; list_del(&seq->list); free(seq); goto end; @@ -594,7 +601,7 @@ report_lock_release_event(struct trace_release_event *release_event, case SEQ_STATE_RELEASED: /* broken lock sequence, discard it */ ls->discard = 1; - bad_hist[3]++; + bad_hist[BROKEN_RELEASE]++; goto free_seq; break; default: @@ -713,6 +720,21 @@ process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) process_lock_release_event(data, event, cpu, timestamp, thread); } +static void print_bad_events(int bad, int total) +{ + /* Output for debug, this have to be removed */ + int i; + const char *name[4] = + { "acquire", "acquired", "contended", "release" }; + + pr_info("\n=== output for debug===\n\n"); + pr_info("bad:%d, total:%d\n", bad, total); + pr_info("bad rate:%f\n", (double)(bad / total)); + pr_info("histogram of events caused bad sequence\n"); + for (i = 0; i < BROKEN_MAX; i++) + pr_info(" %10s: %d\n", name[i], bad_hist[i]); +} + /* TODO: various way to print, coloring, nano or milli sec */ static void print_result(void) { @@ -762,20 +784,7 @@ static void print_result(void) pr_info("\n"); } - { - /* Output for debug, this have to be removed */ - int i; - const char *name[4] = - { "acquire", "acquired", "contended", "release" }; - - pr_debug("\n=== output for debug===\n\n"); - pr_debug("bad:%d, total:%d\n", bad, total); - pr_debug("bad rate:%f\n", (double)(bad / total)); - - pr_debug("histogram of events caused bad sequence\n"); - for (i = 0; i < 4; i++) - pr_debug(" %10s: %d\n", name[i], bad_hist[i]); - } + print_bad_events(bad, total); } static int info_threads; -- cgit v0.10.2 From 84c7a21791eb2e962a27e19bab5b77d5d9e13a34 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 5 May 2010 23:57:25 +0200 Subject: perf: Humanize lock flags in perf lock Use an enum instead of plain constants for lock flags. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 1e93179..3b304ed 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -397,6 +397,11 @@ enum broken_state { static int bad_hist[BROKEN_MAX]; +enum acquire_flags { + TRY_LOCK = 1, + READ_LOCK = 2, +}; + static void report_lock_acquire_event(struct trace_acquire_event *acquire_event, struct event *__event __used, @@ -421,9 +426,9 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event, if (!acquire_event->flag) { seq->state = SEQ_STATE_ACQUIRING; } else { - if (acquire_event->flag & 1) + if (acquire_event->flag & TRY_LOCK) ls->nr_trylock++; - if (acquire_event->flag & 2) + if (acquire_event->flag & READ_LOCK) ls->nr_readlock++; seq->state = SEQ_STATE_READ_ACQUIRED; seq->read_count = 1; @@ -431,7 +436,7 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event, } break; case SEQ_STATE_READ_ACQUIRED: - if (acquire_event->flag & 2) { + if (acquire_event->flag & READ_LOCK) { seq->read_count++; ls->nr_acquired++; goto end; -- cgit v0.10.2 From 5efe08cf685f33f562566dc68b6077b6f6a4f706 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 6 May 2010 04:55:22 +0200 Subject: perf: Fix perf lock bad rate Fix the cast made to get the bad rate. It is made in the result instead of the operands. We need the operands to be cast in double, otherwise the result will always be zero. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 3b304ed..d7dde9c 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -733,8 +733,8 @@ static void print_bad_events(int bad, int total) { "acquire", "acquired", "contended", "release" }; pr_info("\n=== output for debug===\n\n"); - pr_info("bad:%d, total:%d\n", bad, total); - pr_info("bad rate:%f\n", (double)(bad / total)); + pr_info("bad: %d, total: %d\n", bad, total); + pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100); pr_info("histogram of events caused bad sequence\n"); for (i = 0; i < BROKEN_MAX; i++) pr_info(" %10s: %d\n", name[i], bad_hist[i]); -- cgit v0.10.2 From 90c0e5fc7b73d2575c7367e1da70ff9521718e5e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 7 May 2010 02:33:42 +0200 Subject: perf lock: Always check min AND max wait time When a lock is acquired after beeing contended, we update the wait time statistics for the given lock. But if the min wait time is updated, we don't check the max wait time. This is wrong because the first time we update the wait time, we want to update both min and max wait time. Before: Name acquired contended total wait (ns) max wait (ns) min wait (ns) key 8 1 21656 0 21656 After: Name acquired contended total wait (ns) max wait (ns) min wait (ns) key 8 1 21656 21656 21656 Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index d7dde9c..e549f45 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -496,7 +496,7 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event, ls->wait_time_total += contended_term; if (contended_term < ls->wait_time_min) ls->wait_time_min = contended_term; - else if (ls->wait_time_max < contended_term) + if (ls->wait_time_max < contended_term) ls->wait_time_max = contended_term; break; case SEQ_STATE_RELEASED: -- cgit v0.10.2 From 883a2a3189dae9d2912c417e47152f51cb922a3f Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 8 May 2010 06:16:11 +0200 Subject: tracing: Drop lock_acquired waittime field Drop the waittime field from the lock_acquired event, we can calculate it by substracting the lock_acquired event timestamp with the matching lock_acquire one. It is not needed and takes useless space in the traces. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Hitoshi Mitake Cc: Steven Rostedt diff --git a/include/trace/events/lock.h b/include/trace/events/lock.h index 5c1dcfc..17ca287 100644 --- a/include/trace/events/lock.h +++ b/include/trace/events/lock.h @@ -78,24 +78,21 @@ TRACE_EVENT(lock_contended, ); TRACE_EVENT(lock_acquired, - TP_PROTO(struct lockdep_map *lock, unsigned long ip, s64 waittime), + TP_PROTO(struct lockdep_map *lock, unsigned long ip), - TP_ARGS(lock, ip, waittime), + TP_ARGS(lock, ip), TP_STRUCT__entry( __string(name, lock->name) - __field(s64, wait_nsec) __field(void *, lockdep_addr) ), TP_fast_assign( __assign_str(name, lock->name); - __entry->wait_nsec = waittime; __entry->lockdep_addr = lock; ), - TP_printk("%p %s (%llu ns)", __entry->lockdep_addr, - __get_str(name), - __entry->wait_nsec) + TP_printk("%p %s", __entry->lockdep_addr, + __get_str(name)) ); #endif diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 2594e1c..31e22e7 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -3380,7 +3380,7 @@ found_it: hlock->holdtime_stamp = now; } - trace_lock_acquired(lock, ip, waittime); + trace_lock_acquired(lock, ip); stats = get_lock_stats(hlock_class(hlock)); if (waittime) { -- cgit v0.10.2 From 93135439459920c4d856f4ab8f068c030085c8df Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 8 May 2010 06:24:25 +0200 Subject: tracing: Drop the nested field from lock_release event Drop the nested field as we don't use it. Every nested state can be computed from a state machine on post processing already. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Hitoshi Mitake Cc: Steven Rostedt diff --git a/include/trace/events/lock.h b/include/trace/events/lock.h index 17ca287..fde4c38 100644 --- a/include/trace/events/lock.h +++ b/include/trace/events/lock.h @@ -37,9 +37,9 @@ TRACE_EVENT(lock_acquire, TRACE_EVENT(lock_release, - TP_PROTO(struct lockdep_map *lock, int nested, unsigned long ip), + TP_PROTO(struct lockdep_map *lock, unsigned long ip), - TP_ARGS(lock, nested, ip), + TP_ARGS(lock, ip), TP_STRUCT__entry( __string(name, lock->name) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 31e22e7..e9c759f 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -3227,7 +3227,7 @@ void lock_release(struct lockdep_map *lock, int nested, raw_local_irq_save(flags); check_flags(flags); current->lockdep_recursion = 1; - trace_lock_release(lock, nested, ip); + trace_lock_release(lock, ip); __lock_release(lock, nested, ip); current->lockdep_recursion = 0; raw_local_irq_restore(flags); -- cgit v0.10.2 From 2c193c736803ceb547daec725e5c5d992d039f20 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 8 May 2010 06:36:02 +0200 Subject: tracing: Factorize lock events in a lock class lock_acquired, lock_contended and lock_release now share the same prototype and format. Let's factorize them into a lock event class. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Hitoshi Mitake Cc: Steven Rostedt diff --git a/include/trace/events/lock.h b/include/trace/events/lock.h index fde4c38..2821b86 100644 --- a/include/trace/events/lock.h +++ b/include/trace/events/lock.h @@ -35,15 +35,15 @@ TRACE_EVENT(lock_acquire, __get_str(name)) ); -TRACE_EVENT(lock_release, +DECLARE_EVENT_CLASS(lock, TP_PROTO(struct lockdep_map *lock, unsigned long ip), TP_ARGS(lock, ip), TP_STRUCT__entry( - __string(name, lock->name) - __field(void *, lockdep_addr) + __string( name, lock->name ) + __field( void *, lockdep_addr ) ), TP_fast_assign( @@ -51,48 +51,30 @@ TRACE_EVENT(lock_release, __entry->lockdep_addr = lock; ), - TP_printk("%p %s", - __entry->lockdep_addr, __get_str(name)) + TP_printk("%p %s", __entry->lockdep_addr, __get_str(name)) ); -#ifdef CONFIG_LOCK_STAT - -TRACE_EVENT(lock_contended, +DEFINE_EVENT(lock, lock_release, TP_PROTO(struct lockdep_map *lock, unsigned long ip), - TP_ARGS(lock, ip), - - TP_STRUCT__entry( - __string(name, lock->name) - __field(void *, lockdep_addr) - ), + TP_ARGS(lock, ip) +); - TP_fast_assign( - __assign_str(name, lock->name); - __entry->lockdep_addr = lock; - ), +#ifdef CONFIG_LOCK_STAT - TP_printk("%p %s", - __entry->lockdep_addr, __get_str(name)) -); +DEFINE_EVENT(lock, lock_contended, -TRACE_EVENT(lock_acquired, TP_PROTO(struct lockdep_map *lock, unsigned long ip), - TP_ARGS(lock, ip), + TP_ARGS(lock, ip) +); - TP_STRUCT__entry( - __string(name, lock->name) - __field(void *, lockdep_addr) - ), +DEFINE_EVENT(lock, lock_acquired, - TP_fast_assign( - __assign_str(name, lock->name); - __entry->lockdep_addr = lock; - ), - TP_printk("%p %s", __entry->lockdep_addr, - __get_str(name)) + TP_PROTO(struct lockdep_map *lock, unsigned long ip), + + TP_ARGS(lock, ip) ); #endif -- cgit v0.10.2 From 794e43b56c18b95fc9776c914a2659e7d558a352 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Wed, 5 May 2010 00:27:40 -0500 Subject: perf/live-mode: Handle payload-less events Some events, such as the PERF_RECORD_FINISHED_ROUND event consist of only an event header and no data. In this case, a 0-length payload will be read, and the 0 return value will be wrongly interpreted as an 'unexpected end of event stream'. This patch allows for proper handling of data-less events by skipping 0-length reads. Signed-off-by: Tom Zanussi Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Masami Hiramatsu LKML-Reference: <1273038527.6383.51.camel@tropicana> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9401909..00ab298 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -696,15 +696,18 @@ more: p = &event; p += sizeof(struct perf_event_header); - err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); - if (err <= 0) { - if (err == 0) { - pr_err("unexpected end of event stream\n"); - goto done; - } + if (size - sizeof(struct perf_event_header)) { + err = do_read(self->fd, p, + size - sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) { + pr_err("unexpected end of event stream\n"); + goto done; + } - pr_err("failed to read event data\n"); - goto out_err; + pr_err("failed to read event data\n"); + goto out_err; + } } if (size == 0 || -- cgit v0.10.2 From 139633c6a43781cd44798165b0472a34bf53a1e8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 11:47:13 -0300 Subject: perf callchain: Move validate_callchain to callchain lib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 196473b..1cae877 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -123,19 +123,6 @@ static int perf_session__add_hist_entry(struct perf_session *self, return 0; } -static int validate_chain(struct ip_callchain *chain, event_t *event) -{ - unsigned int chain_size; - - chain_size = event->header.size; - chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; - - if (chain->nr*sizeof(u64) > chain_size) - return -1; - - return 0; -} - static int add_event_total(struct perf_session *session, struct sample_data *data, struct perf_event_attr *attr) @@ -171,7 +158,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) dump_printf("... chain: nr:%Lu\n", data.callchain->nr); - if (validate_chain(data.callchain, event) < 0) { + if (!ip_callchain__valid(data.callchain, event)) { pr_debug("call-chain problem with event, " "skipping it.\n"); return 0; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index db628af..ac14861 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -17,6 +17,13 @@ #include "callchain.h" +bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) +{ + unsigned int chain_size = event->header.size; + chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; + return chain->nr * sizeof(u64) <= chain_size; +} + #define chain_for_each_child(child, parent) \ list_for_each_entry(child, &parent->children, brothers) diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8a7e8bb..0f4da09 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -4,6 +4,7 @@ #include "../perf.h" #include #include +#include "event.h" #include "util.h" #include "symbol.h" @@ -58,4 +59,6 @@ static inline u64 cumul_hits(struct callchain_node *node) int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, struct map_symbol *syms); + +bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); #endif /* __PERF_CALLCHAIN_H */ -- cgit v0.10.2 From 39d1e1b1e26dc84d40bf2792287d0d61e44b57df Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 12:01:05 -0300 Subject: perf report: Fix leak of resolved callchains array on error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1cae877..3a70c58 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -83,7 +83,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct map_symbol *syms = NULL; struct symbol *parent = NULL; bool hit; - int err; + int err = -ENOMEM; struct hist_entry *he; struct event_stat_id *stats; struct perf_event_attr *attr; @@ -101,26 +101,24 @@ static int perf_session__add_hist_entry(struct perf_session *self, else stats = get_stats(self, data->id, 0, 0); if (stats == NULL) - return -ENOMEM; + goto out_free_syms; he = __perf_session__add_hist_entry(&stats->hists, al, parent, data->period, &hit); if (he == NULL) - return -ENOMEM; + goto out_free_syms; if (hit) __perf_session__add_count(he, al, data->period); + err = 0; if (symbol_conf.use_callchain) { if (!hit) callchain_init(he->callchain); err = append_chain(he->callchain, data->callchain, syms); - free(syms); - - if (err) - return err; } - - return 0; +out_free_syms: + free(syms); + return err; } static int add_event_total(struct perf_session *session, -- cgit v0.10.2 From 28e2a106d16046ca792722795f809e3f80a5af80 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 13:02:23 -0300 Subject: perf hist: Simplify the insertion of new hist_entry instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And with that fix at least one bug: The first hit for an entry, the one that calls malloc to create a new instance in __perf_session__add_hist_entry, wasn't adding the count to the per cpumode (PERF_RECORD_MISC_USER, etc) total variable. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ee154b5..c7ac45a 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -72,8 +72,6 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) struct sym_priv *priv; struct sym_hist *h; - he->count++; - if (!sym || !he->ms.map) return 0; @@ -99,9 +97,8 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) } static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al, u64 count) + struct addr_location *al) { - bool hit; struct hist_entry *he; if (sym_hist_filter != NULL && @@ -115,7 +112,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return 0; } - he = __perf_session__add_hist_entry(&self->hists, al, NULL, count, &hit); + he = __perf_session__add_hist_entry(&self->hists, al, NULL, 1); if (he == NULL) return -ENOMEM; @@ -135,7 +132,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) return -1; } - if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { + if (!al.filtered && perf_session__add_hist_entry(session, &al)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4cce68f..613a5c4 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -25,17 +25,9 @@ static bool show_displacement; static int perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, u64 count) { - bool hit; - struct hist_entry *he = __perf_session__add_hist_entry(&self->hists, - al, NULL, - count, &hit); - if (he == NULL) - return -ENOMEM; - - if (hit) - __perf_session__add_count(he, al, count); - - return 0; + if (__perf_session__add_hist_entry(&self->hists, al, NULL, count) != NULL) + return 0; + return -ENOMEM; } static int diff__process_sample_event(event_t *event, struct perf_session *session) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3a70c58..5e2f47f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -82,7 +82,6 @@ static int perf_session__add_hist_entry(struct perf_session *self, { struct map_symbol *syms = NULL; struct symbol *parent = NULL; - bool hit; int err = -ENOMEM; struct hist_entry *he; struct event_stat_id *stats; @@ -103,19 +102,12 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (stats == NULL) goto out_free_syms; he = __perf_session__add_hist_entry(&stats->hists, al, parent, - data->period, &hit); + data->period); if (he == NULL) goto out_free_syms; - - if (hit) - __perf_session__add_count(he, al, data->period); - err = 0; - if (symbol_conf.use_callchain) { - if (!hit) - callchain_init(he->callchain); + if (symbol_conf.use_callchain) err = append_chain(he->callchain, data->callchain, syms); - } out_free_syms: free(syms); return err; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ad6b22d..e0c8a72 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,13 +8,10 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count) +static void perf_session__add_cpumode_count(struct hist_entry *he, + unsigned int cpumode, u64 count) { - he->count += count; - - switch (al->cpumode) { + switch (cpumode) { case PERF_RECORD_MISC_KERNEL: he->count_sys += count; break; @@ -36,10 +33,24 @@ void __perf_session__add_count(struct hist_entry *he, * histogram, sorted on item, collects counts */ +static struct hist_entry *hist_entry__new(struct hist_entry *template) +{ + size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; + struct hist_entry *self = malloc(sizeof(*self) + callchain_size); + + if (self != NULL) { + *self = *template; + if (symbol_conf.use_callchain) + callchain_init(self->callchain); + } + + return self; +} + struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *sym_parent, - u64 count, bool *hit) + u64 count) { struct rb_node **p = &hists->rb_node; struct rb_node *parent = NULL; @@ -64,8 +75,8 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - *hit = true; - return he; + he->count += count; + goto out; } if (cmp < 0) @@ -74,14 +85,13 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? - sizeof(struct callchain_node) : 0)); + he = hist_entry__new(&entry); if (!he) return NULL; - *he = entry; rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, hists); - *hit = false; +out: + perf_session__add_cpumode_count(he, al->cpumode, count); return he; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9df1c34..b49013a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -12,13 +12,10 @@ struct addr_location; struct symbol; struct rb_root; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count); struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, struct addr_location *al, struct symbol *parent, - u64 count, bool *hit); + u64 count); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); int hist_entry__fprintf(struct hist_entry *self, -- cgit v0.10.2 From 76ba7e846fcc89d9d4b25b89e303c9058de96d60 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Sat, 8 May 2010 17:10:29 +0900 Subject: perf lock: Drop "-a" option from cmd_record() default arguments set This patch drops "-a" from the default arguments passed to perf record by perf lock. If a user wants to do a system wide record of lock events, perf lock record -a ... is enough for this purpose. This can reduce the size of the perf.data file. % sudo ./perf lock record whoami root [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.439 MB perf.data (~19170 samples) ] % sudo ./perf lock record -a whoami # with -a option root [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 48.962 MB perf.data (~2139197 samples) ] Signed-off-by: Hitoshi Mitake Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Jens Axboe Cc: Jason Baron Cc: Xiao Guangrong LKML-Reference: Message-Id: <1273306229-5216-1-git-send-email-mitake@dcl.info.waseda.ac.jp> Signed-off-by: Frederic Weisbecker diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index e549f45..e18dfdc 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -930,7 +930,6 @@ static const struct option lock_options[] = { static const char *record_args[] = { "record", - "-a", "-R", "-f", "-m", "1024", -- cgit v0.10.2 From 3ceb0d4438876a65606c258e5d69e03e57460dd6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 16:07:01 -0300 Subject: perf symbols: Consider unresolved DSOs in the dso__col_widt calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using BITS_PER_LONG / 4, that is the number of chars that will be used in such cases as the DSO "name". Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 23d5dfd..46563e1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -676,6 +676,13 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dso__calc_col_width(al->map->dso); al->sym = map__find_symbol(al->map, al->addr, filter); + } else { + const unsigned int unresolved_col_width = BITS_PER_LONG / 4; + + if (dsos__col_width < unresolved_col_width && + !symbol_conf.col_width_list_str && !symbol_conf.field_sep && + !symbol_conf.dso_list) + dsos__col_width = unresolved_col_width; } if (symbol_conf.sym_list && al->sym && -- cgit v0.10.2 From 4cc4945844fe2cf493f1783b6ce938ba1617d5c2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 21:14:07 -0300 Subject: perf symbols: Check if a struct machine instance was found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Which can happen when processing old files that had no fake kernel MMAP, events. That shouldn't result in perf_session__create_kernel_maps not being called, this will be fixed in a followup patch, for now do these checks to avoid segfaulting. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 46563e1..dfc8bf6 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -493,8 +493,10 @@ int event__process_mmap(event_t *self, struct perf_session *session) return 0; } - thread = perf_session__findnew(session, self->mmap.pid); machine = perf_session__find_host_machine(session); + if (machine == NULL) + goto out_problem; + thread = perf_session__findnew(session, self->mmap.pid); map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, @@ -552,6 +554,10 @@ void thread__find_addr_map(struct thread *self, if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; machine = perf_session__find_host_machine(session); + if (machine == NULL) { + al->map = NULL; + return; + } mg = &machine->kmaps; } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; @@ -559,7 +565,7 @@ void thread__find_addr_map(struct thread *self, } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { al->level = 'g'; machine = perf_session__find_machine(session, pid); - if (!machine) { + if (machine == NULL) { al->map = NULL; return; } -- cgit v0.10.2 From 1f626bc36847ac8dd192f055aed0f9678a781313 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 19:57:08 -0300 Subject: perf session: Embed the host machine data on perf_session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have just one host on a given session, and that is the most common setup right now, so embed a ->host_machine struct machine instance directly in the perf_session class, check if we're looking for it before going to the rb_tree. This also fixes a problem found when we try to process old perf.data files where we didn't have MMAP events for the kernel and modules and thus don't create the kernel maps, do it in event__preprocess_sample if it wasn't already. Reported-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi Cc: Zhang, Yanmin LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dfc8bf6..d2ea9dd 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -656,6 +656,16 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, goto out_filtered; dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); + /* + * Have we already created the kernel maps for the host machine? + * + * This should have happened earlier, when we processed the kernel MMAP + * events, but for older perf.data files there was no such thing, so do + * it now. + */ + if (cpumode == PERF_RECORD_MISC_KERNEL && + session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL) + machine__create_kernel_maps(&session->host_machine); thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, self->ip.pid, self->ip.ip, al); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 44a4df6..e672f2f 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -579,30 +579,6 @@ struct machine *machines__find(struct rb_root *self, pid_t pid) return default_machine; } -/* - * FIXME: Why repeatedly search for this? - */ -struct machine *machines__find_host(struct rb_root *self) -{ - struct rb_node **p = &self->rb_node; - struct rb_node *parent = NULL; - struct machine *machine; - pid_t pid = HOST_KERNEL_ID; - - while (*p != NULL) { - parent = *p; - machine = rb_entry(parent, struct machine, rb_node); - if (pid < machine->pid) - p = &(*p)->rb_left; - else if (pid > machine->pid) - p = &(*p)->rb_right; - else - return machine; - } - - return NULL; -} - struct machine *machines__findnew(struct rb_root *self, pid_t pid) { char path[PATH_MAX]; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d353e7..71bc608 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -100,6 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->repipe = repipe; self->ordered_samples.flush_limit = ULLONG_MAX; INIT_LIST_HEAD(&self->ordered_samples.samples_head); + machine__init(&self->host_machine, "", HOST_KERNEL_ID); if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -870,3 +871,10 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, return 0; } + +size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +{ + return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + + __dsos__fprintf(&self->host_machine.user_dsos, fp) + + machines__fprintf_dsos(&self->machines, fp); +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f2b2c6a..eb9f179 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -25,6 +25,7 @@ struct perf_session { unsigned long mmap_window; struct rb_root threads; struct thread *last_match; + struct machine host_machine; struct rb_root machines; struct events_stats events_stats; struct rb_root stats_by_id; @@ -107,18 +108,22 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, static inline struct machine *perf_session__find_host_machine(struct perf_session *self) { - return machines__find_host(&self->machines); + return &self->host_machine; } static inline struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) { + if (pid == HOST_KERNEL_ID) + return &self->host_machine; return machines__find(&self->machines, pid); } static inline struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) { + if (pid == HOST_KERNEL_ID) + return &self->host_machine; return machines__findnew(&self->machines, pid); } @@ -126,14 +131,11 @@ static inline void perf_session__process_machines(struct perf_session *self, machine__process_t process) { + process(&self->host_machine, self); return machines__process(&self->machines, process, self); } -static inline -size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) -{ - return machines__fprintf_dsos(&self->machines, fp); -} +size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); static inline size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 4c0146a..994efdb 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1889,7 +1889,7 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name) return dso; } -static size_t __dsos__fprintf(struct list_head *head, FILE *fp) +size_t __dsos__fprintf(struct list_head *head, FILE *fp) { struct dso *pos; size_t ret = 0; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index a517c17..edff866 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -167,6 +167,8 @@ int machine__load_kallsyms(struct machine *self, const char *filename, int machine__load_vmlinux_path(struct machine *self, enum map_type type, symbol_filter_t filter); +size_t __dsos__fprintf(struct list_head *head, FILE *fp); + size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp); size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); -- cgit v0.10.2 From 232a5c948da5e23dff27e48180abf4a4238f7602 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 9 May 2010 20:28:10 -0300 Subject: perf report: Allow limiting the number of entries to print in callchains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Works by adding a third parameter to the '-g' argument, after the graph type and minimum percentage, for example: [root@doppio linux-2.6-tip]# perf report -g fractal,0.5,2 Will show only the first two symbols where at least 0.5% of the samples took place. All the other symbols that don't fall outside these constraints will be put together in the last entry, prefixed with "[...]" and the total percentage for them. Suggested-by: Arjan van de Ven Acked-by: Arjan van de Ven Cc: Arjan van de Ven Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5e2f47f..642a6d8 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -343,7 +343,7 @@ static int parse_callchain_opt(const struct option *opt __used, const char *arg, int unset) { - char *tok; + char *tok, *tok2; char *endptr; /* @@ -388,10 +388,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, if (!tok) goto setup; + tok2 = strtok(NULL, ","); callchain_param.min_percent = strtod(tok, &endptr); if (tok == endptr) return -1; + if (tok2) + callchain_param.print_limit = strtod(tok2, &endptr); setup: if (register_callchain_param(&callchain_param) < 0) { fprintf(stderr, "Can't register callchain params\n"); diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 0f4da09..1cba1f5 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -34,6 +34,7 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, struct callchain_param { enum chain_mode mode; + u32 print_limit; double min_percent; sort_chain_func_t sort; }; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e0c8a72..0f154a5 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -333,6 +333,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, u64 remaining; size_t ret = 0; int i; + uint entries_printed = 0; if (callchain_param.mode == CHAIN_GRAPH_REL) new_total = self->children_hit; @@ -379,6 +380,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, new_depth_mask | (1 << depth), left_margin); node = next; + if (++entries_printed == callchain_param.print_limit) + break; } if (callchain_param.mode == CHAIN_GRAPH_REL && @@ -404,6 +407,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, bool printed = false; int i = 0; int ret = 0; + u32 entries_printed = 0; list_for_each_entry(chain, &self->val, list) { if (!i++ && sort__first_dimension == SORT_SYM) @@ -424,6 +428,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); + + if (++entries_printed == callchain_param.print_limit) + break; } ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); @@ -462,6 +469,7 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, struct rb_node *rb_node; struct callchain_node *chain; size_t ret = 0; + u32 entries_printed = 0; rb_node = rb_first(&self->sorted_chain); while (rb_node) { @@ -484,6 +492,8 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, break; } ret += fprintf(fp, "\n"); + if (++entries_printed == callchain_param.print_limit) + break; rb_node = rb_next(rb_node); } -- cgit v0.10.2 From 7f8264539c62378cccbdf9b598927b034bef4a92 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 10:51:25 -0300 Subject: perf newt: Use newtAddComponent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of newtAddComponents(just-one-entry, NULL), that is not needed if, like in this browser, we're adding just one component at a time. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7a123a9..e283a6e 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -31,7 +31,7 @@ struct ui_progress *ui_progress__new(const char *title, u64 total) self->scale = newtScale(0, 0, cols, total); if (self->scale == NULL) goto out_free_form; - newtFormAddComponents(self->form, self->scale, NULL); + newtFormAddComponent(self->form, self->scale); newtRefresh(); } @@ -107,7 +107,7 @@ static int popup_menu(int argc, char * const argv[]) if (listbox == NULL) goto out_destroy_form; - newtFormAddComponents(form, listbox, NULL); + newtFormAddComponent(form, listbox); for (i = 0; i < argc; ++i) { int len = strlen(argv[i]); @@ -365,7 +365,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); form = newt_form__new(); - newtFormAddComponents(form, tree, NULL); + newtFormAddComponent(form, tree); newtFormRun(form, &es); newtFormDestroy(form); -- cgit v0.10.2 From cdd5b75b0cd24c4d6a98b12a219217b1ccfe2586 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 10:56:50 -0300 Subject: perf callchains: Use zalloc to allocate objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index ac14861..21a52e0 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -167,7 +167,7 @@ create_child(struct callchain_node *parent, bool inherit_children) { struct callchain_node *new; - new = malloc(sizeof(*new)); + new = zalloc(sizeof(*new)); if (!new) { perror("not enough memory to create child for code path tree"); return NULL; @@ -213,7 +213,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) for (i = start; i < chain->nr; i++) { struct callchain_list *call; - call = malloc(sizeof(*call)); + call = zalloc(sizeof(*call)); if (!call) { perror("not enough memory for the code path tree"); return; @@ -386,7 +386,7 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain, if (!chain->nr) return 0; - filtered = malloc(sizeof(*filtered) + + filtered = zalloc(sizeof(*filtered) + chain->nr * sizeof(struct resolved_ip)); if (!filtered) return -ENOMEM; -- cgit v0.10.2 From d118f8ba6ac2af2bf11d40cba657c813f0f39ca2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 12:51:05 -0300 Subject: perf session: create_kernel_maps should use ->host_machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using machines__create_kernel_maps(..., HOST_KERNEL_ID) it would create another machine instance for the host machine, and since 1f626bc we have it out of the machines rb_tree. Fix it by using machine__create_kernel_maps(&self->host_machine) directly. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c088d8f..4130036 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -69,11 +69,10 @@ void perf_session__update_sample_type(struct perf_session *self) int perf_session__create_kernel_maps(struct perf_session *self) { - struct rb_root *machines = &self->machines; - int ret = machines__create_kernel_maps(machines, HOST_KERNEL_ID); + int ret = machine__create_kernel_maps(&self->host_machine); if (ret >= 0) - ret = machines__create_guest_kernel_maps(machines); + ret = machines__create_guest_kernel_maps(&self->machines); return ret; } -- cgit v0.10.2 From 1c02c4d2e92f2097f1bba63ec71560b0e05a7f36 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 13:04:11 -0300 Subject: perf hist: Introduce hists class and move lots of methods to it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cbbc79a we introduced support for multiple events by introducing a new "event_stat_id" struct and then made several perf_session methods receive a point to it instead of a pointer to perf_session, and kept the event_stats and hists rb_tree in perf_session. While working on the new newt based browser, I realised that it would be better to introduce a new class, "hists" (short for "histograms"), renaming the "event_stat_id" struct and the perf_session methods that were really "hists" methods, as they manipulate only struct hists members, not touching anything in the other perf_session members. Other optimizations, such as calculating the maximum lenght of a symbol name present in an hists instance will be possible as we add them, avoiding a re-traversal just for finding that information. The rationale for the name "hists" to replace "event_stat_id" is that we may have multiple sets of hists for the same event_stat id, as, for instance, the 'perf diff' tool has, so event stat id is not what characterizes what this struct and the functions that manipulate it do. Cc: Eric B Munson Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index c7ac45a..3940964 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -96,8 +96,7 @@ static int annotate__hist_hit(struct hist_entry *he, u64 ip) return 0; } -static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al) +static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -112,7 +111,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, return 0; } - he = __perf_session__add_hist_entry(&self->hists, al, NULL, 1); + he = __hists__add_entry(self, al, NULL, 1); if (he == NULL) return -ENOMEM; @@ -132,7 +131,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) return -1; } - if (!al.filtered && perf_session__add_hist_entry(session, &al)) { + if (!al.filtered && hists__add_entry(&session->hists, &al)) { pr_warning("problem incrementing symbol count, " "skipping event\n"); return -1; @@ -514,11 +513,11 @@ static void annotate_sym(struct hist_entry *he) free_source_line(he, len); } -static void perf_session__find_annotations(struct perf_session *self) +static void hists__find_annotations(struct hists *self) { struct rb_node *nd; - for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); struct sym_priv *priv; @@ -570,9 +569,9 @@ static int __cmd_annotate(void) if (verbose > 2) perf_session__fprintf_dsos(session, stdout); - perf_session__collapse_resort(&session->hists); - perf_session__output_resort(&session->hists, session->event_total[0]); - perf_session__find_annotations(session); + hists__collapse_resort(&session->hists); + hists__output_resort(&session->hists); + hists__find_annotations(&session->hists); out_delete: perf_session__delete(session); diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 613a5c4..3a95a02 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -22,10 +22,10 @@ static char diff__default_sort_order[] = "dso,symbol"; static bool force; static bool show_displacement; -static int perf_session__add_hist_entry(struct perf_session *self, - struct addr_location *al, u64 count) +static int hists__add_entry(struct hists *self, + struct addr_location *al, u64 count) { - if (__perf_session__add_hist_entry(&self->hists, al, NULL, count) != NULL) + if (__hists__add_entry(self, al, NULL, count) != NULL) return 0; return -ENOMEM; } @@ -49,12 +49,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi event__parse_sample(event, session->sample_type, &data); - if (perf_session__add_hist_entry(session, &al, data.period)) { + if (hists__add_entry(&session->hists, &al, data.period)) { pr_warning("problem incrementing symbol count, skipping event\n"); return -1; } - session->events_stats.total += data.period; + session->hists.stats.total += data.period; return 0; } @@ -87,35 +87,34 @@ static void perf_session__insert_hist_entry_by_name(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -static void perf_session__resort_hist_entries(struct perf_session *self) +static void hists__resort_entries(struct hists *self) { unsigned long position = 1; struct rb_root tmp = RB_ROOT; - struct rb_node *next = rb_first(&self->hists); + struct rb_node *next = rb_first(&self->entries); while (next != NULL) { struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, &self->hists); + rb_erase(&n->rb_node, &self->entries); n->position = position++; perf_session__insert_hist_entry_by_name(&tmp, n); } - self->hists = tmp; + self->entries = tmp; } -static void perf_session__set_hist_entries_positions(struct perf_session *self) +static void hists__set_positions(struct hists *self) { - perf_session__output_resort(&self->hists, self->events_stats.total); - perf_session__resort_hist_entries(self); + hists__output_resort(self); + hists__resort_entries(self); } -static struct hist_entry * -perf_session__find_hist_entry(struct perf_session *self, - struct hist_entry *he) +static struct hist_entry *hists__find_entry(struct hists *self, + struct hist_entry *he) { - struct rb_node *n = self->hists.rb_node; + struct rb_node *n = self->entries.rb_node; while (n) { struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); @@ -132,14 +131,13 @@ perf_session__find_hist_entry(struct perf_session *self, return NULL; } -static void perf_session__match_hists(struct perf_session *old_session, - struct perf_session *new_session) +static void hists__match(struct hists *older, struct hists *newer) { struct rb_node *nd; - for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) { struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); - pos->pair = perf_session__find_hist_entry(old_session, pos); + pos->pair = hists__find_entry(older, pos); } } @@ -159,15 +157,13 @@ static int __cmd_diff(void) goto out_delete; } - perf_session__output_resort(&session[1]->hists, - session[1]->events_stats.total); + hists__output_resort(&session[1]->hists); if (show_displacement) - perf_session__set_hist_entries_positions(session[0]); + hists__set_positions(&session[0]->hists); - perf_session__match_hists(session[0], session[1]); - perf_session__fprintf_hists(&session[1]->hists, session[0], - show_displacement, stdout, - session[1]->events_stats.total); + hists__match(&session[0]->hists, &session[1]->hists); + hists__fprintf(&session[1]->hists, &session[0]->hists, + show_displacement, stdout); out_delete: for (i = 0; i < 2; ++i) perf_session__delete(session[i]); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 642a6d8..53077fd 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -44,16 +44,17 @@ static char *pretty_printing_style = default_pretty_printing_style; static char callchain_default_opt[] = "fractal,0.5"; -static struct event_stat_id *get_stats(struct perf_session *self, - u64 event_stream, u32 type, u64 config) +static struct hists *perf_session__hists_findnew(struct perf_session *self, + u64 event_stream, u32 type, + u64 config) { - struct rb_node **p = &self->stats_by_id.rb_node; + struct rb_node **p = &self->hists_tree.rb_node; struct rb_node *parent = NULL; - struct event_stat_id *iter, *new; + struct hists *iter, *new; while (*p != NULL) { parent = *p; - iter = rb_entry(parent, struct event_stat_id, rb_node); + iter = rb_entry(parent, struct hists, rb_node); if (iter->config == config) return iter; @@ -64,15 +65,15 @@ static struct event_stat_id *get_stats(struct perf_session *self, p = &(*p)->rb_left; } - new = malloc(sizeof(struct event_stat_id)); + new = malloc(sizeof(struct hists)); if (new == NULL) return NULL; - memset(new, 0, sizeof(struct event_stat_id)); + memset(new, 0, sizeof(struct hists)); new->event_stream = event_stream; new->config = config; new->type = type; rb_link_node(&new->rb_node, parent, p); - rb_insert_color(&new->rb_node, &self->stats_by_id); + rb_insert_color(&new->rb_node, &self->hists_tree); return new; } @@ -84,7 +85,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, struct symbol *parent = NULL; int err = -ENOMEM; struct hist_entry *he; - struct event_stat_id *stats; + struct hists *hists; struct perf_event_attr *attr; if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { @@ -96,13 +97,12 @@ static int perf_session__add_hist_entry(struct perf_session *self, attr = perf_header__find_attr(data->id, &self->header); if (attr) - stats = get_stats(self, data->id, attr->type, attr->config); + hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); else - stats = get_stats(self, data->id, 0, 0); - if (stats == NULL) + hists = perf_session__hists_findnew(self, data->id, 0, 0); + if (hists == NULL) goto out_free_syms; - he = __perf_session__add_hist_entry(&stats->hists, al, parent, - data->period); + he = __hists__add_entry(hists, al, parent, data->period); if (he == NULL) goto out_free_syms; err = 0; @@ -117,18 +117,19 @@ static int add_event_total(struct perf_session *session, struct sample_data *data, struct perf_event_attr *attr) { - struct event_stat_id *stats; + struct hists *hists; if (attr) - stats = get_stats(session, data->id, attr->type, attr->config); + hists = perf_session__hists_findnew(session, data->id, + attr->type, attr->config); else - stats = get_stats(session, data->id, 0, 0); + hists = perf_session__hists_findnew(session, data->id, 0, 0); - if (!stats) + if (!hists) return -ENOMEM; - stats->stats.total += data->period; - session->events_stats.total += data->period; + hists->stats.total += data->period; + session->hists.stats.total += data->period; return 0; } @@ -292,35 +293,33 @@ static int __cmd_report(void) if (verbose > 2) perf_session__fprintf_dsos(session, stdout); - next = rb_first(&session->stats_by_id); + next = rb_first(&session->hists_tree); while (next) { - struct event_stat_id *stats; + struct hists *hists; u64 nr_hists; - stats = rb_entry(next, struct event_stat_id, rb_node); - perf_session__collapse_resort(&stats->hists); - nr_hists = perf_session__output_resort(&stats->hists, - stats->stats.total); + hists = rb_entry(next, struct hists, rb_node); + hists__collapse_resort(hists); + nr_hists = hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&stats->hists, nr_hists, - stats->stats.total, help, + perf_session__browse_hists(&hists->entries, nr_hists, + hists->stats.total, help, input_name); else { - if (rb_first(&session->stats_by_id) == - rb_last(&session->stats_by_id)) + if (rb_first(&session->hists.entries) == + rb_last(&session->hists.entries)) fprintf(stdout, "# Samples: %Ld\n#\n", - stats->stats.total); + hists->stats.total); else fprintf(stdout, "# Samples: %Ld %s\n#\n", - stats->stats.total, - __event_name(stats->type, stats->config)); + hists->stats.total, + __event_name(hists->type, hists->config)); - perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, - stats->stats.total); + hists__fprintf(hists, NULL, false, stdout); fprintf(stdout, "\n\n"); } - next = rb_next(&stats->rb_node); + next = rb_next(&hists->rb_node); } if (!use_browser && sort_order == default_sort_order && diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 9c483e9..6e268ca 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -107,7 +107,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) data.time, thread->comm); } - session->events_stats.total += data.period; + session->hists.stats.total += data.period; return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d2ea9dd..cce006e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -368,7 +368,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->events_stats.lost += self->lost.lost; + session->hists.stats.lost += self->lost.lost; return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6cc1b1d..48c2cc9 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -131,20 +131,6 @@ typedef union event_union { struct build_id_event build_id; } event_t; -struct events_stats { - u64 total; - u64 lost; -}; - -struct event_stat_id { - struct rb_node rb_node; - struct rb_root hists; - struct events_stats stats; - u64 config; - u64 event_stream; - u32 type; -}; - void event__print_totals(void); struct perf_session; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0f154a5..410cf56 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -8,21 +8,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -static void perf_session__add_cpumode_count(struct hist_entry *he, - unsigned int cpumode, u64 count) +static void hist_entry__add_cpumode_count(struct hist_entry *self, + unsigned int cpumode, u64 count) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->count_sys += count; + self->count_sys += count; break; case PERF_RECORD_MISC_USER: - he->count_us += count; + self->count_us += count; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->count_guest_sys += count; + self->count_guest_sys += count; break; case PERF_RECORD_MISC_GUEST_USER: - he->count_guest_us += count; + self->count_guest_us += count; break; default: break; @@ -47,12 +47,11 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *sym_parent, - u64 count) +struct hist_entry *__hists__add_entry(struct hists *self, + struct addr_location *al, + struct symbol *sym_parent, u64 count) { - struct rb_node **p = &hists->rb_node; + struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { @@ -89,9 +88,9 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, if (!he) return NULL; rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, hists); + rb_insert_color(&he->rb_node, &self->entries); out: - perf_session__add_cpumode_count(he, al->cpumode, count); + hist_entry__add_cpumode_count(he, al->cpumode, count); return he; } @@ -167,7 +166,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) rb_insert_color(&he->rb_node, root); } -void perf_session__collapse_resort(struct rb_root *hists) +void hists__collapse_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; @@ -177,28 +176,28 @@ void perf_session__collapse_resort(struct rb_root *hists) return; tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); + rb_erase(&n->rb_node, &self->entries); collapse__insert_entry(&tmp, n); } - *hists = tmp; + self->entries = tmp; } /* * reverse the map, sort on count. */ -static void perf_session__insert_output_hist_entry(struct rb_root *root, - struct hist_entry *he, - u64 min_callchain_hits) +static void __hists__insert_output_entry(struct rb_root *entries, + struct hist_entry *he, + u64 min_callchain_hits) { - struct rb_node **p = &root->rb_node; + struct rb_node **p = &entries->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; @@ -217,10 +216,10 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, } rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, root); + rb_insert_color(&he->rb_node, entries); } -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) +u64 hists__output_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; @@ -228,23 +227,21 @@ u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) u64 min_callchain_hits; u64 nr_hists = 0; - min_callchain_hits = - total_samples * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); - perf_session__insert_output_hist_entry(&tmp, n, - min_callchain_hits); + rb_erase(&n->rb_node, &self->entries); + __hists__insert_output_entry(&tmp, n, min_callchain_hits); ++nr_hists; } - *hists = tmp; + self->entries = tmp; return nr_hists; } @@ -500,12 +497,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -int hist_entry__snprintf(struct hist_entry *self, - char *s, size_t size, - struct perf_session *pair_session, - bool show_displacement, - long displacement, bool color, - u64 session_total) +int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, + struct hists *pair_hists, bool show_displacement, + long displacement, bool color, u64 session_total) { struct sort_entry *se; u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; @@ -515,9 +509,9 @@ int hist_entry__snprintf(struct hist_entry *self, if (symbol_conf.exclude_other && !self->parent) return 0; - if (pair_session) { + if (pair_hists) { count = self->pair ? self->pair->count : 0; - total = pair_session->events_stats.total; + total = pair_hists->stats.total; count_sys = self->pair ? self->pair->count_sys : 0; count_us = self->pair ? self->pair->count_us : 0; count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; @@ -569,7 +563,7 @@ int hist_entry__snprintf(struct hist_entry *self, ret += snprintf(s + ret, size - ret, "%11lld", count); } - if (pair_session) { + if (pair_hists) { char bf[32]; double old_percent = 0, new_percent = 0, diff; @@ -615,14 +609,12 @@ int hist_entry__snprintf(struct hist_entry *self, return ret; } -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); @@ -644,10 +636,8 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, left_margin); } -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total) +size_t hists__fprintf(struct hists *self, struct hists *pair, + bool show_displacement, FILE *fp) { struct sort_entry *se; struct rb_node *nd; @@ -753,7 +743,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fprintf(fp, "\n#\n"); print_entries: - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (show_displacement) { @@ -765,10 +755,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, session_total); + displacement, fp, self->stats.total); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, session_total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index b49013a..bdde81e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -6,34 +6,40 @@ extern struct callchain_param callchain_param; -struct perf_session; struct hist_entry; struct addr_location; struct symbol; struct rb_root; -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *parent, - u64 count); +struct events_stats { + u64 total; + u64 lost; +}; + +struct hists { + struct rb_node rb_node; + struct rb_root entries; + struct events_stats stats; + u64 config; + u64 event_stream; + u32 type; +}; + +struct hist_entry *__hists__add_entry(struct hists *self, + struct addr_location *al, + struct symbol *parent, u64 count); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total); -int hist_entry__snprintf(struct hist_entry *self, - char *bf, size_t size, - struct perf_session *pair_session, - bool show_displacement, long displacement, - bool color, u64 session_total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, + u64 total); +int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, + struct hists *pair_hists, bool show_displacement, + long displacement, bool color, u64 total); void hist_entry__free(struct hist_entry *); -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); -void perf_session__collapse_resort(struct rb_root *hists); -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total); +u64 hists__output_resort(struct hists *self); +void hists__collapse_resort(struct hists *self); +size_t hists__fprintf(struct hists *self, struct hists *pair, + bool show_displacement, FILE *fp); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4130036..72a7f6a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -89,7 +89,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc memcpy(self->filename, filename, len); self->threads = RB_ROOT; - self->stats_by_id = RB_ROOT; + self->hists_tree = RB_ROOT; self->last_match = NULL; self->mmap_window = 32; self->cwd = NULL; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 242d528..46190f9 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,6 +1,7 @@ #ifndef __PERF_SESSION_H #define __PERF_SESSION_H +#include "hist.h" #include "event.h" #include "header.h" #include "symbol.h" @@ -28,11 +29,16 @@ struct perf_session { struct thread *last_match; struct machine host_machine; struct rb_root machines; - struct events_stats events_stats; - struct rb_root stats_by_id; + struct rb_root hists_tree; unsigned long event_total[PERF_RECORD_MAX]; unsigned long unknown_events; - struct rb_root hists; + /* + * FIXME: should point to the first entry in hists_tree and + * be a hists instance. Right now its only 'report' + * that is using ->hists_tree while all the rest use + * ->hists. + */ + struct hists hists; u64 sample_type; int fd; bool fd_pipe; -- cgit v0.10.2 From fefb0b94bbab858be0909a7eb5ef357e0f996a79 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 10 May 2010 13:57:51 -0300 Subject: perf hist: Calculate max_sym name len and nr_entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better done when we are adding entries, be it initially of when we're re-sorting the histograms. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 53077fd..d7c7529 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -296,13 +296,13 @@ static int __cmd_report(void) next = rb_first(&session->hists_tree); while (next) { struct hists *hists; - u64 nr_hists; hists = rb_entry(next, struct hists, rb_node); hists__collapse_resort(hists); - nr_hists = hists__output_resort(hists); + hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, nr_hists, + perf_session__browse_hists(&hists->entries, + hists->nr_entries, hists->stats.total, help, input_name); else { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 410cf56..e34fd24 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -47,6 +47,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) +{ + if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) + self->max_sym_namelen = entry->ms.sym->namelen; + ++self->nr_entries; +} + struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *sym_parent, u64 count) @@ -89,6 +96,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, return NULL; rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, &self->entries); + hists__inc_nr_entries(self, he); out: hist_entry__add_cpumode_count(he, al->cpumode, count); return he; @@ -137,7 +145,7 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) +static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -153,7 +161,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) if (!cmp) { iter->count += he->count; hist_entry__free(he); - return; + return false; } if (cmp < 0) @@ -164,6 +172,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); + return true; } void hists__collapse_resort(struct hists *self) @@ -177,13 +186,16 @@ void hists__collapse_resort(struct hists *self) tmp = RB_ROOT; next = rb_first(&self->entries); + self->nr_entries = 0; + self->max_sym_namelen = 0; while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->entries); - collapse__insert_entry(&tmp, n); + if (collapse__insert_entry(&tmp, n)) + hists__inc_nr_entries(self, n); } self->entries = tmp; @@ -219,30 +231,31 @@ static void __hists__insert_output_entry(struct rb_root *entries, rb_insert_color(&he->rb_node, entries); } -u64 hists__output_resort(struct hists *self) +void hists__output_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; - u64 nr_hists = 0; min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->entries); + self->nr_entries = 0; + self->max_sym_namelen = 0; + while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->entries); __hists__insert_output_entry(&tmp, n, min_callchain_hits); - ++nr_hists; + hists__inc_nr_entries(self, n); } self->entries = tmp; - return nr_hists; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index bdde81e..1b18d04 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -19,10 +19,12 @@ struct events_stats { struct hists { struct rb_node rb_node; struct rb_root entries; + u64 nr_entries; struct events_stats stats; u64 config; u64 event_stream; u32 type; + u32 max_sym_namelen; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -38,7 +40,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, long displacement, bool color, u64 total); void hist_entry__free(struct hist_entry *); -u64 hists__output_resort(struct hists *self); +void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 994efdb..ecccc8d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -130,8 +130,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) if (symbol_conf.priv_size) self = ((void *)self) + symbol_conf.priv_size; - self->start = start; - self->end = len ? start + len - 1 : start; + self->start = start; + self->end = len ? start + len - 1 : start; + self->namelen = namelen - 1; pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index edff866..6389d1a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -54,6 +54,7 @@ struct symbol { struct rb_node rb_node; u64 start; u64 end; + u16 namelen; char name[0]; }; -- cgit v0.10.2 From c3f5fd287aa897f710f3305367a1d256c9cf3e83 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:52 -0500 Subject: perf/trace/scripting: failed-syscalls script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A couple small fixes for the failed syscalls script: - The script description says it can be restricted to a specific comm, make it so. - silence the match output in the shell script Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-2-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report index f634608..e3a5e55 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-report +++ b/tools/perf/scripts/perl/bin/failed-syscalls-report @@ -2,7 +2,7 @@ # description: system-wide failed syscalls # args: [comm] if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then + if ! expr match "$1" "-" > /dev/null ; then comm=$1 shift fi diff --git a/tools/perf/scripts/perl/failed-syscalls.pl b/tools/perf/scripts/perl/failed-syscalls.pl index c18e7e2..94bc25a 100644 --- a/tools/perf/scripts/perl/failed-syscalls.pl +++ b/tools/perf/scripts/perl/failed-syscalls.pl @@ -11,6 +11,8 @@ use Perf::Trace::Core; use Perf::Trace::Context; use Perf::Trace::Util; +my $for_comm = shift; + my %failed_syscalls; sub raw_syscalls::sys_exit @@ -33,6 +35,8 @@ sub trace_end foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}} keys %failed_syscalls) { - printf("%-20s %10s\n", $comm, $failed_syscalls{$comm}); + next if ($for_comm && $comm ne $for_comm); + + printf("%-20s %10s\n", $comm, $failed_syscalls{$comm}); } } -- cgit v0.10.2 From 6922c3d772711239e75ddaea760e6b0535e7e7a6 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:53 -0500 Subject: perf/trace/scripting: rw-by-pid script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some minor fixes for the rw-by-pid script: - Fix nuisance 'use of uninitialized value' warnings - Change the failed read/write sections to sort by error counts Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-3-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl/rw-by-pid.pl index da601fa..9db23c9 100644 --- a/tools/perf/scripts/perl/rw-by-pid.pl +++ b/tools/perf/scripts/perl/rw-by-pid.pl @@ -79,12 +79,12 @@ sub trace_end printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------", "-----------", "----------", "----------"); - foreach my $pid (sort {$reads{$b}{bytes_read} <=> - $reads{$a}{bytes_read}} keys %reads) { - my $comm = $reads{$pid}{comm}; - my $total_reads = $reads{$pid}{total_reads}; - my $bytes_requested = $reads{$pid}{bytes_requested}; - my $bytes_read = $reads{$pid}{bytes_read}; + foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=> + ($reads{$a}{bytes_read} || 0) } keys %reads) { + my $comm = $reads{$pid}{comm} || ""; + my $total_reads = $reads{$pid}{total_reads} || 0; + my $bytes_requested = $reads{$pid}{bytes_requested} || 0; + my $bytes_read = $reads{$pid}{bytes_read} || 0; printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, $total_reads, $bytes_requested, $bytes_read); @@ -96,16 +96,23 @@ sub trace_end printf("%6s %20s %6s %10s\n", "------", "--------------------", "------", "----------"); - foreach my $pid (keys %reads) { - my $comm = $reads{$pid}{comm}; - foreach my $err (sort {$reads{$b}{comm} cmp $reads{$a}{comm}} - keys %{$reads{$pid}{errors}}) { - my $errors = $reads{$pid}{errors}{$err}; + my @errcounts = (); - printf("%6d %-20s %6d %10s\n", $pid, $comm, $err, $errors); + foreach my $pid (keys %reads) { + foreach my $error (keys %{$reads{$pid}{errors}}) { + my $comm = $reads{$pid}{comm} || ""; + my $errcount = $reads{$pid}{errors}{$error} || 0; + push @errcounts, [$pid, $comm, $error, $errcount]; } } + @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts; + + for my $i (0 .. $#errcounts) { + printf("%6d %-20s %6d %10s\n", $errcounts[$i][0], + $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]); + } + printf("\nwrite counts by pid:\n\n"); printf("%6s %20s %10s %10s\n", "pid", "comm", @@ -113,11 +120,11 @@ sub trace_end printf("%6s %-20s %10s %10s\n", "------", "--------------------", "-----------", "----------"); - foreach my $pid (sort {$writes{$b}{bytes_written} <=> - $writes{$a}{bytes_written}} keys %writes) { - my $comm = $writes{$pid}{comm}; - my $total_writes = $writes{$pid}{total_writes}; - my $bytes_written = $writes{$pid}{bytes_written}; + foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=> + ($writes{$a}{bytes_written} || 0)} keys %writes) { + my $comm = $writes{$pid}{comm} || ""; + my $total_writes = $writes{$pid}{total_writes} || 0; + my $bytes_written = $writes{$pid}{bytes_written} || 0; printf("%6s %-20s %10s %10s\n", $pid, $comm, $total_writes, $bytes_written); @@ -129,16 +136,23 @@ sub trace_end printf("%6s %20s %6s %10s\n", "------", "--------------------", "------", "----------"); - foreach my $pid (keys %writes) { - my $comm = $writes{$pid}{comm}; - foreach my $err (sort {$writes{$b}{comm} cmp $writes{$a}{comm}} - keys %{$writes{$pid}{errors}}) { - my $errors = $writes{$pid}{errors}{$err}; + @errcounts = (); - printf("%6d %-20s %6d %10s\n", $pid, $comm, $err, $errors); + foreach my $pid (keys %writes) { + foreach my $error (keys %{$writes{$pid}{errors}}) { + my $comm = $writes{$pid}{comm} || ""; + my $errcount = $writes{$pid}{errors}{$error} || 0; + push @errcounts, [$pid, $comm, $error, $errcount]; } } + @errcounts = sort { $b->[3] <=> $a->[3] } @errcounts; + + for my $i (0 .. $#errcounts) { + printf("%6d %-20s %6d %10s\n", $errcounts[$i][0], + $errcounts[$i][1], $errcounts[$i][2], $errcounts[$i][3]); + } + print_unhandled(); } -- cgit v0.10.2 From e88a4bfbcda440b1c6b9d5a31a554a6ad9686182 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:54 -0500 Subject: perf/trace/scripting: rwtop script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A couple of fixes for the rwtop script: - printing the totals and clearing the hashes in the signal handler eventually leads to various random and serious problems when running the rwtop script continuously. Moving the print_totals() calls to the event handlers solves that problem, and the event handlers are invoked frequently enough that it doesn't affect the timeliness of the output. - Fix nuisance 'use of uninitialized value' warnings Cc: Frédéric Weisbecker Cc: Ingo Molnar Message-Id: <1273466820-9330-4-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl index ec2ab49..4bb3ecd 100644 --- a/tools/perf/scripts/perl/rwtop.pl +++ b/tools/perf/scripts/perl/rwtop.pl @@ -21,6 +21,7 @@ use Perf::Trace::Util; my $default_interval = 3; my $nlines = 20; my $print_thread; +my $print_pending = 0; my %reads; my %writes; @@ -36,6 +37,8 @@ sub syscalls::sys_exit_read $common_pid, $common_comm, $nr, $ret) = @_; + print_check(); + if ($ret > 0) { $reads{$common_pid}{bytes_read} += $ret; } else { @@ -52,6 +55,8 @@ sub syscalls::sys_enter_read $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_; + print_check(); + $reads{$common_pid}{bytes_requested} += $count; $reads{$common_pid}{total_reads}++; $reads{$common_pid}{comm} = $common_comm; @@ -63,6 +68,8 @@ sub syscalls::sys_exit_write $common_pid, $common_comm, $nr, $ret) = @_; + print_check(); + if ($ret <= 0) { $writes{$common_pid}{errors}{$ret}++; } @@ -74,6 +81,8 @@ sub syscalls::sys_enter_write $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_; + print_check(); + $writes{$common_pid}{bytes_written} += $count; $writes{$common_pid}{total_writes}++; $writes{$common_pid}{comm} = $common_comm; @@ -81,7 +90,7 @@ sub syscalls::sys_enter_write sub trace_begin { - $SIG{ALRM} = \&print_totals; + $SIG{ALRM} = \&set_print_pending; alarm 1; } @@ -91,6 +100,20 @@ sub trace_end print_totals(); } +sub print_check() +{ + if ($print_pending == 1) { + $print_pending = 0; + print_totals(); + } +} + +sub set_print_pending() +{ + $print_pending = 1; + alarm $interval; +} + sub print_totals { my $count; @@ -106,12 +129,12 @@ sub print_totals printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------", "----------", "----------", "----------"); - foreach my $pid (sort {$reads{$b}{bytes_read} <=> - $reads{$a}{bytes_read}} keys %reads) { - my $comm = $reads{$pid}{comm}; - my $total_reads = $reads{$pid}{total_reads}; - my $bytes_requested = $reads{$pid}{bytes_requested}; - my $bytes_read = $reads{$pid}{bytes_read}; + foreach my $pid (sort { ($reads{$b}{bytes_read} || 0) <=> + ($reads{$a}{bytes_read} || 0) } keys %reads) { + my $comm = $reads{$pid}{comm} || ""; + my $total_reads = $reads{$pid}{total_reads} || 0; + my $bytes_requested = $reads{$pid}{bytes_requested} || 0; + my $bytes_read = $reads{$pid}{bytes_read} || 0; printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, $total_reads, $bytes_requested, $bytes_read); @@ -130,11 +153,11 @@ sub print_totals printf("%6s %-20s %10s %13s\n", "------", "--------------------", "----------", "-------------"); - foreach my $pid (sort {$writes{$b}{bytes_written} <=> - $writes{$a}{bytes_written}} keys %writes) { - my $comm = $writes{$pid}{comm}; - my $total_writes = $writes{$pid}{total_writes}; - my $bytes_written = $writes{$pid}{bytes_written}; + foreach my $pid (sort { ($writes{$b}{bytes_written} || 0) <=> + ($writes{$a}{bytes_written} || 0)} keys %writes) { + my $comm = $writes{$pid}{comm} || ""; + my $total_writes = $writes{$pid}{total_writes} || 0; + my $bytes_written = $writes{$pid}{bytes_written} || 0; printf("%6s %-20s %10s %13s\n", $pid, $comm, $total_writes, $bytes_written); @@ -146,7 +169,6 @@ sub print_totals %reads = (); %writes = (); - alarm $interval; } my %unhandled; -- cgit v0.10.2 From e366728d57cb8c708f76b282ae194c6044355b5f Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:55 -0500 Subject: perf/trace/scripting: wakeup-latency script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some minor fixes for the wakeup-latency script: - Fix nuisance 'use of uninitialized value' warnings - Avoid divide-by-zero error Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-5-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts/perl/wakeup-latency.pl index ed58ef2..d9143dc 100644 --- a/tools/perf/scripts/perl/wakeup-latency.pl +++ b/tools/perf/scripts/perl/wakeup-latency.pl @@ -22,8 +22,8 @@ my %last_wakeup; my $max_wakeup_latency; my $min_wakeup_latency; -my $total_wakeup_latency; -my $total_wakeups; +my $total_wakeup_latency = 0; +my $total_wakeups = 0; sub sched::sched_switch { @@ -67,8 +67,12 @@ sub trace_end { printf("wakeup_latency stats:\n\n"); print "total_wakeups: $total_wakeups\n"; - printf("avg_wakeup_latency (ns): %u\n", - avg($total_wakeup_latency, $total_wakeups)); + if ($total_wakeups) { + printf("avg_wakeup_latency (ns): %u\n", + avg($total_wakeup_latency, $total_wakeups)); + } else { + printf("avg_wakeup_latency (ns): N/A\n"); + } printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency); printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency); -- cgit v0.10.2 From a3412d9b358d37fce4527fd67ea601635f2b9496 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:56 -0500 Subject: perf/trace/scripting: workqueue-stats script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some minor fixes for the workqueue-stats script: - Fix nuisance 'use of uninitialized value' warnings Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-6-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl index 511302c..b84b126 100644 --- a/tools/perf/scripts/perl/workqueue-stats.pl +++ b/tools/perf/scripts/perl/workqueue-stats.pl @@ -71,9 +71,9 @@ sub trace_end printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----"); foreach my $pidhash (@cpus) { while ((my $pid, my $wqhash) = each %$pidhash) { - my $ins = $$wqhash{'inserted'}; - my $exe = $$wqhash{'executed'}; - my $comm = $$wqhash{'comm'}; + my $ins = $$wqhash{'inserted'} || 0; + my $exe = $$wqhash{'executed'} || 0; + my $comm = $$wqhash{'comm'} || ""; if ($ins || $exe) { printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm); } @@ -87,9 +87,9 @@ sub trace_end printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----"); foreach my $pidhash (@cpus) { while ((my $pid, my $wqhash) = each %$pidhash) { - my $created = $$wqhash{'created'}; - my $destroyed = $$wqhash{'destroyed'}; - my $comm = $$wqhash{'comm'}; + my $created = $$wqhash{'created'} || 0; + my $destroyed = $$wqhash{'destroyed'} || 0; + my $comm = $$wqhash{'comm'} || ""; if ($created || $destroyed) { printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed, $comm); -- cgit v0.10.2 From 3824a4e8da9791f4eed99d69bfcdb3b42f440426 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:57 -0500 Subject: perf/trace/scripting: don't show script start/stop messages by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only print the script start/stop messages in verbose mode - users normally don't care and it just clutters up the output. Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-7-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6e268ca..95fcb05 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -53,6 +53,8 @@ static void setup_scripting(void) static int cleanup_scripting(void) { + pr_debug("\nperf trace script stopped\n"); + return scripting_ops->stop_script(); } @@ -703,6 +705,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) err = scripting_ops->start_script(script_name, argc, argv); if (err) goto out; + pr_debug("perf trace started with script %s\n\n", script_name); } err = __cmd_trace(session); diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 5376378..b059dc5 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -371,7 +371,6 @@ static int perl_start_script(const char *script, int argc, const char **argv) run_start_sub(); free(command_line); - fprintf(stderr, "perf trace started with Perl script %s\n\n", script); return 0; error: perl_free(my_perl); @@ -394,8 +393,6 @@ static int perl_stop_script(void) perl_destruct(my_perl); perl_free(my_perl); - fprintf(stderr, "\nperf trace Perl script stopped\n"); - return 0; } diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 6a72f14..81f39ca 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -374,8 +374,6 @@ static int python_start_script(const char *script, int argc, const char **argv) } free(command_line); - fprintf(stderr, "perf trace started with Python script %s\n\n", - script); return err; error: @@ -407,8 +405,6 @@ out: Py_XDECREF(main_module); Py_Finalize(); - fprintf(stderr, "\nperf trace Python script stopped\n"); - return err; } -- cgit v0.10.2 From a4ab0c12975d1286b2696370f5e0576450609bf0 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:58 -0500 Subject: perf/trace/scripting: failed-syscalls-by-pid script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small fixe for the failed syscalls by pid script: - silence the match output in the shell script Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-8-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report index 8c128ef..3029354 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report @@ -2,7 +2,7 @@ # description: system-wide failed syscalls, by pid # args: [comm] if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then + if ! expr match "$1" "-" > /dev/null ; then comm=$1 shift fi -- cgit v0.10.2 From 79e653f1bf2e52d12a952366e782dadf590b9d1d Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:46:59 -0500 Subject: perf/trace/scripting: syscall-counts-by-pid script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small fix for the syscall counts by pid script: - silence the match output in the shell script Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-9-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report index c53362e..9e9d8dd 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report @@ -2,7 +2,7 @@ # description: system-wide syscall counts, by pid # args: [comm] if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then + if ! expr match "$1" "-" > /dev/null ; then comm=$1 shift fi -- cgit v0.10.2 From e61a639a794063d78fd248a37ce2c21d5c81fc19 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 9 May 2010 23:47:00 -0500 Subject: perf/trace/scripting: syscall-counts script cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small fix for the syscall counts script: - silence the match output in the shell script Cc: Frédéric Weisbecker Cc: Ingo Molnar LKML-Reference: <1273466820-9330-10-git-send-email-tzanussi@gmail.com> Signed-off-by: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report index 8c21552..dc076b6 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-report +++ b/tools/perf/scripts/python/bin/syscall-counts-report @@ -2,7 +2,7 @@ # description: system-wide syscall counts # args: [comm] if [ $# -gt 0 ] ; then - if ! expr match "$1" "-" ; then + if ! expr match "$1" "-" > /dev/null ; then comm=$1 shift fi -- cgit v0.10.2 From e3174cfd2a1e28fff774681f00a0eef3d31da970 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 11 May 2010 08:31:49 +0200 Subject: Revert "perf: Fix exit() vs PERF_FORMAT_GROUP" This reverts commit 4fd38e4595e2f6c9d27732c042a0e16b2753049c. It causes various crashes and hangs when events are activated. The cause is not fully understood yet but we need to revert it because the effects are severe. Reported-by: Stephane Eranian Reported-by: Lin Ming Cc: Peter Zijlstra Cc: Corey Ashford Cc: Mike Galbraith Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 4924c96..3fd5c82 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -575,7 +575,6 @@ struct pmu { * enum perf_event_active_state - the states of a event */ enum perf_event_active_state { - PERF_EVENT_STATE_FREE = -3, PERF_EVENT_STATE_ERROR = -2, PERF_EVENT_STATE_OFF = -1, PERF_EVENT_STATE_INACTIVE = 0, diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 180151f..a904746 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -334,9 +334,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) if (event->state > PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_OFF; - if (event->state > PERF_EVENT_STATE_FREE) - return; - /* * If this was a group event with sibling events then * upgrade the siblings to singleton events by adding them @@ -1871,8 +1868,6 @@ int perf_event_release_kernel(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; - event->state = PERF_EVENT_STATE_FREE; - WARN_ON_ONCE(ctx->parent_ctx); /* * There are two ways this annotation is useful: -- cgit v0.10.2 From 050735b08ca8a016bbace4445fa025b88fee770b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 11 May 2010 11:51:53 +0200 Subject: perf: Fix exit() vs PERF_FORMAT_GROUP Both Stephane and Corey reported that PERF_FORMAT_GROUP didn't work as expected if the task the counters were attached to quit before the read() call. The cause is that we unconditionally destroy the grouping when we remove counters from their context. Fix this by splitting off the group destroy from the list removal such that perf_event_remove_from_context() does not do this and change perf_event_release() to do so. Reported-by: Corey Ashford Reported-by: Stephane Eranian Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: # .34.x LKML-Reference: <1273571513.5605.3527.camel@twins> Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index a904746..c97e825 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -308,8 +308,6 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) static void list_del_event(struct perf_event *event, struct perf_event_context *ctx) { - struct perf_event *sibling, *tmp; - if (list_empty(&event->group_entry)) return; ctx->nr_events--; @@ -333,6 +331,12 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) */ if (event->state > PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_OFF; +} + +static void +perf_destroy_group(struct perf_event *event, struct perf_event_context *ctx) +{ + struct perf_event *sibling, *tmp; /* * If this was a group event with sibling events then @@ -1868,6 +1872,12 @@ int perf_event_release_kernel(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; + /* + * Remove from the PMU, can't get re-enabled since we got + * here because the last ref went. + */ + perf_event_disable(event); + WARN_ON_ONCE(ctx->parent_ctx); /* * There are two ways this annotation is useful: @@ -1882,7 +1892,10 @@ int perf_event_release_kernel(struct perf_event *event) * to trigger the AB-BA case. */ mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); - perf_event_remove_from_context(event); + raw_spin_lock_irq(&ctx->lock); + list_del_event(event, ctx); + perf_destroy_group(event, ctx); + raw_spin_unlock_irq(&ctx->lock); mutex_unlock(&ctx->mutex); mutex_lock(&event->owner->perf_event_mutex); -- cgit v0.10.2 From 96c21a460a37880abfbc8445d5b098dbab958a29 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 11 May 2010 16:19:10 +0200 Subject: perf: Fix exit() vs event-groups Corey reported that the value scale times of group siblings are not updated when the monitored task dies. The problem appears to be that we only update the group leader's time values, fix it by updating the whole group. Reported-by: Corey Ashford Signed-off-by: Peter Zijlstra Cc: Paul Mackerras Cc: Mike Galbraith Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: # .34.x LKML-Reference: <1273588935.1810.6.camel@laptop> Signed-off-by: Ingo Molnar diff --git a/kernel/perf_event.c b/kernel/perf_event.c index c97e825..a4fa381 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -255,6 +255,18 @@ static void update_event_times(struct perf_event *event) event->total_time_running = run_end - event->tstamp_running; } +/* + * Update total_time_enabled and total_time_running for all events in a group. + */ +static void update_group_times(struct perf_event *leader) +{ + struct perf_event *event; + + update_event_times(leader); + list_for_each_entry(event, &leader->sibling_list, group_entry) + update_event_times(event); +} + static struct list_head * ctx_group_list(struct perf_event *event, struct perf_event_context *ctx) { @@ -320,7 +332,7 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx) if (event->group_leader != event) event->group_leader->nr_siblings--; - update_event_times(event); + update_group_times(event); /* * If event was in error state, then keep it @@ -502,18 +514,6 @@ retry: } /* - * Update total_time_enabled and total_time_running for all events in a group. - */ -static void update_group_times(struct perf_event *leader) -{ - struct perf_event *event; - - update_event_times(leader); - list_for_each_entry(event, &leader->sibling_list, group_entry) - update_event_times(event); -} - -/* * Cross CPU call to disable a performance event */ static void __perf_event_disable(void *info) -- cgit v0.10.2 From 8e6d5573af55435160d329f6ae3fe16a0abbdaec Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Sat, 8 May 2010 20:28:41 +1000 Subject: perf, powerpc: Implement group scheduling transactional APIs [paulus@samba.org: Set cpuhw->event[i]->hw.config in power_pmu_commit_txn.] Signed-off-by: Lin Ming Signed-off-by: Paul Mackerras Signed-off-by: Peter Zijlstra LKML-Reference: <20100508102841.GA10650@brick.ozlabs.ibm.com> Signed-off-by: Ingo Molnar diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index 08460a2..43b83c3 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -35,6 +35,9 @@ struct cpu_hw_events { u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + + unsigned int group_flag; + int n_txn_start; }; DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); @@ -718,66 +721,6 @@ static int collect_events(struct perf_event *group, int max_count, return n; } -static void event_sched_in(struct perf_event *event) -{ - event->state = PERF_EVENT_STATE_ACTIVE; - event->oncpu = smp_processor_id(); - event->tstamp_running += event->ctx->time - event->tstamp_stopped; - if (is_software_event(event)) - event->pmu->enable(event); -} - -/* - * Called to enable a whole group of events. - * Returns 1 if the group was enabled, or -EAGAIN if it could not be. - * Assumes the caller has disabled interrupts and has - * frozen the PMU with hw_perf_save_disable. - */ -int hw_perf_group_sched_in(struct perf_event *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx) -{ - struct cpu_hw_events *cpuhw; - long i, n, n0; - struct perf_event *sub; - - if (!ppmu) - return 0; - cpuhw = &__get_cpu_var(cpu_hw_events); - n0 = cpuhw->n_events; - n = collect_events(group_leader, ppmu->n_counter - n0, - &cpuhw->event[n0], &cpuhw->events[n0], - &cpuhw->flags[n0]); - if (n < 0) - return -EAGAIN; - if (check_excludes(cpuhw->event, cpuhw->flags, n0, n)) - return -EAGAIN; - i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n + n0); - if (i < 0) - return -EAGAIN; - cpuhw->n_events = n0 + n; - cpuhw->n_added += n; - - /* - * OK, this group can go on; update event states etc., - * and enable any software events - */ - for (i = n0; i < n0 + n; ++i) - cpuhw->event[i]->hw.config = cpuhw->events[i]; - cpuctx->active_oncpu += n; - n = 1; - event_sched_in(group_leader); - list_for_each_entry(sub, &group_leader->sibling_list, group_entry) { - if (sub->state != PERF_EVENT_STATE_OFF) { - event_sched_in(sub); - ++n; - } - } - ctx->nr_active += n; - - return 1; -} - /* * Add a event to the PMU. * If all events are not already frozen, then we disable and @@ -805,12 +748,22 @@ static int power_pmu_enable(struct perf_event *event) cpuhw->event[n0] = event; cpuhw->events[n0] = event->hw.config; cpuhw->flags[n0] = event->hw.event_base; + + /* + * If group events scheduling transaction was started, + * skip the schedulability test here, it will be peformed + * at commit time(->commit_txn) as a whole + */ + if (cpuhw->group_flag & PERF_EVENT_TXN_STARTED) + goto nocheck; + if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1)) goto out; if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1)) goto out; - event->hw.config = cpuhw->events[n0]; + +nocheck: ++cpuhw->n_events; ++cpuhw->n_added; @@ -896,11 +849,65 @@ static void power_pmu_unthrottle(struct perf_event *event) local_irq_restore(flags); } +/* + * Start group events scheduling transaction + * Set the flag to make pmu::enable() not perform the + * schedulability test, it will be performed at commit time + */ +void power_pmu_start_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + + cpuhw->group_flag |= PERF_EVENT_TXN_STARTED; + cpuhw->n_txn_start = cpuhw->n_events; +} + +/* + * Stop group events scheduling transaction + * Clear the flag and pmu::enable() will perform the + * schedulability test. + */ +void power_pmu_cancel_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + + cpuhw->group_flag &= ~PERF_EVENT_TXN_STARTED; +} + +/* + * Commit group events scheduling transaction + * Perform the group schedulability test as a whole + * Return 0 if success + */ +int power_pmu_commit_txn(const struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw; + long i, n; + + if (!ppmu) + return -EAGAIN; + cpuhw = &__get_cpu_var(cpu_hw_events); + n = cpuhw->n_events; + if (check_excludes(cpuhw->event, cpuhw->flags, 0, n)) + return -EAGAIN; + i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n); + if (i < 0) + return -EAGAIN; + + for (i = cpuhw->n_txn_start; i < n; ++i) + cpuhw->event[i]->hw.config = cpuhw->events[i]; + + return 0; +} + struct pmu power_pmu = { .enable = power_pmu_enable, .disable = power_pmu_disable, .read = power_pmu_read, .unthrottle = power_pmu_unthrottle, + .start_txn = power_pmu_start_txn, + .cancel_txn = power_pmu_cancel_txn, + .commit_txn = power_pmu_commit_txn, }; /* -- cgit v0.10.2 From b09e0190acf88c7fe3b05e3c331e1b2ef5310896 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 11:10:15 -0300 Subject: perf hist: Adopt filter by dso and by thread methods from the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those are really not specific to the newt code, can be used by other UI frontends. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7c7529..3d67d6b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -301,10 +301,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, - hists->nr_entries, - hists->stats.total, help, - input_name); + hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e34fd24..baa55be 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -784,3 +784,62 @@ print_entries: return ret; } + +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + +void hists__filter_by_dso(struct hists *self, const struct dso *dso) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (symbol_conf.exclude_other && !h->parent) + continue; + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered |= (1 << HIST_FILTER__DSO); + continue; + } + + h->filtered &= ~(1 << HIST_FILTER__DSO); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +void hists__filter_by_thread(struct hists *self, const struct thread *thread) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1b18d04..1c5f93a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -44,4 +44,19 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); + +void hists__filter_by_dso(struct hists *self, const struct dso *dso); +void hists__filter_by_thread(struct hists *self, const struct thread *thread); + +#ifdef NO_NEWT_SUPPORT +static inline int hists__browse(struct hists self __used, + const char *helpline __used, + const char *input_name __used) +{ + return 0; +} +#else +int hists__browse(struct hists *self, const char *helpline, + const char *input_name); +#endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e283a6e..638b519 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -410,8 +410,8 @@ static void hist_browser__delete(struct hist_browser *self) free(self); } -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) +static int hist_browser__populate(struct hist_browser *self, struct hists *hists, + const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -426,7 +426,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); + hists->stats.total); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -442,24 +442,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtComponentAddCallback(self->tree, hist_browser__selection, &self->selection); - progress = ui_progress__new("Adding entries to the browser...", nr_hists); + progress = ui_progress__new("Adding entries to the browser...", + hists->nr_entries); if (progress == NULL) return -1; idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); int len; if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, session_total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); + hists->stats.total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -490,57 +491,6 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - static struct thread *hist_browser__selected_thread(struct hist_browser *self) { int *indexes; @@ -577,9 +527,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, return printed ?: snprintf(bf, size, "Report: %s", input_name); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) +int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); const struct thread *thread_filter = NULL; @@ -595,7 +543,7 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; while (1) { @@ -672,10 +620,10 @@ do_annotate: newtPushHelpLine(msg); dso_filter = dso; } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { if (thread_filter) { @@ -689,10 +637,10 @@ do_annotate: newtPushHelpLine(msg); thread_filter = thread; } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 46190f9..ce00fa6 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -102,21 +102,6 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) -{ - return 0; -} -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif - static inline struct machine *perf_session__find_host_machine(struct perf_session *self) { -- cgit v0.10.2 From 6b3c4ef50441e85dc9b2c9b67e95e8ad1185c15e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 11 May 2010 00:59:53 -0400 Subject: perf probe: Check older elfutils and set NO_DWARF Check whether elfutils is older than 0.138 (from which version checking routine has been introduced). And if so, set NO_DWARF because it is hard to check the API dependency without version checking. Signed-off-by: Masami Hiramatsu Reported-by: Robert Richter Cc: Robert Richter Cc: Ingo Molnar LKML-Reference: <20100511045953.9913.19485.stgit@localhost6.localdomain6> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0ef5cfe..b28bb73 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE) -include config.mak ifndef NO_DWARF -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); +ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo '\#include '; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) + msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support endif # NO_DWARF -- cgit v0.10.2 From d11c7addfe0fa501cb54c824c0fac3481d527433 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Mon, 10 May 2010 16:43:35 -0400 Subject: perf symbols: allow forcing use of cplus_demangle For Fedora, I want to force perf to link against libiberty.a for cplus_demangle, rather than libbfd.a for bfd_demangle due to licensing insanity on binutils. (libiberty is LGPL2, libbfd is GPL3.) If we just rely on autodetection, we'll end up with libbfd linked against us, since they're both in binutils-static in the buildroot. Cc: Ingo Molnar LKML-Reference: <20100510204335.GA7565@bombadil.infradead.org> Signed-off-by: Kyle McMartin Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index b28bb73..0797786 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -592,6 +592,9 @@ endif ifdef NO_DEMANGLE BASIC_CFLAGS += -DNO_DEMANGLE +else ifdef HAVE_CPLUS_DEMANGLE + EXTLIBS += -liberty + BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else has_bfd := $(shell sh -c "(echo '\#include '; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") -- cgit v0.10.2 From 3798ed7bc7ade26d3f59506cd06288615dfc7585 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 18:01:23 -0300 Subject: perf ui: Add ui_helpline methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initially this was just to be able to have a printf like method to prepare the formatted string and then pass to newtPushHelpLine, but as we already have for ui_progress, etc, its a step in identifying a restricted, highlevel set of widgets we can then have implementations for multiple widget sets (GTK, etc). Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 638b519..daa86ef 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -57,6 +57,43 @@ void ui_progress__delete(struct ui_progress *self) free(self); } +static void ui_helpline__pop(void) +{ + newtPopHelpLine(); +} + +static void ui_helpline__push(const char *msg) +{ + newtPushHelpLine(msg); +} + +static void ui_helpline__vpush(const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) < 0) + vfprintf(stderr, fmt, ap); + else { + ui_helpline__push(s); + free(s); + } +} + +static void ui_helpline__fpush(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ui_helpline__vpush(fmt, ap); + va_end(ap); +} + +static void ui_helpline__puts(const char *msg) +{ + ui_helpline__pop(); + ui_helpline__push(msg); +} + static char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -69,8 +106,7 @@ int browser__show_help(const char *format, va_list ap) backlog += ret; if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); + ui_helpline__puts(browser__last_msg); newtRefresh(); backlog = 0; } @@ -340,7 +376,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, if (fp == NULL) goto out_free_str; - newtPushHelpLine("Press ESC to exit"); + ui_helpline__push("Press ESC to exit"); newtGetScreenSize(&cols, &rows); tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); @@ -370,7 +406,7 @@ static void map_symbol__annotate_browser(const struct map_symbol *self, newtFormRun(form, &es); newtFormDestroy(form); newtPopWindow(); - newtPopHelpLine(); + ui_helpline__pop(); out_free_str: free(str); } @@ -539,7 +575,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (browser == NULL) return -1; - newtPushHelpLine(helpline); + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); @@ -602,8 +638,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na do_annotate: if (choice == annotate) { if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - newtPopHelpLine(); - newtPushHelpLine("No vmlinux file found, can't " + ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; @@ -611,13 +646,11 @@ do_annotate: map_symbol__annotate_browser(browser->selection, input_name); } else if (choice == zoom_dso) { if (dso_filter) { - newtPopHelpLine(); + ui_helpline__pop(); dso_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; } hists__filter_by_dso(self, dso_filter); @@ -627,14 +660,12 @@ do_annotate: goto out; } else if (choice == zoom_thread) { if (thread_filter) { - newtPopHelpLine(); + ui_helpline__pop(); thread_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s(%d) thread\"", - (thread->comm_set ? thread->comm : ""), - thread->pid); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); thread_filter = thread; } hists__filter_by_thread(self, thread_filter); @@ -658,7 +689,7 @@ void setup_browser(void) use_browser = true; newtInit(); newtCls(); - newtPushHelpLine(" "); + ui_helpline__puts(" "); } void exit_browser(bool wait_for_ok) -- cgit v0.10.2 From ef7b93a11904c6ba10604233d318d9e8ec88cddc Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 May 2010 23:18:06 -0300 Subject: perf report: Librarize the annotation code and use it in the newt browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we don't anymore use popen to run 'perf annotate' for the selected symbol, instead we collect per address samplings when processing samples in 'perf report' if we're using the newt browser, then we use this data directly to do annotation. Done this way we can actually traverse the objdump_line objects directly, matching the addresses to the collected samples and colouring them appropriately using lower level slang routines. The new ui_browser class will be reused for the main, callchain aware, histogram browser, when it will be made generic and don't assume that the objects are always instances of the objdump_line class maintained using list_heads. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0797786..9c4dc30 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif @@ -948,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< +$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964..fd1b786 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; -struct sym_hist { - u64 sum; - u64 ip[0]; -}; - -struct sym_ext { - struct rb_node node; - double percent; - char *path; -}; - -struct sym_priv { - struct sym_hist *hist; - struct sym_ext *ext; -}; - static const char *sym_hist_filter; -static int sym__alloc_hist(struct symbol *self) -{ - struct sym_priv *priv = symbol__priv(self); - const int size = (sizeof(*priv->hist) + - (self->end - self->start) * sizeof(u64)); - - priv->hist = zalloc(size); - return priv->hist == NULL ? -1 : 0; -} - -/* - * collect histogram counts - */ -static int annotate__hist_hit(struct hist_entry *he, u64 ip) -{ - unsigned int sym_size, offset; - struct symbol *sym = he->ms.sym; - struct sym_priv *priv; - struct sym_hist *h; - - if (!sym || !he->ms.map) - return 0; - - priv = symbol__priv(sym); - if (priv->hist == NULL && sym__alloc_hist(sym) < 0) - return -ENOMEM; - - sym_size = sym->end - sym->start; - offset = ip - sym->start; - - pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); - - if (offset >= sym_size) - return 0; - - h = priv->hist; - h->sum++; - h->ip[offset]++; - - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, - he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); - return 0; -} - static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; - return annotate__hist_hit(he, al->addr); + return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -struct objdump_line { - struct list_head node; - s64 offset; - char *line; -}; - -static struct objdump_line *objdump_line__new(s64 offset, char *line) -{ - struct objdump_line *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->offset = offset; - self->line = line; - } - - return self; -} - -static void objdump_line__free(struct objdump_line *self) -{ - free(self->line); - free(self); -} - -static void objdump__add_line(struct list_head *head, struct objdump_line *line) -{ - list_add_tail(&line->node, head); -} - -static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) -{ - list_for_each_entry_continue(pos, head, node) - if (pos->offset >= 0) - return pos; - - return NULL; -} - -static int parse_line(FILE *file, struct hist_entry *he, - struct list_head *head) -{ - struct symbol *sym = he->ms.sym; - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2; - size_t line_len; - s64 line_ip, offset = -1; - char *c; - - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - c = strchr(line, '\n'); - if (c) - *c = 0; - - line_ip = -1; - - /* - * Strip leading spaces: - */ - tmp = line; - while (*tmp) { - if (*tmp != ' ') - break; - tmp++; - } - - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':') - line_ip = -1; - } - - if (line_ip != -1) { - u64 start = map__rip_2objdump(he->ms.map, sym->start); - offset = line_ip - start; - } - - objdump_line = objdump_line__new(offset, line); - if (objdump_line == NULL) { - free(line); - return -1; - } - objdump__add_line(head, objdump_line); - - return 0; -} - static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) @@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; - char command[PATH_MAX*2]; - FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; - if (!filename) - return; - - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return; - dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file was found in the " - "path:\n", sym->name); - vmlinux_path__fprintf(stderr); + if (hist_entry__annotate(he, &head) < 0) return; - } - - pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, - filename, sym->name, map->unmap_ip(map, sym->start), - map->unmap_ip(map, sym->end)); if (full_paths) d_filename = filename; @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); - if (verbose >= 2) - printf("annotating [%p] %30s : [%p] %30s\n", - dso, dso->long_name, sym, sym->name); - - sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", - map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end), - filename, filename); - - if (verbose >= 3) - printf("doing: %s\n", command); - - file = popen(command, "r"); - if (!file) - return; - - while (!feof(file)) { - if (parse_line(file, he, &head) < 0) - break; - } - - pclose(file); - if (verbose) hist_entry__print_hits(he); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3d67d6b..04de338 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; - if (symbol_conf.use_callchain) + if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); + if (err) + goto out_free_syms; + } + /* + * Only in the newt browser we are doing integrated annotation, + * so we don't allocated the extra space needed because the stdio + * code will not use it. + */ + if (use_browser) + err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; @@ -458,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); + /* + * Only in the newt browser we are doing integrated annotation, + * so don't allocate extra space that won't be used in the stdio + * implementation. + */ + if (use_browser) + symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index baa55be..451d2e4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -843,3 +843,187 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) } } } + +static int symbol__alloc_hist(struct symbol *self) +{ + struct sym_priv *priv = symbol__priv(self); + const int size = (sizeof(*priv->hist) + + (self->end - self->start) * sizeof(u64)); + + priv->hist = zalloc(size); + return priv->hist == NULL ? -1 : 0; +} + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) +{ + unsigned int sym_size, offset; + struct symbol *sym = self->ms.sym; + struct sym_priv *priv; + struct sym_hist *h; + + if (!sym || !self->ms.map) + return 0; + + priv = symbol__priv(sym); + if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) + return -ENOMEM; + + sym_size = sym->end - sym->start; + offset = ip - sym->start; + + pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + + if (offset >= sym_size) + return 0; + + h = priv->hist; + h->sum++; + h->ip[offset]++; + + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line) +{ + struct objdump_line *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, + struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(self->ms.map, sym->start); + offset = line_ip - start; + } + + objdump_line = objdump_line__new(offset, line); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(head, objdump_line); + + return 0; +} + +int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct map *map = self->ms.map; + struct dso *dso = map->dso; + const char *filename = dso->long_name; + char command[PATH_MAX * 2]; + FILE *file; + u64 len; + + if (!filename) + return -1; + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return 0; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path:\n", sym->name); + vmlinux_path__fprintf(stderr); + return -1; + } + + pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, + filename, sym->name, map->unmap_ip(map, sym->start), + map->unmap_ip(map, sym->end)); + + len = sym->end - sym->start; + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + return -1; + + while (!feof(file)) + if (hist_entry__parse_objdump_line(self, file, head) < 0) + break; + + pclose(file); + return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1c5f93a..ed9c067 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 ip[0]; +}; + +struct sym_ext { + struct rb_node node; + double percent; + char *path; +}; + +struct sym_priv { + struct sym_hist *hist; + struct sym_ext *ext; +}; + struct events_stats { u64 total; u64 lost; @@ -45,6 +71,9 @@ void hists__collapse_resort(struct hists *self); size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head); + void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index daa86ef..ba6acd0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -2,6 +2,7 @@ #include #undef _GNU_SOURCE +#include #include #include #include @@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +static int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + u32 nr_entries; +}; + +static void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +static void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; +} + +static int objdump_line__show(struct objdump_line *self, struct list_head *head, + int width, struct hist_entry *he, int len, + bool current_entry) +{ + if (self->offset != -1) { + struct symbol *sym = he->ms.sym; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + SLsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + SLsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + SLsmg_write_nstring(" ", 8); + if (!*self->line) + SLsmg_write_nstring(" ", width - 18); + else + SLsmg_write_nstring(self->line, width - 18); + + return 0; +} + +static int ui_browser__refresh_entries(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = (self->first_visible_entry_idx + row) == self->index; + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +static int ui_browser__run(struct ui_browser *self, const char *title, + struct newtExitStruct *es) +{ + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + + if (ui_browser__refresh_entries(self) < 0) + return -1; + newtFormAddComponent(self->form, self->sb); + + while (1) { + unsigned int offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + struct list_head *pos = self->first_visible_entry; + ++self->first_visible_entry_idx; + self->first_visible_entry = pos->next; + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + struct list_head *pos = self->first_visible_entry; + --self->first_visible_entry_idx; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_PGDN: + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->next; + } + + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: { + struct list_head *head = self->entries; + offset = self->height - 1; + + if (offset > self->nr_entries) + offset = self->nr_entries; + + self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->first_visible_entry = head->prev; + while (offset-- != 0) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + } + break; + case NEWT_KEY_ESCAPE: + case CTRL('c'): + case 'Q': + case 'q': + return 0; + default: + continue; + } + if (ui_browser__refresh_entries(self) < 0) + return -1; + } + return 0; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) +static void hist_entry__annotate_browser(struct hist_entry *self) { - FILE *fp; - int cols, rows; - newtComponent form, tree; + struct ui_browser browser; struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; + struct objdump_line *pos, *n; + LIST_HEAD(head); - if (self->sym == NULL) + if (self->ms.sym == NULL) return; - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) + if (hist_entry__annotate(self, &head) < 0) return; - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - ui_helpline__push("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); + memset(&browser, 0, sizeof(browser)); + browser.entries = &head; + browser.priv = self; + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; } - fclose(fp); - free(line); - - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - - newtListboxSetWidth(tree, max_line_len); - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponent(form, tree); - - newtFormRun(form, &es); - newtFormDestroy(form); + browser.width += 18; /* Percentage */ + ui_browser__run(&browser, self->ms.sym->name, &es); + newtFormDestroy(browser.form); newtPopWindow(); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } ui_helpline__pop(); -out_free_str: - free(str); } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists return 0; } -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { int *indexes; @@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } return NULL; out: - return *(struct thread **)(self->selection + 1); + return container_of(self->selection, struct hist_entry, ms); +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + struct hist_entry *he = hist_browser__selected_entry(self); + return he ? he->thread : NULL; } static int hist_browser__title(char *bf, size_t size, const char *input_name, @@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na continue; do_annotate: if (choice == annotate) { + struct hist_entry *he; + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, input_name); + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { ui_helpline__pop(); @@ -681,8 +921,23 @@ out: return err; } +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + void setup_browser(void) { + struct newtPercentTreeColors *c = &defaultPercentTreeColors; if (!isatty(1)) return; @@ -690,6 +945,11 @@ void setup_browser(void) newtInit(); newtCls(); ui_helpline__puts(" "); + SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54ee..af301ac 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,12 +48,6 @@ struct hist_entry { u64 count_us; u64 count_guest_sys; u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ struct map_symbol ms; struct thread *thread; u64 ip; -- cgit v0.10.2 From 720019908fd5a1bb442bb0a35a6027ba21864d25 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 12 May 2010 21:42:42 +0400 Subject: x86, perf: P4 PMU -- use hash for p4_get_escr_idx() Linear search over all p4 MSRs should be fine if only we would not use it in events scheduling routine which is pretty time critical. Lets use hashes. It should speed scheduling up significantly. v2: Steven proposed to use more gentle approach than issue BUG on error, so we use WARN_ONCE now Signed-off-by: Cyrill Gorcunov Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Lin Ming LKML-Reference: <20100512174242.GA5190@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index a603930..cb875b1 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -668,66 +668,80 @@ static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu) } } -/* ESCRs are not sequential in memory so we need a map */ -static const unsigned int p4_escr_map[ARCH_P4_TOTAL_ESCR] = { - MSR_P4_ALF_ESCR0, /* 0 */ - MSR_P4_ALF_ESCR1, /* 1 */ - MSR_P4_BPU_ESCR0, /* 2 */ - MSR_P4_BPU_ESCR1, /* 3 */ - MSR_P4_BSU_ESCR0, /* 4 */ - MSR_P4_BSU_ESCR1, /* 5 */ - MSR_P4_CRU_ESCR0, /* 6 */ - MSR_P4_CRU_ESCR1, /* 7 */ - MSR_P4_CRU_ESCR2, /* 8 */ - MSR_P4_CRU_ESCR3, /* 9 */ - MSR_P4_CRU_ESCR4, /* 10 */ - MSR_P4_CRU_ESCR5, /* 11 */ - MSR_P4_DAC_ESCR0, /* 12 */ - MSR_P4_DAC_ESCR1, /* 13 */ - MSR_P4_FIRM_ESCR0, /* 14 */ - MSR_P4_FIRM_ESCR1, /* 15 */ - MSR_P4_FLAME_ESCR0, /* 16 */ - MSR_P4_FLAME_ESCR1, /* 17 */ - MSR_P4_FSB_ESCR0, /* 18 */ - MSR_P4_FSB_ESCR1, /* 19 */ - MSR_P4_IQ_ESCR0, /* 20 */ - MSR_P4_IQ_ESCR1, /* 21 */ - MSR_P4_IS_ESCR0, /* 22 */ - MSR_P4_IS_ESCR1, /* 23 */ - MSR_P4_ITLB_ESCR0, /* 24 */ - MSR_P4_ITLB_ESCR1, /* 25 */ - MSR_P4_IX_ESCR0, /* 26 */ - MSR_P4_IX_ESCR1, /* 27 */ - MSR_P4_MOB_ESCR0, /* 28 */ - MSR_P4_MOB_ESCR1, /* 29 */ - MSR_P4_MS_ESCR0, /* 30 */ - MSR_P4_MS_ESCR1, /* 31 */ - MSR_P4_PMH_ESCR0, /* 32 */ - MSR_P4_PMH_ESCR1, /* 33 */ - MSR_P4_RAT_ESCR0, /* 34 */ - MSR_P4_RAT_ESCR1, /* 35 */ - MSR_P4_SAAT_ESCR0, /* 36 */ - MSR_P4_SAAT_ESCR1, /* 37 */ - MSR_P4_SSU_ESCR0, /* 38 */ - MSR_P4_SSU_ESCR1, /* 39 */ - MSR_P4_TBPU_ESCR0, /* 40 */ - MSR_P4_TBPU_ESCR1, /* 41 */ - MSR_P4_TC_ESCR0, /* 42 */ - MSR_P4_TC_ESCR1, /* 43 */ - MSR_P4_U2L_ESCR0, /* 44 */ - MSR_P4_U2L_ESCR1, /* 45 */ +/* + * ESCR address hashing is tricky, ESCRs are not sequential + * in memory but all starts from MSR_P4_BSU_ESCR0 (0x03e0) and + * the metric between any ESCRs is laid in range [0xa0,0xe1] + * + * so we make ~70% filled hashtable + */ + +#define P4_ESCR_MSR_BASE 0x000003a0 +#define P4_ESCR_MSR_MAX 0x000003e1 +#define P4_ESCR_MSR_TABLE_SIZE (P4_ESCR_MSR_MAX - P4_ESCR_MSR_BASE + 1) +#define P4_ESCR_MSR_IDX(msr) (msr - P4_ESCR_MSR_BASE) +#define P4_ESCR_MSR_TABLE_ENTRY(msr) [P4_ESCR_MSR_IDX(msr)] = msr + +static const unsigned int p4_escr_table[P4_ESCR_MSR_TABLE_SIZE] = { + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ALF_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ALF_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BPU_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BPU_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BSU_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_BSU_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR2), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR3), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR4), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_CRU_ESCR5), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_DAC_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_DAC_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FIRM_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FIRM_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FLAME_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FLAME_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FSB_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_FSB_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IQ_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IQ_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IS_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IS_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ITLB_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_ITLB_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IX_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_IX_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MOB_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MOB_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MS_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_MS_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_PMH_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_PMH_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_RAT_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_RAT_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SAAT_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SAAT_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SSU_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_SSU_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TBPU_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TBPU_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TC_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_TC_ESCR1), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_U2L_ESCR0), + P4_ESCR_MSR_TABLE_ENTRY(MSR_P4_U2L_ESCR1), }; static int p4_get_escr_idx(unsigned int addr) { - unsigned int i; + unsigned int idx = P4_ESCR_MSR_IDX(addr); - for (i = 0; i < ARRAY_SIZE(p4_escr_map); i++) { - if (addr == p4_escr_map[i]) - return i; + if (unlikely(idx >= P4_ESCR_MSR_TABLE_SIZE || + !p4_escr_table[idx])) { + WARN_ONCE(1, "P4 PMU: Wrong address passed: %x\n", addr); + return -1; } - return -1; + return idx; } static int p4_next_cntr(int thread, unsigned long *used_mask, @@ -747,7 +761,7 @@ static int p4_next_cntr(int thread, unsigned long *used_mask, static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) { unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; - unsigned long escr_mask[BITS_TO_LONGS(ARCH_P4_TOTAL_ESCR)]; + unsigned long escr_mask[BITS_TO_LONGS(P4_ESCR_MSR_TABLE_SIZE)]; int cpu = raw_smp_processor_id(); struct hw_perf_event *hwc; struct p4_event_bind *bind; @@ -755,7 +769,7 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign int cntr_idx, escr_idx; bitmap_zero(used_mask, X86_PMC_IDX_MAX); - bitmap_zero(escr_mask, ARCH_P4_TOTAL_ESCR); + bitmap_zero(escr_mask, P4_ESCR_MSR_TABLE_SIZE); for (i = 0, num = n; i < n; i++, num--) { @@ -763,6 +777,8 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign thread = p4_ht_thread(cpu); bind = p4_config_get_bind(hwc->config); escr_idx = p4_get_escr_idx(bind->escr_msr[thread]); + if (unlikely(escr_idx == -1)) + goto done; if (hwc->idx != -1 && !p4_should_swap_ts(hwc->config, cpu)) { cntr_idx = hwc->idx; -- cgit v0.10.2 From 8769e1c7177fd9f6981042bcc6c2851c99a4a7e7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 13 May 2010 19:22:58 +0200 Subject: perf hist: Fix hists__browse no-newt case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix mistake in a parameter type of the no-newt hists__browse() version. Fixes: builtin-report.c: In function ‘__cmd_report’: builtin-report.c:314: erreur: incompatible type for argument 1 of ‘hists__browse’ Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras LKML-Reference: <1273771378-8577-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ed9c067..0b4c8df 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -78,7 +78,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); #ifdef NO_NEWT_SUPPORT -static inline int hists__browse(struct hists self __used, +static inline int hists__browse(struct hists *self __used, const char *helpline __used, const char *input_name __used) { -- cgit v0.10.2 From 8a0ecfb8b47dc765fdf460913231876bbc95385e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 13 May 2010 19:47:16 +0200 Subject: perf hist: Fix missing getline declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hist.c needs to include util.h so that it gets stdio.h inclusion with __GNU_SOURCE defined. Fixes: util/hist.c: In function ‘hist_entry__parse_objdump_line’: util/hist.c:931: erreur: implicit declaration of function ‘getline’ util/hist.c:931: erreur: nested extern declaration of ‘getline’ Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras LKML-Reference: <1273772836-11533-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 451d2e4..5dc4f84 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,3 +1,4 @@ +#include "util.h" #include "hist.h" #include "session.h" #include "sort.h" -- cgit v0.10.2 From 2e6cdf996ba43ce0b090ffbf754f83e17362cd69 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 12 May 2010 10:40:01 +0200 Subject: perf tools: change event inheritance logic in stat and record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, event inheritance across fork and pthread_create was on but the -i option of stat and record, which enabled inheritance, led to believe it was off by default. This patch fixes this logic by inverting the meaning of the -i option. By default inheritance is on whether you attach to a process (-p), a thread (-t) or start a process. If you pass -i, then you turn off inheritance. Turning off inheritance if you don't need it, helps limit perf resource usage as well. The patch also fixes perf stat -t xxxx and perf record -t xxxx which did not start the counters. Acked-by: Frederic Weisbecker Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <4bea9d2f.d60ce30a.0b5b.08e1@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 020d871..34e255f 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -69,8 +69,8 @@ OPTIONS Output file name. -i:: ---inherit:: - Child tasks inherit counters. +--no-inherit:: + Child tasks do not inherit counters. -F:: --freq=:: Profile at this frequency. diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 484080d..2cab8e8 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -31,8 +31,8 @@ OPTIONS hexadecimal event descriptor. -i:: ---inherit:: - child tasks inherit counters +--no-inherit:: + child tasks do not inherit counters -p:: --pid=:: stat events on existing pid diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 6b77b28..0f467cf 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -54,7 +54,7 @@ static pid_t target_tid = -1; static pid_t *all_tids = NULL; static int thread_num = 0; static pid_t child_pid = -1; -static bool inherit = true; +static bool no_inherit = false; static enum write_mode_t write_mode = WRITE_FORCE; static bool call_graph = false; static bool inherit_stat = false; @@ -298,8 +298,8 @@ static void create_counter(int counter, int cpu) attr->mmap = track; attr->comm = track; - attr->inherit = inherit; - if (target_pid == -1 && !system_wide) { + attr->inherit = !no_inherit; + if (target_pid == -1 && target_tid == -1 && !system_wide) { attr->disabled = 1; attr->enable_on_exec = 1; } @@ -641,7 +641,7 @@ static int __cmd_record(int argc, const char **argv) close(child_ready_pipe[0]); } - if ((!system_wide && !inherit) || profile_cpu != -1) { + if ((!system_wide && no_inherit) || profile_cpu != -1) { open_counters(profile_cpu); } else { nr_cpus = read_cpu_map(); @@ -821,8 +821,8 @@ static const struct option options[] = { "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), - OPT_BOOLEAN('i', "inherit", &inherit, - "child tasks inherit counters"), + OPT_BOOLEAN('i', "no-inherit", &no_inherit, + "child tasks do not inherit counters"), OPT_INTEGER('F', "freq", &user_freq, "profile at this frequency"), OPT_INTEGER('m', "mmap-pages", &mmap_pages, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e619ac8..ff8c413 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -72,7 +72,7 @@ static unsigned int nr_cpus = 0; static int run_idx = 0; static int run_count = 1; -static bool inherit = true; +static bool no_inherit = false; static bool scale = true; static pid_t target_pid = -1; static pid_t target_tid = -1; @@ -167,8 +167,8 @@ static int create_perf_stat_counter(int counter) ++ncreated; } } else { - attr->inherit = inherit; - if (target_pid == -1) { + attr->inherit = !no_inherit; + if (target_pid == -1 && target_tid == -1) { attr->disabled = 1; attr->enable_on_exec = 1; } @@ -518,8 +518,8 @@ static const struct option options[] = { OPT_CALLBACK('e', "event", NULL, "event", "event selector. use 'perf list' to list available events", parse_events), - OPT_BOOLEAN('i', "inherit", &inherit, - "child tasks inherit counters"), + OPT_BOOLEAN('i', "no-inherit", &no_inherit, + "child tasks do not inherit counters"), OPT_INTEGER('p', "pid", &target_pid, "stat events on existing process id"), OPT_INTEGER('t', "tid", &target_tid, -- cgit v0.10.2 From 5d2be7cb198a0a6bc6088d3806fb7261b184ad89 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Thu, 13 May 2010 14:39:25 +0400 Subject: perf trace scripts: Fix typos in perf-trace-python.txt option option -> option special special -> special Signed-off-by: Kirill Smelkov Cc: Tom Zanussi LKML-Reference: <1273747165-17242-1-git-send-email-kirr@mns.spb.ru> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-trace-python.txt index 864aac2..693be80 100644 --- a/tools/perf/Documentation/perf-trace-python.txt +++ b/tools/perf/Documentation/perf-trace-python.txt @@ -182,7 +182,7 @@ mean either that the record step recorded event types that it wasn't really interested in, or the script was run against a trace file that doesn't correspond to the script. -The script generated by -g option option simply prints a line for each +The script generated by -g option simply prints a line for each event found in the trace stream i.e. it basically just dumps the event and its parameter values to stdout. The print_header() function is simply a utility function used for that purpose. Let's rename the @@ -582,7 +582,7 @@ files: flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name -The *autodict* function returns a special special kind of Python +The *autodict* function returns a special kind of Python dictionary that implements Perl's 'autovivifying' hashes in Python i.e. with autovivifying hashes, you can assign nested hash values without having to go to the trouble of creating intermediate levels if -- cgit v0.10.2 From c8446b9bdabcb0caa61bb341bd73c58f7104b503 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 10:36:42 -0300 Subject: perf hist: Make event__totals per hists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is one more thing that started global but are more useful per hist or per session. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index fd1b786..77bcc9b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -365,7 +365,7 @@ static int __cmd_annotate(void) goto out_delete; if (dump_trace) { - event__print_totals(); + perf_session__fprintf_nr_events(session, stdout); goto out_delete; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 04de338..f13cda1 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -139,6 +139,12 @@ static int add_event_total(struct perf_session *session, return -ENOMEM; hists->stats.total += data->period; + /* + * FIXME: add_event_total should be moved from here to + * perf_session__process_event so that the proper hist is passed to + * the event_op methods. + */ + hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); session->hists.stats.total += data->period; return 0; } @@ -293,7 +299,7 @@ static int __cmd_report(void) goto out_delete; if (dump_trace) { - event__print_totals(); + perf_session__fprintf_nr_events(session, stdout); goto out_delete; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index cce006e..3e8fec1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,6 +7,23 @@ #include "strlist.h" #include "thread.h" +const char *event__name[] = { + [0] = "TOTAL", + [PERF_RECORD_MMAP] = "MMAP", + [PERF_RECORD_LOST] = "LOST", + [PERF_RECORD_COMM] = "COMM", + [PERF_RECORD_EXIT] = "EXIT", + [PERF_RECORD_THROTTLE] = "THROTTLE", + [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", + [PERF_RECORD_FORK] = "FORK", + [PERF_RECORD_READ] = "READ", + [PERF_RECORD_SAMPLE] = "SAMPLE", + [PERF_RECORD_HEADER_ATTR] = "ATTR", + [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", + [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", + [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", +}; + static pid_t event__synthesize_comm(pid_t pid, int full, event__handler_t process, struct perf_session *session) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 48c2cc9..8577085 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -160,4 +160,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, struct addr_location *al, symbol_filter_t filter); int event__parse_sample(event_t *event, u64 type, struct sample_data *data); +extern const char *event__name[]; + #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5dc4f84..1614ad7 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1028,3 +1028,24 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) pclose(file); return 0; } + +void hists__inc_nr_events(struct hists *self, u32 type) +{ + ++self->hists.stats.nr_events[0]; + ++self->hists.stats.nr_events[type]; +} + +size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) +{ + int i; + size_t ret = 0; + + for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { + if (!event__name[i]) + continue; + ret += fprintf(fp, "%10s events: %10d\n", + event__name[i], self->stats.nr_events[i]); + } + + return ret; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 0b4c8df..97b8962 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -40,6 +40,8 @@ struct sym_priv { struct events_stats { u64 total; u64 lost; + u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_unknown_events; }; struct hists { @@ -68,6 +70,10 @@ void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); + +void hists__inc_nr_events(struct hists *self, u32 type); +size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); + size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72a7f6a..7231f6b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -94,7 +94,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->mmap_window = 32; self->cwd = NULL; self->cwdlen = 0; - self->unknown_events = 0; self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples_head); @@ -241,36 +240,6 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) } } -static const char *event__name[] = { - [0] = "TOTAL", - [PERF_RECORD_MMAP] = "MMAP", - [PERF_RECORD_LOST] = "LOST", - [PERF_RECORD_COMM] = "COMM", - [PERF_RECORD_EXIT] = "EXIT", - [PERF_RECORD_THROTTLE] = "THROTTLE", - [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", - [PERF_RECORD_FORK] = "FORK", - [PERF_RECORD_READ] = "READ", - [PERF_RECORD_SAMPLE] = "SAMPLE", - [PERF_RECORD_HEADER_ATTR] = "ATTR", - [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", - [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", - [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", -}; - -unsigned long event__total[PERF_RECORD_HEADER_MAX]; - -void event__print_totals(void) -{ - int i; - for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - if (!event__name[i]) - continue; - pr_info("%10s events: %10ld\n", - event__name[i], event__total[i]); - } -} - void mem_bswap_64(void *src, int byte_size) { u64 *m = src; @@ -580,8 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - ++event__total[0]; - ++event__total[event->header.type]; + hists__inc_nr_events(self, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) @@ -619,7 +587,7 @@ static int perf_session__process_event(struct perf_session *self, case PERF_RECORD_FINISHED_ROUND: return ops->finished_round(event, self, ops); default: - self->unknown_events++; + ++self->hists.stats.nr_unknown_events; return -1; } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index ce00fa6..e7fce48 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -30,8 +30,6 @@ struct perf_session { struct machine host_machine; struct rb_root machines; struct rb_root hists_tree; - unsigned long event_total[PERF_RECORD_MAX]; - unsigned long unknown_events; /* * FIXME: should point to the first entry in hists_tree and * be a hists instance. Right now its only 'report' @@ -140,4 +138,10 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, { return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); } + +static inline +size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) +{ + return hists__fprintf_nr_events(&self->hists, fp); +} #endif /* __PERF_SESSION_H */ -- cgit v0.10.2 From cee75ac7ecc27084accdb9d9d6fde65a09f047ae Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 13:16:55 -0300 Subject: perf hist: Clarify events_stats fields usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The events_stats.total field is too generic, rename it to .total_period, and also add a comment explaining that it is the sum of all the .period fields in samples, that is needed because we use auto-freq to avoid sampling artifacts. Ditto for events_stats.lost, that is the sum of all lost_event.lost fields, i.e. the number of events the kernel dropped. Looking at the users, builtin-sched.c can make use of these fields and stop doing it again. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3a95a02..6dd4bda 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -54,7 +54,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi return -1; } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f13cda1..b8f47de 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -138,14 +138,14 @@ static int add_event_total(struct perf_session *session, if (!hists) return -ENOMEM; - hists->stats.total += data->period; + hists->stats.total_period += data->period; /* * FIXME: add_event_total should be moved from here to * perf_session__process_event so that the proper hist is passed to * the event_op methods. */ hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); - session->hists.stats.total += data->period; + session->hists.stats.total_period += data->period; return 0; } @@ -322,10 +322,10 @@ static int __cmd_report(void) if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total); + hists->stats.total_period); else fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total, + hists->stats.total_period, __event_name(hists->type, hists->config)); hists__fprintf(hists, NULL, false, stdout); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index aef6ed0..be7bc92 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1641,19 +1641,10 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -static int process_lost_event(event_t *event __used, - struct perf_session *session __used) -{ - nr_lost_chunks++; - nr_lost_events += event->lost.lost; - - return 0; -} - static struct perf_event_ops event_ops = { .sample = process_sample_event, .comm = event__process_comm, - .lost = process_lost_event, + .lost = event__process_lost, .ordered_samples = true, }; @@ -1664,8 +1655,12 @@ static int read_events(void) if (session == NULL) return -ENOMEM; - if (perf_session__has_traces(session, "record -R")) + if (perf_session__has_traces(session, "record -R")) { err = perf_session__process_events(session, &event_ops); + nr_events = session->hists.stats.nr_events[0]; + nr_lost_events = session->hists.stats.total_lost; + nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; + } perf_session__delete(session); return err; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 95fcb05..dddf3f0 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -109,7 +109,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) data.time, thread->comm); } - session->hists.stats.total += data.period; + session->hists.stats.total_period += data.period; return 0; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3e8fec1..50771b5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -385,7 +385,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->hists.stats.lost += self->lost.lost; + session->hists.stats.total_lost += self->lost.lost; return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1614ad7..c592245 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -239,7 +239,7 @@ void hists__output_resort(struct hists *self) struct hist_entry *n; u64 min_callchain_hits; - min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->entries); @@ -525,7 +525,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, if (pair_hists) { count = self->pair ? self->pair->count : 0; - total = pair_hists->stats.total; + total = pair_hists->stats.total_period; count_sys = self->pair ? self->pair->count_sys : 0; count_us = self->pair ? self->pair->count_us : 0; count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; @@ -769,10 +769,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, self->stats.total); + displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, @@ -795,7 +795,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +812,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -824,7 +824,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) { struct rb_node *nd; - self->nr_entries = self->stats.total = 0; + self->nr_entries = self->stats.total_period = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +837,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total += h->count; + self->stats.total_period += h->count; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -1031,8 +1031,8 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head) void hists__inc_nr_events(struct hists *self, u32 type) { - ++self->hists.stats.nr_events[0]; - ++self->hists.stats.nr_events[type]; + ++self->stats.nr_events[0]; + ++self->stats.nr_events[type]; } size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 97b8962..da6b848 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -37,9 +37,21 @@ struct sym_priv { struct sym_ext *ext; }; +/* + * The kernel collects the number of events it couldn't send in a stretch and + * when possible sends this number in a PERF_RECORD_LOST event. The number of + * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while + * total_lost tells exactly how many events the kernel in fact lost, i.e. it is + * the sum of all struct lost_event.lost fields reported. + * + * The total_period is needed because by default auto-freq is used, so + * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get + * the total number of low level events, it is necessary to to sum all struct + * sample_event.period and stash the result in total_period. + */ struct events_stats { - u64 total; - u64 lost; + u64 total_period; + u64 total_lost; u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_unknown_events; }; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index ba6acd0..010bacf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -689,7 +689,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists } snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total); + hists->stats.total_period); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -718,12 +718,12 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total, idx++); + hists->stats.total_period, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7231f6b..25bfca4 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -549,7 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - hists__inc_nr_events(self, event->header.type); + hists__inc_nr_events(&self->hists, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) -- cgit v0.10.2 From c82ee828aa20487d254a5225d256cd422acee459 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 14:19:35 -0300 Subject: perf report: Report number of events, not samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Number of samples is meaningless after we switched to auto-freq, so report the number of events, i.e. not the sum of the different periods, but the number PERF_RECORD_SAMPLE emitted by the kernel. While doing this I noticed that naming "count" to the sum of all the event periods can be confusing, so rename it to .period, just like in struct sample.data, so that we become more consistent. This helps with the next step, that was to record in struct hist_entry the number of sample events for each instance, we need that because we use it to generate the number of events when applying filters to the tree of hist entries like it is being done in the TUI report browser. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 6dd4bda..a6e2fdc 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -23,9 +23,9 @@ static bool force; static bool show_displacement; static int hists__add_entry(struct hists *self, - struct addr_location *al, u64 count) + struct addr_location *al, u64 period) { - if (__hists__add_entry(self, al, NULL, count) != NULL) + if (__hists__add_entry(self, al, NULL, period) != NULL) return 0; return -ENOMEM; } @@ -50,7 +50,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi event__parse_sample(event, session->sample_type, &data); if (hists__add_entry(&session->hists, &al, data.period)) { - pr_warning("problem incrementing symbol count, skipping event\n"); + pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b8f47de..6826512 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -188,14 +188,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; if (perf_session__add_hist_entry(session, &al, &data)) { - pr_debug("problem incrementing symbol count, skipping event\n"); + pr_debug("problem incrementing symbol period, skipping event\n"); return -1; } attr = perf_header__find_attr(data.id, &session->header); if (add_event_total(session, &data, attr)) { - pr_debug("problem adding event count\n"); + pr_debug("problem adding event period\n"); return -1; } @@ -269,11 +269,25 @@ static struct perf_event_ops event_ops = { extern volatile int session_done; -static void sig_handler(int sig __attribute__((__unused__))) +static void sig_handler(int sig __used) { session_done = 1; } +static size_t hists__fprintf_nr_sample_events(struct hists *self, + const char *evname, FILE *fp) +{ + size_t ret; + char unit; + unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; + + nr_events = convert_unit(nr_events, &unit); + ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); + if (evname != NULL) + ret += fprintf(fp, " %s", evname); + return ret + fprintf(fp, "\n#\n"); +} + static int __cmd_report(void) { int ret = -EINVAL; @@ -319,14 +333,12 @@ static int __cmd_report(void) if (use_browser) hists__browse(hists, help, input_name); else { - if (rb_first(&session->hists.entries) == + const char *evname = NULL; + if (rb_first(&session->hists.entries) != rb_last(&session->hists.entries)) - fprintf(stdout, "# Samples: %Ld\n#\n", - hists->stats.total_period); - else - fprintf(stdout, "# Samples: %Ld %s\n#\n", - hists->stats.total_period, - __event_name(hists->type, hists->config)); + evname = __event_name(hists->type, hists->config); + + hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf(hists, NULL, false, stdout); fprintf(stdout, "\n\n"); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c592245..f75c5f6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -9,21 +9,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -static void hist_entry__add_cpumode_count(struct hist_entry *self, - unsigned int cpumode, u64 count) +static void hist_entry__add_cpumode_period(struct hist_entry *self, + unsigned int cpumode, u64 period) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - self->count_sys += count; + self->period_sys += period; break; case PERF_RECORD_MISC_USER: - self->count_us += count; + self->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - self->count_guest_sys += count; + self->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - self->count_guest_us += count; + self->period_guest_us += period; break; default: break; @@ -31,7 +31,7 @@ static void hist_entry__add_cpumode_count(struct hist_entry *self, } /* - * histogram, sorted on item, collects counts + * histogram, sorted on item, collects periods */ static struct hist_entry *hist_entry__new(struct hist_entry *template) @@ -41,6 +41,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) if (self != NULL) { *self = *template; + self->nr_events = 1; if (symbol_conf.use_callchain) callchain_init(self->callchain); } @@ -57,7 +58,7 @@ static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *sym_parent, u64 count) + struct symbol *sym_parent, u64 period) { struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; @@ -70,7 +71,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, }, .ip = al->addr, .level = al->level, - .count = count, + .period = period, .parent = sym_parent, }; int cmp; @@ -82,7 +83,8 @@ struct hist_entry *__hists__add_entry(struct hists *self, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - he->count += count; + he->period += period; + ++he->nr_events; goto out; } @@ -99,7 +101,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, rb_insert_color(&he->rb_node, &self->entries); hists__inc_nr_entries(self, he); out: - hist_entry__add_cpumode_count(he, al->cpumode, count); + hist_entry__add_cpumode_period(he, al->cpumode, period); return he; } @@ -160,7 +162,7 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) cmp = hist_entry__collapse(iter, he); if (!cmp) { - iter->count += he->count; + iter->period += he->period; hist_entry__free(he); return false; } @@ -203,7 +205,7 @@ void hists__collapse_resort(struct hists *self) } /* - * reverse the map, sort on count. + * reverse the map, sort on period. */ static void __hists__insert_output_entry(struct rb_root *entries, @@ -222,7 +224,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (he->count > iter->count) + if (he->period > iter->period) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -288,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, } static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, - int depth, int depth_mask, int count, + int depth, int depth_mask, int period, u64 total_samples, int hits, int left_margin) { @@ -301,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, ret += fprintf(fp, "|"); else ret += fprintf(fp, " "); - if (!count && i == depth - 1) { + if (!period && i == depth - 1) { double percent; percent = hits * 100.0 / total_samples; @@ -516,7 +518,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, long displacement, bool color, u64 session_total) { struct sort_entry *se; - u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; + u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; const char *sep = symbol_conf.field_sep; int ret; @@ -524,57 +526,57 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, return 0; if (pair_hists) { - count = self->pair ? self->pair->count : 0; + period = self->pair ? self->pair->period : 0; total = pair_hists->stats.total_period; - count_sys = self->pair ? self->pair->count_sys : 0; - count_us = self->pair ? self->pair->count_us : 0; - count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; - count_guest_us = self->pair ? self->pair->count_guest_us : 0; + period_sys = self->pair ? self->pair->period_sys : 0; + period_us = self->pair ? self->pair->period_us : 0; + period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; + period_guest_us = self->pair ? self->pair->period_guest_us : 0; } else { - count = self->count; + period = self->period; total = session_total; - count_sys = self->count_sys; - count_us = self->count_us; - count_guest_sys = self->count_guest_sys; - count_guest_us = self->count_guest_us; + period_sys = self->period_sys; + period_us = self->period_us; + period_guest_sys = self->period_guest_sys; + period_guest_us = self->period_guest_us; } if (total) { if (color) ret = percent_color_snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); if (symbol_conf.show_cpu_utilization) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_sys * 100.0) / total); + (period_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_us * 100.0) / total); + (period_us * 100.0) / total); if (perf_guest) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_sys * 100.0) / + (period_guest_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_us * 100.0) / + (period_guest_us * 100.0) / total); } } } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); else - ret += snprintf(s + ret, size - ret, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", period); } if (pair_hists) { @@ -582,9 +584,9 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, double old_percent = 0, new_percent = 0, diff; if (total > 0) - old_percent = (count * 100.0) / total; + old_percent = (period * 100.0) / total; if (session_total > 0) - new_percent = (self->count * 100.0) / session_total; + new_percent = (self->period * 100.0) / session_total; diff = new_percent - old_percent; @@ -796,6 +798,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -812,7 +815,8 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) h->filtered &= ~(1 << HIST_FILTER__DSO); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -825,6 +829,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) struct rb_node *nd; self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; self->max_sym_namelen = 0; for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { @@ -837,7 +842,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) h->filtered &= ~(1 << HIST_FILTER__THREAD); if (!h->filtered) { ++self->nr_entries; - self->stats.total_period += h->count; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) self->max_sym_namelen = h->ms.sym->namelen; @@ -881,7 +887,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index da6b848..6f17dcd 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -69,7 +69,7 @@ struct hists { struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, - struct symbol *parent, u64 count); + struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 010bacf..3402453 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -680,16 +680,18 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; - char seq[] = "."; + char seq[] = ".", unit; char str[256]; + unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; if (self->form) { newtFormDestroy(self->form); newtPopWindow(); } - snprintf(str, sizeof(str), "Samples: %Ld ", - hists->stats.total_period); + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index af301ac..eab2e0b3 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -43,14 +43,15 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; - u64 count; - u64 count_sys; - u64 count_us; - u64 count_guest_sys; - u64 count_guest_us; + u64 period; + u64 period_sys; + u64 period_us; + u64 period_guest_sys; + u64 period_guest_us; struct map_symbol ms; struct thread *thread; u64 ip; + u32 nr_events; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index f9b890f..2142656 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -92,3 +92,25 @@ out_close_from: out: return err; } + +unsigned long convert_unit(unsigned long value, char *unit) +{ + *unit = ' '; + + if (value > 1000) { + value /= 1000; + *unit = 'K'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'M'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'G'; + } + + return value; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fbf45d1..0795bf30 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -423,6 +423,7 @@ char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); +unsigned long convert_unit(unsigned long value, char *unit); #define _STR(x) #x #define STR(x) _STR(x) -- cgit v0.10.2 From 3e1bbdc3a721f4b1ed44f4554402a8dbc60fa97f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 14 May 2010 20:05:21 -0300 Subject: perf newt: Make <- zoom out filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After we use the filters to zoom into DSOs or threads, we can use <- (left arrow) to zoom out from the last filter applied. It is still possible to zoom out of order by using the popup menu. With this we now have the zoom out operation on the browsing fast path, by allowing fast navigation using just the four arrors and the enter key to expand collapse callchains. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9c4dc30..a9281cc 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -416,6 +416,7 @@ LIB_H += util/thread.h LIB_H += util/trace-event.h LIB_H += util/probe-finder.h LIB_H += util/probe-event.h +LIB_H += util/pstack.h LIB_H += util/cpumap.h LIB_OBJS += $(OUTPUT)util/abspath.o @@ -451,6 +452,7 @@ LIB_OBJS += $(OUTPUT)util/callchain.o LIB_OBJS += $(OUTPUT)util/values.o LIB_OBJS += $(OUTPUT)util/debug.o LIB_OBJS += $(OUTPUT)util/map.o +LIB_OBJS += $(OUTPUT)util/pstack.o LIB_OBJS += $(OUTPUT)util/session.o LIB_OBJS += $(OUTPUT)util/thread.o LIB_OBJS += $(OUTPUT)util/trace-event-parse.o diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 3402453..e74df12 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -9,6 +9,7 @@ #include "cache.h" #include "hist.h" +#include "pstack.h" #include "session.h" #include "sort.h" #include "symbol.h" @@ -750,6 +751,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); @@ -801,6 +803,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); + struct pstack *fstack = pstack__new(2); const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; struct newtExitStruct es; @@ -810,12 +813,16 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (browser == NULL) return -1; + fstack = pstack__new(2); + if (fstack == NULL) + goto out; + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); if (hist_browser__populate(browser, self, msg) < 0) - goto out; + goto out_free_stack; while (1) { const struct thread *thread; @@ -836,6 +843,19 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na else continue; } + + if (es.u.key == NEWT_KEY_LEFT) { + const void *top; + + if (pstack__empty(fstack)) + continue; + top = pstack__pop(fstack); + if (top == &dso_filter) + goto zoom_out_dso; + if (top == &thread_filter) + goto zoom_out_thread; + continue; + } } if (browser->selection->sym != NULL && @@ -888,12 +908,15 @@ do_annotate: hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { + pstack__remove(fstack, &dso_filter); +zoom_out_dso: ui_helpline__pop(); dso_filter = NULL; } else { - ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; + pstack__push(fstack, &dso_filter); } hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, @@ -902,13 +925,16 @@ do_annotate: goto out; } else if (choice == zoom_thread) { if (thread_filter) { + pstack__remove(fstack, &thread_filter); +zoom_out_thread: ui_helpline__pop(); thread_filter = NULL; } else { - ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", thread->comm_set ? thread->comm : "", thread->pid); thread_filter = thread; + pstack__push(fstack, &thread_filter); } hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, @@ -918,6 +944,8 @@ do_annotate: } } err = 0; +out_free_stack: + pstack__delete(fstack); out: hist_browser__delete(browser); return err; diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c new file mode 100644 index 0000000..13d36fa --- /dev/null +++ b/tools/perf/util/pstack.c @@ -0,0 +1,75 @@ +/* + * Simple pointer stack + * + * (c) 2010 Arnaldo Carvalho de Melo + */ + +#include "util.h" +#include "pstack.h" +#include +#include + +struct pstack { + unsigned short top; + unsigned short max_nr_entries; + void *entries[0]; +}; + +struct pstack *pstack__new(unsigned short max_nr_entries) +{ + struct pstack *self = zalloc((sizeof(*self) + + max_nr_entries * sizeof(void *))); + if (self != NULL) + self->max_nr_entries = max_nr_entries; + return self; +} + +void pstack__delete(struct pstack *self) +{ + free(self); +} + +bool pstack__empty(const struct pstack *self) +{ + return self->top == 0; +} + +void pstack__remove(struct pstack *self, void *key) +{ + unsigned short i = self->top, last_index = self->top - 1; + + while (i-- != 0) { + if (self->entries[i] == key) { + if (i < last_index) + memmove(self->entries + i, + self->entries + i + 1, + (last_index - i) * sizeof(void *)); + --self->top; + return; + } + } + pr_err("%s: %p not on the pstack!\n", __func__, key); +} + +void pstack__push(struct pstack *self, void *key) +{ + if (self->top == self->max_nr_entries) { + pr_err("%s: top=%d, overflow!\n", __func__, self->top); + return; + } + self->entries[self->top++] = key; +} + +void *pstack__pop(struct pstack *self) +{ + void *ret; + + if (self->top == 0) { + pr_err("%s: underflow!\n", __func__); + return NULL; + } + + ret = self->entries[--self->top]; + self->entries[self->top] = NULL; + return ret; +} diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h new file mode 100644 index 0000000..5ad0702 --- /dev/null +++ b/tools/perf/util/pstack.h @@ -0,0 +1,12 @@ +#ifndef _PERF_PSTACK_ +#define _PERF_PSTACK_ + +struct pstack; +struct pstack *pstack__new(unsigned short max_nr_entries); +void pstack__delete(struct pstack *self); +bool pstack__empty(const struct pstack *self); +void pstack__remove(struct pstack *self, void *key); +void pstack__push(struct pstack *self, void *key); +void *pstack__pop(struct pstack *self); + +#endif /* _PERF_PSTACK_ */ -- cgit v0.10.2 From 1ff3d7d79204612ebe2e611d2592f8898908ca00 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Fri, 14 May 2010 23:08:15 +0400 Subject: x86, perf: P4 PMU - fix counters management logic Jaswinder reported this #GP: | | Message from syslogd@ht at May 14 09:39:32 ... | kernel:[ 314.908612] EIP: [] | x86_perf_event_set_period+0x19d/0x1b2 SS:ESP 0068:edac3d70 | Ming has narrowed it down to a comparision issue between arguments with different sizes and signs. As result event index reached a wrong value which in turn led to a GP fault. At the same time it was found that p4_next_cntr has broken logic and should return the counter index only if it was not yet borrowed for another event. Reported-by: Jaswinder Singh Rajput Reported-by: Lin Ming Bisected-by: Lin Ming Tested-by: Jaswinder Singh Rajput Signed-off-by: Cyrill Gorcunov CC: Peter Zijlstra CC: Frederic Weisbecker LKML-Reference: <20100514190815.GG13509@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index cb875b1..424fc8d 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -18,7 +18,7 @@ struct p4_event_bind { unsigned int opcode; /* Event code and ESCR selector */ unsigned int escr_msr[2]; /* ESCR MSR for this event */ - unsigned char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on abscence */ + char cntr[2][P4_CNTR_LIMIT]; /* counter index (offset), -1 on abscence */ }; struct p4_cache_event_bind { @@ -747,11 +747,11 @@ static int p4_get_escr_idx(unsigned int addr) static int p4_next_cntr(int thread, unsigned long *used_mask, struct p4_event_bind *bind) { - int i = 0, j; + int i, j; for (i = 0; i < P4_CNTR_LIMIT; i++) { - j = bind->cntr[thread][i++]; - if (j == -1 || !test_bit(j, used_mask)) + j = bind->cntr[thread][i]; + if (j != -1 && !test_bit(j, used_mask)) return j; } -- cgit v0.10.2 From 605539034f208d365f76af8e2152cb25f702367d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 20:40:34 -0300 Subject: perf newt: Make <- exit the ui_browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now that means that pressing the left arrow willl make the symbol annotation window to exit back to the main symbol histogram browser. This is another improvement on the UI fastpath, i.e. just the arrows and enter are enough for most browsing. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e74df12..5ded2f0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -323,6 +323,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddHotKey(self->form, NEWT_KEY_LEFT); if (ui_browser__refresh_entries(self) < 0) return -1; @@ -408,6 +409,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, } break; case NEWT_KEY_ESCAPE: + case NEWT_KEY_LEFT: case CTRL('c'): case 'Q': case 'q': @@ -616,7 +618,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self) if (hist_entry__annotate(self, &head) < 0) return; - ui_helpline__push("Press ESC to exit"); + ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); browser.entries = &head; -- cgit v0.10.2 From c1ec5fefd9cd9ccb020966a49a3c7f44b25d9e84 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 20:45:31 -0300 Subject: perf newt: Fix the 'A'/'a' shortcut for annotate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 5ded2f0..094887f 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -892,10 +892,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (choice == -1) continue; -do_annotate: + if (choice == annotate) { struct hist_entry *he; - +do_annotate: if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " -- cgit v0.10.2 From 29351db6a05e7e42be457569428425520a18beec Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 21:06:58 -0300 Subject: perf newt: Exit browser unconditionally when CTRL+C, q or Q is pressed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ESC still asks for confirmation. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 094887f..139eb1a 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -837,9 +837,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na if (es.reason == NEWT_EXIT_HOTKEY) { if (toupper(es.u.key) == 'A') goto do_annotate; - if (es.u.key == NEWT_KEY_ESCAPE || - toupper(es.u.key) == 'Q' || - es.u.key == CTRL('c')) { + if (toupper(es.u.key) == 'Q' || + es.u.key == CTRL('c')) + break; + if (es.u.key == NEWT_KEY_ESCAPE) { if (dialog_yesno("Do you really want to exit?")) break; else -- cgit v0.10.2 From 9d192e118a094087494997ea1c8a2faf39af38c5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 15 May 2010 21:15:01 -0300 Subject: perf newt: Add single key shortcuts for zoom into DSO and threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'D'/'d' for zooming into the DSO in the current highlighted hist entry, 'T'/'t' for zooming into the current thread. Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 139eb1a..6974431 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -752,6 +752,10 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); + newtFormAddHotKey(self->form, 'D'); + newtFormAddHotKey(self->form, 'd'); + newtFormAddHotKey(self->form, 'T'); + newtFormAddHotKey(self->form, 't'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); @@ -834,9 +838,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na annotate = -2, zoom_dso = -2, zoom_thread = -2; newtFormRun(browser->form, &es); + + thread = hist_browser__selected_thread(browser); + dso = browser->selection->map ? browser->selection->map->dso : NULL; + if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') + switch (toupper(es.u.key)) { + case 'A': goto do_annotate; + case 'D': + goto zoom_dso; + case 'T': + goto zoom_thread; + default:; + } if (toupper(es.u.key) == 'Q' || es.u.key == CTRL('c')) break; @@ -866,7 +881,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na browser->selection->sym->name) > 0) annotate = nr_options++; - thread = hist_browser__selected_thread(browser); if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", (thread_filter ? "out of" : "into"), @@ -874,7 +888,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na thread->pid) > 0) zoom_thread = nr_options++; - dso = browser->selection->map ? browser->selection->map->dso : NULL; if (dso != NULL && asprintf(&options[nr_options], "Zoom %s %s DSO", (dso_filter ? "out of" : "into"), @@ -910,12 +923,15 @@ do_annotate: hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { +zoom_dso: if (dso_filter) { pstack__remove(fstack, &dso_filter); zoom_out_dso: ui_helpline__pop(); dso_filter = NULL; } else { + if (dso == NULL) + continue; ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; @@ -927,6 +943,7 @@ zoom_out_dso: if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { +zoom_thread: if (thread_filter) { pstack__remove(fstack, &thread_filter); zoom_out_thread: -- cgit v0.10.2 From a308f3a868185d4f804fe71d0400e2b058c6d9af Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 16 May 2010 20:29:38 -0300 Subject: perf tui: Make <- exit menus too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In fact it is now added to the hot key list when newt_form__new is used, allowing us to remove the explicit assignment in all its users. The visible change is that <- will exit the menu that pops up when -> is pressed (and Enter when callchains are not being used). Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 6974431..59a63e4 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -118,6 +118,7 @@ int browser__show_help(const char *format, va_list ap) static void newt_form__set_exit_keys(newtComponent self) { + newtFormAddHotKey(self, NEWT_KEY_LEFT); newtFormAddHotKey(self, NEWT_KEY_ESCAPE); newtFormAddHotKey(self, 'Q'); newtFormAddHotKey(self, 'q'); @@ -323,7 +324,6 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddHotKey(self->form, NEWT_KEY_LEFT); if (ui_browser__refresh_entries(self) < 0) return -1; @@ -757,7 +757,6 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'T'); newtFormAddHotKey(self->form, 't'); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->form, NEWT_KEY_LEFT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); -- cgit v0.10.2 From a9a4ab747e2d45bf08fddbc1568f080091486af9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 16 May 2010 21:04:27 -0300 Subject: perf tui: Add help window to show key associations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Ingo Molnar Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 59a63e4..2001d26 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -167,6 +167,48 @@ out_destroy_form: return rc; } +static int ui__help_window(const char *text) +{ + struct newtExitStruct es; + newtComponent tb, form = newt_form__new(); + int rc = -1; + int max_len = 0, nr_lines = 0; + const char *t; + + if (form == NULL) + return -1; + + t = text; + while (1) { + const char *sep = strchr(t, '\n'); + int len; + + if (sep == NULL) + sep = strchr(t, '\0'); + len = sep - t; + if (max_len < len) + max_len = len; + ++nr_lines; + if (*sep == '\0') + break; + t = sep + 1; + } + + tb = newtTextbox(0, 0, max_len, nr_lines, 0); + if (tb == NULL) + goto out_destroy_form; + + newtTextboxSetText(tb, text); + newtFormAddComponent(form, tb); + newtCenteredWindow(max_len, nr_lines, NULL); + newtFormRun(form, &es); + newtPopWindow(); + rc = 0; +out_destroy_form: + newtFormDestroy(form); + return rc; +} + static bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ @@ -756,6 +798,10 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists newtFormAddHotKey(self->form, 'd'); newtFormAddHotKey(self->form, 'T'); newtFormAddHotKey(self->form, 't'); + newtFormAddHotKey(self->form, '?'); + newtFormAddHotKey(self->form, 'H'); + newtFormAddHotKey(self->form, 'h'); + newtFormAddHotKey(self->form, NEWT_KEY_F1); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); @@ -842,6 +888,9 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na dso = browser->selection->map ? browser->selection->map->dso : NULL; if (es.reason == NEWT_EXIT_HOTKEY) { + if (es.u.key == NEWT_KEY_F1) + goto do_help; + switch (toupper(es.u.key)) { case 'A': goto do_annotate; @@ -849,6 +898,17 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na goto zoom_dso; case 'T': goto zoom_thread; + case 'H': + case '?': +do_help: + ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "h/?/F1 Show this window\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n" + "q/CTRL+C Exit browser"); + continue; default:; } if (toupper(es.u.key) == 'Q' || -- cgit v0.10.2 From 6ba85cea872954a36d79e46bf6a9c6ea92794f01 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 12:16:48 -0300 Subject: perf options: Introduce OPT_U64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have things like user_interval (-c/--count) in 'perf record' that needs this. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index ed88764..b05d51d 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -60,6 +60,7 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_STRING: case OPTION_INTEGER: case OPTION_LONG: + case OPTION_U64: default: break; } @@ -141,6 +142,22 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_U64: + if (unset) { + *(u64 *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(u64 *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + case OPTION_END: case OPTION_ARGUMENT: case OPTION_GROUP: @@ -487,6 +504,7 @@ int usage_with_options_internal(const char * const *usagestr, case OPTION_SET_INT: case OPTION_SET_PTR: case OPTION_LONG: + case OPTION_U64: break; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b2da725..e301e96 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -17,6 +17,7 @@ enum parse_opt_type { OPTION_INTEGER, OPTION_LONG, OPTION_CALLBACK, + OPTION_U64, }; enum parse_opt_flags { @@ -101,6 +102,7 @@ struct option { #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } -- cgit v0.10.2 From 3de29cab1f8d62db557a4afed0fb17eebfe64438 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 17 May 2010 12:20:43 -0300 Subject: perf record: Fix bug mismatch with -c option definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The -c option defines the user requested sampling period. It was implemented using an unsigned int variable but the type of the option was OPT_LONG. Thus, the option parser was overwriting memory belonging to other variables, namely the mmap_pages leading to a zero page sampling buffer. The bug was exposed only when compiling at -O0, probably because the compiler was padding variables at higher optimization levels. This patch fixes this problem by declaring user_interval as u64. This also avoids wrap-around issues for large period on 32-bit systems. Commiter note: Made it use OPT_U64(user_interval) after implementing OPT_U64 in the previous patch. Cc: David S. Miller Cc: Frédéric Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <4bf11ae9.e88cd80a.06b0.ffffa8e3@mx.google.com> Signed-off-by: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0f467cf..b93573c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -33,8 +33,8 @@ enum write_mode_t { static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; -static unsigned int user_interval = UINT_MAX; -static long default_interval = 0; +static u64 user_interval = ULLONG_MAX; +static u64 default_interval = 0; static int nr_cpus = 0; static unsigned int page_size; @@ -268,7 +268,7 @@ static void create_counter(int counter, int cpu) * it a weak assumption overridable by the user. */ if (!attr->sample_period || (user_freq != UINT_MAX && - user_interval != UINT_MAX)) { + user_interval != ULLONG_MAX)) { if (freq) { attr->sample_type |= PERF_SAMPLE_PERIOD; attr->freq = 1; @@ -817,8 +817,7 @@ static const struct option options[] = { "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, "overwrite existing data file (deprecated)"), - OPT_LONG('c', "count", &user_interval, - "event period to sample"), + OPT_U64('c', "count", &user_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN('i', "no-inherit", &no_inherit, @@ -901,7 +900,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (!event_array) return -ENOMEM; - if (user_interval != UINT_MAX) + if (user_interval != ULLONG_MAX) default_interval = user_interval; if (user_freq != UINT_MAX) freq = user_freq; -- cgit v0.10.2 From dc4ff19341126155c5714119396efbae62ab40bf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 12:25:09 -0300 Subject: perf tui: Add workaround for slang < 2.1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of the slang library didn't used the 'const' specifier, causing problems with modern compilers of this kind: util/newt.c:252: error: passing argument 1 of ‘SLsmg_printf’ discards qualifiers from pointer target type Fix it by using some wrappers that when needed const the affected parameters back to plain (char *). Reported-by: Lin Ming Cc: Frédéric Weisbecker Cc: Lin Ming Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tom Zanussi LKML-Reference: <20100517145421.GD29052@ghostprotocols.net> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2001d26..ccb7c5b 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -14,6 +14,17 @@ #include "sort.h" #include "symbol.h" +#if SLANG_VERSION < 20104 +#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) +#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len) +#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\ + (char *)fg, (char *)bg) +#else +#define slsmg_printf SLsmg_printf +#define slsmg_write_nstring SLsmg_write_nstring +#define sltt_set_color SLtt_set_color +#endif + struct ui_progress { newtComponent form, scale; }; @@ -292,21 +303,21 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, color = ui_browser__percent_color(percent, current_entry); SLsmg_set_color(color); - SLsmg_printf(" %7.2f ", percent); + slsmg_printf(" %7.2f ", percent); if (!current_entry) SLsmg_set_color(HE_COLORSET_CODE); } else { int color = ui_browser__percent_color(0, current_entry); SLsmg_set_color(color); - SLsmg_write_nstring(" ", 9); + slsmg_write_nstring(" ", 9); } SLsmg_write_char(':'); - SLsmg_write_nstring(" ", 8); + slsmg_write_nstring(" ", 8); if (!*self->line) - SLsmg_write_nstring(" ", width - 18); + slsmg_write_nstring(" ", width - 18); else - SLsmg_write_nstring(self->line, width - 18); + slsmg_write_nstring(self->line, width - 18); return 0; } @@ -1054,11 +1065,11 @@ void setup_browser(void) newtInit(); newtCls(); ui_helpline__puts(" "); - SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); - SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); - SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); - SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); - SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); + sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) -- cgit v0.10.2 From c100edbee8dbf033ec4095a976a74c1c75c9fc1d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 15:30:00 -0300 Subject: perf options: Introduce OPT_UINTEGER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For unsigned int options to be parsed, next patches will make use of it. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index b05d51d..36d955e 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -59,6 +59,7 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_GROUP: case OPTION_STRING: case OPTION_INTEGER: + case OPTION_UINTEGER: case OPTION_LONG: case OPTION_U64: default: @@ -126,6 +127,22 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_UINTEGER: + if (unset) { + *(unsigned int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(unsigned int *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + case OPTION_LONG: if (unset) { *(long *)opt->value = 0; @@ -463,7 +480,10 @@ int usage_with_options_internal(const char * const *usagestr, switch (opts->type) { case OPTION_ARGUMENT: break; + case OPTION_LONG: + case OPTION_U64: case OPTION_INTEGER: + case OPTION_UINTEGER: if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=]"); @@ -503,8 +523,6 @@ int usage_with_options_internal(const char * const *usagestr, case OPTION_INCR: case OPTION_SET_INT: case OPTION_SET_PTR: - case OPTION_LONG: - case OPTION_U64: break; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index e301e96..c6aa4ee 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -18,6 +18,7 @@ enum parse_opt_type { OPTION_LONG, OPTION_CALLBACK, OPTION_U64, + OPTION_UINTEGER, }; enum parse_opt_flags { @@ -101,6 +102,7 @@ struct option { #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } -- cgit v0.10.2 From 1967936d688c475b85d34d84e09858cf514c893c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 15:39:16 -0300 Subject: perf options: Check v type in OPT_U?INTEGER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid problems like the one fixed by Stephane Eranian in 3de29ca, now we'll got this instead: bench/sched-messaging.c:259: error: negative width in bit-field ‘’ bench/sched-messaging.c:261: error: negative width in bit-field ‘’ Which is rather cryptic, but is how BUILD_BUG_ON_ZERO works, so kernel hackers should be already used to this. With it in place found some problems, fixed by changing the affected variables to sensible types or changed some OPT_INTEGER to OPT_UINTEGER. Next csets will go thru converting each of the remaining OPT_ so that review can be made easier by grouping changes per type per patch. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index da1b2e9..d1d1b30 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -256,10 +256,8 @@ static const struct option options[] = { "Use pipe() instead of socketpair()"), OPT_BOOLEAN('t', "thread", &thread_mode, "Be multi thread instead of multi process"), - OPT_INTEGER('g', "group", &num_groups, - "Specify number of groups"), - OPT_INTEGER('l', "loop", &loops, - "Specify number of loops"), + OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"), + OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"), OPT_END() }; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b93573c..cb46c7d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -45,7 +45,7 @@ static int output; static int pipe_output = 0; static const char *output_name = "perf.data"; static int group = 0; -static unsigned int realtime_prio = 0; +static int realtime_prio = 0; static bool raw_samples = false; static bool system_wide = false; static int profile_cpu = -1; @@ -822,10 +822,8 @@ static const struct option options[] = { "output file name"), OPT_BOOLEAN('i', "no-inherit", &no_inherit, "child tasks do not inherit counters"), - OPT_INTEGER('F', "freq", &user_freq, - "profile at this frequency"), - OPT_INTEGER('m', "mmap-pages", &mmap_pages, - "number of mmap data pages"), + OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), + OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), OPT_BOOLEAN('g', "call-graph", &call_graph, "do call-graph (stack chain/backtrace) recording"), OPT_INCR('v', "verbose", &verbose, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index be7bc92..c80acdf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -105,7 +105,7 @@ static u64 sum_runtime; static u64 sum_fluct; static u64 run_avg; -static unsigned long replay_repeat = 10; +static unsigned int replay_repeat = 10; static unsigned long nr_timestamps; static unsigned long nr_unordered_timestamps; static unsigned long nr_state_machine_bugs; @@ -1816,8 +1816,8 @@ static const char * const replay_usage[] = { }; static const struct option replay_options[] = { - OPT_INTEGER('r', "repeat", &replay_repeat, - "repeat the workload replay N times (-1: infinite)"), + OPT_UINTEGER('r', "repeat", &replay_repeat, + "repeat the workload replay N times (-1: infinite)"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ed9b5b6..9f0cfa0 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -71,7 +71,7 @@ static int thread_num = 0; static bool inherit = false; static int profile_cpu = -1; static int nr_cpus = 0; -static unsigned int realtime_prio = 0; +static int realtime_prio = 0; static bool group = false; static unsigned int page_size; static unsigned int mmap_pages = 16; @@ -1357,8 +1357,7 @@ static const struct option options[] = { "file", "vmlinux pathname"), OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, "hide kernel symbols"), - OPT_INTEGER('m', "mmap-pages", &mmap_pages, - "number of mmap data pages"), + OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), OPT_INTEGER('r', "realtime", &realtime_prio, "collect data with this RT SCHED_FIFO priority"), OPT_INTEGER('d', "delay", &delay_secs, diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 388ab1b..1eb804f 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -28,6 +28,8 @@ (type *)((char *)__mptr - offsetof(type, member)); }) #endif +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) + #ifndef max #define max(x, y) ({ \ typeof(x) _max1 = (x); \ diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index c6aa4ee..9ca348e 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -1,6 +1,8 @@ #ifndef __PERF_PARSE_OPTIONS_H #define __PERF_PARSE_OPTIONS_H +#include + enum parse_opt_type { /* special types */ OPTION_END, @@ -93,6 +95,8 @@ struct option { intptr_t defval; }; +#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) + #define OPT_END() { .type = OPTION_END } #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } @@ -101,8 +105,8 @@ struct option { #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } -#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } -- cgit v0.10.2 From 8035458fbb567ae138c77a5f710050107c6a7066 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 15:51:10 -0300 Subject: perf options: Type check OPT_BOOLEAN and fix the offenders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index a4c7cae..b1c6b38 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -22,8 +22,8 @@ static char *file_name; static char name_buffer[256]; -int perf_host = 1; -int perf_guest; +bool perf_host = 1; +bool perf_guest; static const char * const kvm_usage[] = { "perf kvm [] {top|record|report|diff|buildid-list}", diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index e18dfdc..821c158 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -792,8 +792,7 @@ static void print_result(void) print_bad_events(bad, total); } -static int info_threads; -static int info_map; +static bool info_threads, info_map; static void dump_threads(void) { diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 0339612..035b9fa 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -257,7 +257,7 @@ static const char * const test_usage[] = { }; static const struct option test_options[] = { - OPT_BOOLEAN('v', "verbose", &verbose, + OPT_INTEGER('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_END() }; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 02821fe..ef7aa0a 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -80,6 +80,7 @@ void get_term_dimensions(struct winsize *ws); #include "../../include/linux/perf_event.h" #include "util/types.h" +#include /* * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all @@ -131,6 +132,6 @@ struct ip_callchain { u64 ips[0]; }; -extern int perf_host, perf_guest; +extern bool perf_host, perf_guest; #endif diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 9ca348e..5838e2d 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -2,6 +2,7 @@ #define __PERF_PARSE_OPTIONS_H #include +#include enum parse_opt_type { /* special types */ @@ -101,7 +102,7 @@ struct option { #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } -#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } +#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } #define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } -- cgit v0.10.2 From edb7c60e27c1baff38d82440dc52eaffac9a45f4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 16:22:41 -0300 Subject: perf options: Type check all the remaining OPT_ variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OPT_SET_INT was renamed to OPT_SET_UINT since the only use in these tools is to set something that has an enum type, that is builtin compatible with unsigned int. Several string constifications were done to make OPT_STRING require a const char * type. Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 4699677..fcb9626 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -95,7 +95,7 @@ static void dump_suites(int subsys_index) return; } -static char *bench_format_str; +static const char *bench_format_str; int bench_format = BENCH_FORMAT_DEFAULT; static const struct option bench_options[] = { @@ -126,7 +126,7 @@ static void print_usage(void) printf("\n"); } -static int bench_str2int(char *str) +static int bench_str2int(const char *str) { if (!str) return BENCH_FORMAT_DEFAULT; diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 81e3ecc..6d5a8a7 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -33,10 +33,10 @@ static bool show_all = false; static enum help_format help_format = HELP_FORMAT_MAN; static struct option builtin_help_options[] = { OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), - OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), - OPT_SET_INT('w', "web", &help_format, "show manual in web browser", + OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), + OPT_SET_UINT('w', "web", &help_format, "show manual in web browser", HELP_FORMAT_WEB), - OPT_SET_INT('i', "info", &help_format, "show info page", + OPT_SET_UINT('i', "info", &help_format, "show info page", HELP_FORMAT_INFO), OPT_END(), }; diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index b1c6b38..34d1e85 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -19,7 +19,7 @@ #include #include -static char *file_name; +static const char *file_name; static char name_buffer[256]; bool perf_host = 1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6826512..1d3c100 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -39,8 +39,8 @@ static bool dont_use_callchains; static bool show_threads; static struct perf_read_values show_threads_values; -static char default_pretty_printing_style[] = "normal"; -static char *pretty_printing_style = default_pretty_printing_style; +static const char default_pretty_printing_style[] = "normal"; +static const char *pretty_printing_style = default_pretty_printing_style; static char callchain_default_opt[] = "fractal,0.5"; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c80acdf..f67bce2 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -22,7 +22,7 @@ static char const *input_name = "perf.data"; static char default_sort_order[] = "avg, max, switch, runtime"; -static char *sort_order = default_sort_order; +static const char *sort_order = default_sort_order; static int profile_cpu = -1; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 9f0cfa0..397290a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -96,7 +96,7 @@ struct source_line { struct source_line *next; }; -static char *sym_filter = NULL; +static const char *sym_filter = NULL; struct sym_entry *sym_filter_entry = NULL; struct sym_entry *sym_filter_entry_sched = NULL; static int sym_pcnt_filter = 5; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f75c5f6..9a71c94 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -662,7 +662,7 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, long displacement = 0; unsigned int width; const char *sep = symbol_conf.field_sep; - char *col_width = symbol_conf.col_width_list_str; + const char *col_width = symbol_conf.col_width_list_str; init_rem_hits(); diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 36d955e..99d02aa 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -51,7 +51,7 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: case OPTION_INCR: case OPTION_BIT: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); case OPTION_END: @@ -83,8 +83,8 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; - case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; + case OPTION_SET_UINT: + *(unsigned int *)opt->value = unset ? 0 : opt->defval; return 0; case OPTION_SET_PTR: @@ -515,13 +515,13 @@ int usage_with_options_internal(const char * const *usagestr, pos += fprintf(stderr, " ..."); } break; - default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ case OPTION_END: case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: case OPTION_INCR: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: break; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 5838e2d..c7d72dc 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -13,7 +13,7 @@ enum parse_opt_type { OPTION_BIT, OPTION_BOOLEAN, OPTION_INCR, - OPTION_SET_INT, + OPTION_SET_UINT, OPTION_SET_PTR, /* options with arguments (usually) */ OPTION_STRING, @@ -79,7 +79,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * * `defval`:: * default value to fill (*->value) with for PARSE_OPT_OPTARG. - * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in + * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. */ @@ -101,16 +101,16 @@ struct option { #define OPT_END() { .type = OPTION_END } #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } -#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } +#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } +#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } -#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } +#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } +#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } +#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index da30b30..2316cb5 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,10 +1,10 @@ #include "sort.h" regex_t parent_regex; -char default_parent_pattern[] = "^sys_|^do_page_fault"; -char *parent_pattern = default_parent_pattern; -char default_sort_order[] = "comm,dso,symbol"; -char *sort_order = default_sort_order; +const char default_parent_pattern[] = "^sys_|^do_page_fault"; +const char *parent_pattern = default_parent_pattern; +const char default_sort_order[] = "comm,dso,symbol"; +const char *sort_order = default_sort_order; int sort__need_collapse = 0; int sort__has_parent = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index eab2e0b3..0d61c40 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -25,10 +25,10 @@ #include "sort.h" extern regex_t parent_regex; -extern char *sort_order; -extern char default_parent_pattern[]; -extern char *parent_pattern; -extern char default_sort_order[]; +extern const char *sort_order; +extern const char default_parent_pattern[]; +extern const char *parent_pattern; +extern const char default_sort_order[]; extern int sort__need_collapse; extern int sort__has_parent; extern char *field_sep; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6389d1a..032469e 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -78,7 +78,7 @@ struct symbol_conf { *default_guest_kallsyms, *default_guest_modules; const char *guestmount; - char *dso_list_str, + const char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; -- cgit v0.10.2 From 63aa9e7e3ab28ad5362502b1a69fae945367ad65 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 16:42:37 -0300 Subject: perf tui: Add explicit -lslang option At least on rawhide using -lnewt is not enough if we use SLang routines directly, so add an explicit -lslang since we use SLang routines. Reported-by: Ingo Molnar Tested-by: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index a9281cc..e2729fc 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -564,7 +564,7 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang - EXTLIBS += -lnewt + EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o endif -- cgit v0.10.2 From 2f51903bc3139e25ec908f8944a0001c7b868e90 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 17:57:59 -0300 Subject: perf symbols: symbol inconsistency message should be done only at verbose=1 That happened for an old perf.data file that had no fake MMAP events for the kernel modules, but even then it should warn once for each module, not one time for every symbol in every module not found. Reported-by: Ingo Molnar LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ecccc8d..a06131f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -525,7 +525,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, curr_map = map_groups__find_by_name(kmaps, map->type, module); if (curr_map == NULL) { - pr_err("%s/proc/{kallsyms,modules} " + pr_debug("%s/proc/{kallsyms,modules} " "inconsistency while looking " "for \"%s\" module!\n", machine->root_dir, module); -- cgit v0.10.2 From 94f3ca95787ada3d64339a4ecb2754236ab563f6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 May 2010 18:18:11 -0300 Subject: perf tools: Add mode to build without newt support make NO_NEWT=1 Will avoid building the newt (tui) support. Suggested-by: Ingo Molnar LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e2729fc..3d8f31e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -558,6 +558,9 @@ else endif # PERF_HAVE_DWARF_REGS endif # NO_DWARF +ifdef NO_NEWT + BASIC_CFLAGS += -DNO_NEWT_SUPPORT +else ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT @@ -567,6 +570,7 @@ else EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o endif +endif # NO_NEWT ifndef NO_LIBPERL PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` -- cgit v0.10.2