Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (47 commits)
  perf report: Add --symbols parameter
  perf report: Add --comms parameter
  perf report: Add --dsos parameter
  perf_counter tools: Adjust only prelinked symbol's addresses
  perf_counter: Provide a way to enable counters on exec
  perf_counter tools: Reduce perf stat measurement overhead/skew
  perf stat: Use percentages for scaling output
  perf_counter, x86: Update x86_pmu after WARN()
  perf stat: Micro-optimize the code: memcpy is only required if no event is selected and !null_run
  perf stat: Improve output
  perf stat: Fix multi-run stats
  perf stat: Add -n/--null option to run without counters
  perf_counter tools: Remove dead code
  perf_counter: Complete counter swap
  perf report: Print sorted callchains per histogram entries
  perf_counter tools: Prepare a small callchain framework
  perf record: Fix unhandled io return value
  perf_counter tools: Add alias for 'l1d' and 'l1i'
  perf-report: Add bare minimum PERF_EVENT_READ parsing
  perf-report: Add modes for inherited stats and no-samples
  ...
This commit is contained in:
Linus Torvalds 2009-06-30 19:02:59 -07:00
commit 55bcab4695
32 changed files with 1602 additions and 420 deletions

View file

@ -61,6 +61,8 @@ struct pt_regs;
extern unsigned long perf_misc_flags(struct pt_regs *regs); extern unsigned long perf_misc_flags(struct pt_regs *regs);
extern unsigned long perf_instruction_pointer(struct pt_regs *regs); extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
#define PERF_COUNTER_INDEX_OFFSET 1
/* /*
* Only override the default definitions in include/linux/perf_counter.h * Only override the default definitions in include/linux/perf_counter.h
* if we have hardware PMU support. * if we have hardware PMU support.

View file

@ -87,6 +87,9 @@ union cpuid10_edx {
#ifdef CONFIG_PERF_COUNTERS #ifdef CONFIG_PERF_COUNTERS
extern void init_hw_perf_counters(void); extern void init_hw_perf_counters(void);
extern void perf_counters_lapic_init(void); extern void perf_counters_lapic_init(void);
#define PERF_COUNTER_INDEX_OFFSET 0
#else #else
static inline void init_hw_perf_counters(void) { } static inline void init_hw_perf_counters(void) { }
static inline void perf_counters_lapic_init(void) { } static inline void perf_counters_lapic_init(void) { }

View file

@ -401,7 +401,7 @@ static const u64 amd_hw_cache_event_ids
[ C(RESULT_MISS) ] = 0x0041, /* Data Cache Misses */ [ C(RESULT_MISS) ] = 0x0041, /* Data Cache Misses */
}, },
[ C(OP_WRITE) ] = { [ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = 0x0042, /* Data Cache Refills from L2 */ [ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */
[ C(RESULT_MISS) ] = 0, [ C(RESULT_MISS) ] = 0,
}, },
[ C(OP_PREFETCH) ] = { [ C(OP_PREFETCH) ] = {
@ -912,6 +912,8 @@ x86_perf_counter_set_period(struct perf_counter *counter,
err = checking_wrmsrl(hwc->counter_base + idx, err = checking_wrmsrl(hwc->counter_base + idx,
(u64)(-left) & x86_pmu.counter_mask); (u64)(-left) & x86_pmu.counter_mask);
perf_counter_update_userpage(counter);
return ret; return ret;
} }
@ -969,13 +971,6 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc)
if (!x86_pmu.num_counters_fixed) if (!x86_pmu.num_counters_fixed)
return -1; return -1;
/*
* Quirk, IA32_FIXED_CTRs do not work on current Atom processors:
*/
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
boot_cpu_data.x86_model == 28)
return -1;
event = hwc->config & ARCH_PERFMON_EVENT_MASK; event = hwc->config & ARCH_PERFMON_EVENT_MASK;
if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS)))
@ -1041,6 +1036,8 @@ try_generic:
x86_perf_counter_set_period(counter, hwc, idx); x86_perf_counter_set_period(counter, hwc, idx);
x86_pmu.enable(hwc, idx); x86_pmu.enable(hwc, idx);
perf_counter_update_userpage(counter);
return 0; return 0;
} }
@ -1133,6 +1130,8 @@ static void x86_pmu_disable(struct perf_counter *counter)
x86_perf_counter_update(counter, hwc, idx); x86_perf_counter_update(counter, hwc, idx);
cpuc->counters[idx] = NULL; cpuc->counters[idx] = NULL;
clear_bit(idx, cpuc->used_mask); clear_bit(idx, cpuc->used_mask);
perf_counter_update_userpage(counter);
} }
/* /*
@ -1428,8 +1427,6 @@ static int intel_pmu_init(void)
*/ */
x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3);
rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
/* /*
* Install the hw-cache-events table: * Install the hw-cache-events table:
*/ */
@ -1499,21 +1496,22 @@ void __init init_hw_perf_counters(void)
pr_cont("%s PMU driver.\n", x86_pmu.name); pr_cont("%s PMU driver.\n", x86_pmu.name);
if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) { if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) {
x86_pmu.num_counters = X86_PMC_MAX_GENERIC;
WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!",
x86_pmu.num_counters, X86_PMC_MAX_GENERIC); x86_pmu.num_counters, X86_PMC_MAX_GENERIC);
x86_pmu.num_counters = X86_PMC_MAX_GENERIC;
} }
perf_counter_mask = (1 << x86_pmu.num_counters) - 1; perf_counter_mask = (1 << x86_pmu.num_counters) - 1;
perf_max_counters = x86_pmu.num_counters; perf_max_counters = x86_pmu.num_counters;
if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) { if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) {
x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED;
WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!",
x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED); x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED);
x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED;
} }
perf_counter_mask |= perf_counter_mask |=
((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED;
x86_pmu.intel_ctrl = perf_counter_mask;
perf_counters_lapic_init(); perf_counters_lapic_init();
register_die_notifier(&perf_counter_nmi_notifier); register_die_notifier(&perf_counter_nmi_notifier);

View file

@ -178,8 +178,10 @@ struct perf_counter_attr {
mmap : 1, /* include mmap data */ mmap : 1, /* include mmap data */
comm : 1, /* include comm data */ comm : 1, /* include comm data */
freq : 1, /* use freq, not period */ freq : 1, /* use freq, not period */
inherit_stat : 1, /* per task counts */
enable_on_exec : 1, /* next exec enables */
__reserved_1 : 53; __reserved_1 : 51;
__u32 wakeup_events; /* wakeup every n events */ __u32 wakeup_events; /* wakeup every n events */
__u32 __reserved_2; __u32 __reserved_2;
@ -232,6 +234,14 @@ struct perf_counter_mmap_page {
__u32 lock; /* seqlock for synchronization */ __u32 lock; /* seqlock for synchronization */
__u32 index; /* hardware counter identifier */ __u32 index; /* hardware counter identifier */
__s64 offset; /* add to hardware counter value */ __s64 offset; /* add to hardware counter value */
__u64 time_enabled; /* time counter active */
__u64 time_running; /* time counter on cpu */
/*
* Hole for extension of the self monitor capabilities
*/
__u64 __reserved[123]; /* align to 1k */
/* /*
* Control data for the mmap() data buffer. * Control data for the mmap() data buffer.
@ -253,7 +263,6 @@ struct perf_counter_mmap_page {
#define PERF_EVENT_MISC_KERNEL (1 << 0) #define PERF_EVENT_MISC_KERNEL (1 << 0)
#define PERF_EVENT_MISC_USER (2 << 0) #define PERF_EVENT_MISC_USER (2 << 0)
#define PERF_EVENT_MISC_HYPERVISOR (3 << 0) #define PERF_EVENT_MISC_HYPERVISOR (3 << 0)
#define PERF_EVENT_MISC_OVERFLOW (1 << 2)
struct perf_event_header { struct perf_event_header {
__u32 type; __u32 type;
@ -327,9 +336,18 @@ enum perf_event_type {
PERF_EVENT_FORK = 7, PERF_EVENT_FORK = 7,
/* /*
* When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field * struct {
* will be PERF_SAMPLE_* * struct perf_event_header header;
* * u32 pid, tid;
* u64 value;
* { u64 time_enabled; } && PERF_FORMAT_ENABLED
* { u64 time_running; } && PERF_FORMAT_RUNNING
* { u64 parent_id; } && PERF_FORMAT_ID
* };
*/
PERF_EVENT_READ = 8,
/*
* struct { * struct {
* struct perf_event_header header; * struct perf_event_header header;
* *
@ -337,8 +355,9 @@ enum perf_event_type {
* { u32 pid, tid; } && PERF_SAMPLE_TID * { u32 pid, tid; } && PERF_SAMPLE_TID
* { u64 time; } && PERF_SAMPLE_TIME * { u64 time; } && PERF_SAMPLE_TIME
* { u64 addr; } && PERF_SAMPLE_ADDR * { u64 addr; } && PERF_SAMPLE_ADDR
* { u64 config; } && PERF_SAMPLE_CONFIG * { u64 id; } && PERF_SAMPLE_ID
* { u32 cpu, res; } && PERF_SAMPLE_CPU * { u32 cpu, res; } && PERF_SAMPLE_CPU
* { u64 period; } && PERF_SAMPLE_PERIOD
* *
* { u64 nr; * { u64 nr;
* { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP * { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP
@ -347,6 +366,9 @@ enum perf_event_type {
* u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
* }; * };
*/ */
PERF_EVENT_SAMPLE = 9,
PERF_EVENT_MAX, /* non-ABI */
}; };
enum perf_callchain_context { enum perf_callchain_context {
@ -582,6 +604,7 @@ struct perf_counter_context {
int nr_counters; int nr_counters;
int nr_active; int nr_active;
int is_active; int is_active;
int nr_stat;
atomic_t refcount; atomic_t refcount;
struct task_struct *task; struct task_struct *task;
@ -669,7 +692,16 @@ static inline int is_software_counter(struct perf_counter *counter)
(counter->attr.type != PERF_TYPE_HW_CACHE); (counter->attr.type != PERF_TYPE_HW_CACHE);
} }
extern void perf_swcounter_event(u32, u64, int, struct pt_regs *, u64); extern atomic_t perf_swcounter_enabled[PERF_COUNT_SW_MAX];
extern void __perf_swcounter_event(u32, u64, int, struct pt_regs *, u64);
static inline void
perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs, u64 addr)
{
if (atomic_read(&perf_swcounter_enabled[event]))
__perf_swcounter_event(event, nr, nmi, regs, addr);
}
extern void __perf_counter_mmap(struct vm_area_struct *vma); extern void __perf_counter_mmap(struct vm_area_struct *vma);

View file

@ -236,6 +236,8 @@ list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
list_add_rcu(&counter->event_entry, &ctx->event_list); list_add_rcu(&counter->event_entry, &ctx->event_list);
ctx->nr_counters++; ctx->nr_counters++;
if (counter->attr.inherit_stat)
ctx->nr_stat++;
} }
/* /*
@ -250,6 +252,8 @@ list_del_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
if (list_empty(&counter->list_entry)) if (list_empty(&counter->list_entry))
return; return;
ctx->nr_counters--; ctx->nr_counters--;
if (counter->attr.inherit_stat)
ctx->nr_stat--;
list_del_init(&counter->list_entry); list_del_init(&counter->list_entry);
list_del_rcu(&counter->event_entry); list_del_rcu(&counter->event_entry);
@ -1006,6 +1010,81 @@ static int context_equiv(struct perf_counter_context *ctx1,
&& !ctx1->pin_count && !ctx2->pin_count; && !ctx1->pin_count && !ctx2->pin_count;
} }
static void __perf_counter_read(void *counter);
static void __perf_counter_sync_stat(struct perf_counter *counter,
struct perf_counter *next_counter)
{
u64 value;
if (!counter->attr.inherit_stat)
return;
/*
* Update the counter value, we cannot use perf_counter_read()
* because we're in the middle of a context switch and have IRQs
* disabled, which upsets smp_call_function_single(), however
* we know the counter must be on the current CPU, therefore we
* don't need to use it.
*/
switch (counter->state) {
case PERF_COUNTER_STATE_ACTIVE:
__perf_counter_read(counter);
break;
case PERF_COUNTER_STATE_INACTIVE:
update_counter_times(counter);
break;
default:
break;
}
/*
* In order to keep per-task stats reliable we need to flip the counter
* values when we flip the contexts.
*/
value = atomic64_read(&next_counter->count);
value = atomic64_xchg(&counter->count, value);
atomic64_set(&next_counter->count, value);
swap(counter->total_time_enabled, next_counter->total_time_enabled);
swap(counter->total_time_running, next_counter->total_time_running);
/*
* Since we swizzled the values, update the user visible data too.
*/
perf_counter_update_userpage(counter);
perf_counter_update_userpage(next_counter);
}
#define list_next_entry(pos, member) \
list_entry(pos->member.next, typeof(*pos), member)
static void perf_counter_sync_stat(struct perf_counter_context *ctx,
struct perf_counter_context *next_ctx)
{
struct perf_counter *counter, *next_counter;
if (!ctx->nr_stat)
return;
counter = list_first_entry(&ctx->event_list,
struct perf_counter, event_entry);
next_counter = list_first_entry(&next_ctx->event_list,
struct perf_counter, event_entry);
while (&counter->event_entry != &ctx->event_list &&
&next_counter->event_entry != &next_ctx->event_list) {
__perf_counter_sync_stat(counter, next_counter);
counter = list_next_entry(counter, event_entry);
next_counter = list_next_entry(counter, event_entry);
}
}
/* /*
* Called from scheduler to remove the counters of the current task, * Called from scheduler to remove the counters of the current task,
* with interrupts disabled. * with interrupts disabled.
@ -1061,6 +1140,8 @@ void perf_counter_task_sched_out(struct task_struct *task,
ctx->task = next; ctx->task = next;
next_ctx->task = task; next_ctx->task = task;
do_switch = 0; do_switch = 0;
perf_counter_sync_stat(ctx, next_ctx);
} }
spin_unlock(&next_ctx->lock); spin_unlock(&next_ctx->lock);
spin_unlock(&ctx->lock); spin_unlock(&ctx->lock);
@ -1347,10 +1428,57 @@ void perf_counter_task_tick(struct task_struct *curr, int cpu)
perf_counter_task_sched_in(curr, cpu); perf_counter_task_sched_in(curr, cpu);
} }
/*
* Enable all of a task's counters that have been marked enable-on-exec.
* This expects task == current.
*/
static void perf_counter_enable_on_exec(struct task_struct *task)
{
struct perf_counter_context *ctx;
struct perf_counter *counter;
unsigned long flags;
int enabled = 0;
local_irq_save(flags);
ctx = task->perf_counter_ctxp;
if (!ctx || !ctx->nr_counters)
goto out;
__perf_counter_task_sched_out(ctx);
spin_lock(&ctx->lock);
list_for_each_entry(counter, &ctx->counter_list, list_entry) {
if (!counter->attr.enable_on_exec)
continue;
counter->attr.enable_on_exec = 0;
if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
continue;
counter->state = PERF_COUNTER_STATE_INACTIVE;
counter->tstamp_enabled =
ctx->time - counter->total_time_enabled;
enabled = 1;
}
/*
* Unclone this context if we enabled any counter.
*/
if (enabled && ctx->parent_ctx) {
put_ctx(ctx->parent_ctx);
ctx->parent_ctx = NULL;
}
spin_unlock(&ctx->lock);
perf_counter_task_sched_in(task, smp_processor_id());
out:
local_irq_restore(flags);
}
/* /*
* Cross CPU call to read the hardware counter * Cross CPU call to read the hardware counter
*/ */
static void __read(void *info) static void __perf_counter_read(void *info)
{ {
struct perf_counter *counter = info; struct perf_counter *counter = info;
struct perf_counter_context *ctx = counter->ctx; struct perf_counter_context *ctx = counter->ctx;
@ -1372,7 +1500,7 @@ static u64 perf_counter_read(struct perf_counter *counter)
*/ */
if (counter->state == PERF_COUNTER_STATE_ACTIVE) { if (counter->state == PERF_COUNTER_STATE_ACTIVE) {
smp_call_function_single(counter->oncpu, smp_call_function_single(counter->oncpu,
__read, counter, 1); __perf_counter_read, counter, 1);
} else if (counter->state == PERF_COUNTER_STATE_INACTIVE) { } else if (counter->state == PERF_COUNTER_STATE_INACTIVE) {
update_counter_times(counter); update_counter_times(counter);
} }
@ -1508,11 +1636,13 @@ static void free_counter(struct perf_counter *counter)
{ {
perf_pending_sync(counter); perf_pending_sync(counter);
atomic_dec(&nr_counters); if (!counter->parent) {
if (counter->attr.mmap) atomic_dec(&nr_counters);
atomic_dec(&nr_mmap_counters); if (counter->attr.mmap)
if (counter->attr.comm) atomic_dec(&nr_mmap_counters);
atomic_dec(&nr_comm_counters); if (counter->attr.comm)
atomic_dec(&nr_comm_counters);
}
if (counter->destroy) if (counter->destroy)
counter->destroy(counter); counter->destroy(counter);
@ -1751,6 +1881,14 @@ int perf_counter_task_disable(void)
return 0; return 0;
} }
static int perf_counter_index(struct perf_counter *counter)
{
if (counter->state != PERF_COUNTER_STATE_ACTIVE)
return 0;
return counter->hw.idx + 1 - PERF_COUNTER_INDEX_OFFSET;
}
/* /*
* Callers need to ensure there can be no nesting of this function, otherwise * Callers need to ensure there can be no nesting of this function, otherwise
* the seqlock logic goes bad. We can not serialize this because the arch * the seqlock logic goes bad. We can not serialize this because the arch
@ -1775,11 +1913,17 @@ void perf_counter_update_userpage(struct perf_counter *counter)
preempt_disable(); preempt_disable();
++userpg->lock; ++userpg->lock;
barrier(); barrier();
userpg->index = counter->hw.idx; userpg->index = perf_counter_index(counter);
userpg->offset = atomic64_read(&counter->count); userpg->offset = atomic64_read(&counter->count);
if (counter->state == PERF_COUNTER_STATE_ACTIVE) if (counter->state == PERF_COUNTER_STATE_ACTIVE)
userpg->offset -= atomic64_read(&counter->hw.prev_count); userpg->offset -= atomic64_read(&counter->hw.prev_count);
userpg->time_enabled = counter->total_time_enabled +
atomic64_read(&counter->child_total_time_enabled);
userpg->time_running = counter->total_time_running +
atomic64_read(&counter->child_total_time_running);
barrier(); barrier();
++userpg->lock; ++userpg->lock;
preempt_enable(); preempt_enable();
@ -2483,15 +2627,14 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
u32 cpu, reserved; u32 cpu, reserved;
} cpu_entry; } cpu_entry;
header.type = 0; header.type = PERF_EVENT_SAMPLE;
header.size = sizeof(header); header.size = sizeof(header);
header.misc = PERF_EVENT_MISC_OVERFLOW; header.misc = 0;
header.misc |= perf_misc_flags(data->regs); header.misc |= perf_misc_flags(data->regs);
if (sample_type & PERF_SAMPLE_IP) { if (sample_type & PERF_SAMPLE_IP) {
ip = perf_instruction_pointer(data->regs); ip = perf_instruction_pointer(data->regs);
header.type |= PERF_SAMPLE_IP;
header.size += sizeof(ip); header.size += sizeof(ip);
} }
@ -2500,7 +2643,6 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
tid_entry.pid = perf_counter_pid(counter, current); tid_entry.pid = perf_counter_pid(counter, current);
tid_entry.tid = perf_counter_tid(counter, current); tid_entry.tid = perf_counter_tid(counter, current);
header.type |= PERF_SAMPLE_TID;
header.size += sizeof(tid_entry); header.size += sizeof(tid_entry);
} }
@ -2510,34 +2652,25 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
*/ */
time = sched_clock(); time = sched_clock();
header.type |= PERF_SAMPLE_TIME;
header.size += sizeof(u64); header.size += sizeof(u64);
} }
if (sample_type & PERF_SAMPLE_ADDR) { if (sample_type & PERF_SAMPLE_ADDR)
header.type |= PERF_SAMPLE_ADDR;
header.size += sizeof(u64); header.size += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_ID) { if (sample_type & PERF_SAMPLE_ID)
header.type |= PERF_SAMPLE_ID;
header.size += sizeof(u64); header.size += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_CPU) { if (sample_type & PERF_SAMPLE_CPU) {
header.type |= PERF_SAMPLE_CPU;
header.size += sizeof(cpu_entry); header.size += sizeof(cpu_entry);
cpu_entry.cpu = raw_smp_processor_id(); cpu_entry.cpu = raw_smp_processor_id();
} }
if (sample_type & PERF_SAMPLE_PERIOD) { if (sample_type & PERF_SAMPLE_PERIOD)
header.type |= PERF_SAMPLE_PERIOD;
header.size += sizeof(u64); header.size += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_GROUP) { if (sample_type & PERF_SAMPLE_GROUP) {
header.type |= PERF_SAMPLE_GROUP;
header.size += sizeof(u64) + header.size += sizeof(u64) +
counter->nr_siblings * sizeof(group_entry); counter->nr_siblings * sizeof(group_entry);
} }
@ -2547,10 +2680,9 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
if (callchain) { if (callchain) {
callchain_size = (1 + callchain->nr) * sizeof(u64); callchain_size = (1 + callchain->nr) * sizeof(u64);
header.type |= PERF_SAMPLE_CALLCHAIN;
header.size += callchain_size; header.size += callchain_size;
} } else
header.size += sizeof(u64);
} }
ret = perf_output_begin(&handle, counter, header.size, nmi, 1); ret = perf_output_begin(&handle, counter, header.size, nmi, 1);
@ -2601,12 +2733,78 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
} }
} }
if (callchain) if (sample_type & PERF_SAMPLE_CALLCHAIN) {
perf_output_copy(&handle, callchain, callchain_size); if (callchain)
perf_output_copy(&handle, callchain, callchain_size);
else {
u64 nr = 0;
perf_output_put(&handle, nr);
}
}
perf_output_end(&handle); perf_output_end(&handle);
} }
/*
* read event
*/
struct perf_read_event {
struct perf_event_header header;
u32 pid;
u32 tid;
u64 value;
u64 format[3];
};
static void
perf_counter_read_event(struct perf_counter *counter,
struct task_struct *task)
{
struct perf_output_handle handle;
struct perf_read_event event = {
.header = {
.type = PERF_EVENT_READ,
.misc = 0,
.size = sizeof(event) - sizeof(event.format),
},
.pid = perf_counter_pid(counter, task),
.tid = perf_counter_tid(counter, task),
.value = atomic64_read(&counter->count),
};
int ret, i = 0;
if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
event.header.size += sizeof(u64);
event.format[i++] = counter->total_time_enabled;
}
if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
event.header.size += sizeof(u64);
event.format[i++] = counter->total_time_running;
}
if (counter->attr.read_format & PERF_FORMAT_ID) {
u64 id;
event.header.size += sizeof(u64);
if (counter->parent)
id = counter->parent->id;
else
id = counter->id;
event.format[i++] = id;
}
ret = perf_output_begin(&handle, counter, event.header.size, 0, 0);
if (ret)
return;
perf_output_copy(&handle, &event, event.header.size);
perf_output_end(&handle);
}
/* /*
* fork tracking * fork tracking
*/ */
@ -2798,6 +2996,9 @@ void perf_counter_comm(struct task_struct *task)
{ {
struct perf_comm_event comm_event; struct perf_comm_event comm_event;
if (task->perf_counter_ctxp)
perf_counter_enable_on_exec(task);
if (!atomic_read(&nr_comm_counters)) if (!atomic_read(&nr_comm_counters))
return; return;
@ -3317,8 +3518,8 @@ out:
put_cpu_var(perf_cpu_context); put_cpu_var(perf_cpu_context);
} }
void void __perf_swcounter_event(u32 event, u64 nr, int nmi,
perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs, u64 addr) struct pt_regs *regs, u64 addr)
{ {
struct perf_sample_data data = { struct perf_sample_data data = {
.regs = regs, .regs = regs,
@ -3509,9 +3710,21 @@ static const struct pmu *tp_perf_counter_init(struct perf_counter *counter)
} }
#endif #endif
atomic_t perf_swcounter_enabled[PERF_COUNT_SW_MAX];
static void sw_perf_counter_destroy(struct perf_counter *counter)
{
u64 event = counter->attr.config;
WARN_ON(counter->parent);
atomic_dec(&perf_swcounter_enabled[event]);
}
static const struct pmu *sw_perf_counter_init(struct perf_counter *counter) static const struct pmu *sw_perf_counter_init(struct perf_counter *counter)
{ {
const struct pmu *pmu = NULL; const struct pmu *pmu = NULL;
u64 event = counter->attr.config;
/* /*
* Software counters (currently) can't in general distinguish * Software counters (currently) can't in general distinguish
@ -3520,7 +3733,7 @@ static const struct pmu *sw_perf_counter_init(struct perf_counter *counter)
* to be kernel events, and page faults are never hypervisor * to be kernel events, and page faults are never hypervisor
* events. * events.
*/ */
switch (counter->attr.config) { switch (event) {
case PERF_COUNT_SW_CPU_CLOCK: case PERF_COUNT_SW_CPU_CLOCK:
pmu = &perf_ops_cpu_clock; pmu = &perf_ops_cpu_clock;
@ -3541,6 +3754,10 @@ static const struct pmu *sw_perf_counter_init(struct perf_counter *counter)
case PERF_COUNT_SW_PAGE_FAULTS_MAJ: case PERF_COUNT_SW_PAGE_FAULTS_MAJ:
case PERF_COUNT_SW_CONTEXT_SWITCHES: case PERF_COUNT_SW_CONTEXT_SWITCHES:
case PERF_COUNT_SW_CPU_MIGRATIONS: case PERF_COUNT_SW_CPU_MIGRATIONS:
if (!counter->parent) {
atomic_inc(&perf_swcounter_enabled[event]);
counter->destroy = sw_perf_counter_destroy;
}
pmu = &perf_ops_generic; pmu = &perf_ops_generic;
break; break;
} }
@ -3556,6 +3773,7 @@ perf_counter_alloc(struct perf_counter_attr *attr,
int cpu, int cpu,
struct perf_counter_context *ctx, struct perf_counter_context *ctx,
struct perf_counter *group_leader, struct perf_counter *group_leader,
struct perf_counter *parent_counter,
gfp_t gfpflags) gfp_t gfpflags)
{ {
const struct pmu *pmu; const struct pmu *pmu;
@ -3591,6 +3809,8 @@ perf_counter_alloc(struct perf_counter_attr *attr,
counter->ctx = ctx; counter->ctx = ctx;
counter->oncpu = -1; counter->oncpu = -1;
counter->parent = parent_counter;
counter->ns = get_pid_ns(current->nsproxy->pid_ns); counter->ns = get_pid_ns(current->nsproxy->pid_ns);
counter->id = atomic64_inc_return(&perf_counter_id); counter->id = atomic64_inc_return(&perf_counter_id);
@ -3648,11 +3868,13 @@ done:
counter->pmu = pmu; counter->pmu = pmu;
atomic_inc(&nr_counters); if (!counter->parent) {
if (counter->attr.mmap) atomic_inc(&nr_counters);
atomic_inc(&nr_mmap_counters); if (counter->attr.mmap)
if (counter->attr.comm) atomic_inc(&nr_mmap_counters);
atomic_inc(&nr_comm_counters); if (counter->attr.comm)
atomic_inc(&nr_comm_counters);
}
return counter; return counter;
} }
@ -3815,7 +4037,7 @@ SYSCALL_DEFINE5(perf_counter_open,
} }
counter = perf_counter_alloc(&attr, cpu, ctx, group_leader, counter = perf_counter_alloc(&attr, cpu, ctx, group_leader,
GFP_KERNEL); NULL, GFP_KERNEL);
ret = PTR_ERR(counter); ret = PTR_ERR(counter);
if (IS_ERR(counter)) if (IS_ERR(counter))
goto err_put_context; goto err_put_context;
@ -3881,7 +4103,8 @@ inherit_counter(struct perf_counter *parent_counter,
child_counter = perf_counter_alloc(&parent_counter->attr, child_counter = perf_counter_alloc(&parent_counter->attr,
parent_counter->cpu, child_ctx, parent_counter->cpu, child_ctx,
group_leader, GFP_KERNEL); group_leader, parent_counter,
GFP_KERNEL);
if (IS_ERR(child_counter)) if (IS_ERR(child_counter))
return child_counter; return child_counter;
get_ctx(child_ctx); get_ctx(child_ctx);
@ -3904,12 +4127,6 @@ inherit_counter(struct perf_counter *parent_counter,
*/ */
add_counter_to_ctx(child_counter, child_ctx); add_counter_to_ctx(child_counter, child_ctx);
child_counter->parent = parent_counter;
/*
* inherit into child's child as well:
*/
child_counter->attr.inherit = 1;
/* /*
* Get a reference to the parent filp - we will fput it * Get a reference to the parent filp - we will fput it
* when the child counter exits. This is safe to do because * when the child counter exits. This is safe to do because
@ -3953,10 +4170,14 @@ static int inherit_group(struct perf_counter *parent_counter,
} }
static void sync_child_counter(struct perf_counter *child_counter, static void sync_child_counter(struct perf_counter *child_counter,
struct perf_counter *parent_counter) struct task_struct *child)
{ {
struct perf_counter *parent_counter = child_counter->parent;
u64 child_val; u64 child_val;
if (child_counter->attr.inherit_stat)
perf_counter_read_event(child_counter, child);
child_val = atomic64_read(&child_counter->count); child_val = atomic64_read(&child_counter->count);
/* /*
@ -3985,7 +4206,8 @@ static void sync_child_counter(struct perf_counter *child_counter,
static void static void
__perf_counter_exit_task(struct perf_counter *child_counter, __perf_counter_exit_task(struct perf_counter *child_counter,
struct perf_counter_context *child_ctx) struct perf_counter_context *child_ctx,
struct task_struct *child)
{ {
struct perf_counter *parent_counter; struct perf_counter *parent_counter;
@ -3999,7 +4221,7 @@ __perf_counter_exit_task(struct perf_counter *child_counter,
* counters need to be zapped - but otherwise linger. * counters need to be zapped - but otherwise linger.
*/ */
if (parent_counter) { if (parent_counter) {
sync_child_counter(child_counter, parent_counter); sync_child_counter(child_counter, child);
free_counter(child_counter); free_counter(child_counter);
} }
} }
@ -4061,7 +4283,7 @@ void perf_counter_exit_task(struct task_struct *child)
again: again:
list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list, list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list,
list_entry) list_entry)
__perf_counter_exit_task(child_counter, child_ctx); __perf_counter_exit_task(child_counter, child_ctx, child);
/* /*
* If the last counter was a group counter, it will have appended all * If the last counter was a group counter, it will have appended all

30
tools/perf/CREDITS Normal file
View file

@ -0,0 +1,30 @@
Most of the infrastructure that 'perf' uses here has been reused
from the Git project, as of version:
66996ec: Sync with 1.6.2.4
Here is an (incomplete!) list of main contributors to those files
in util/* and elsewhere:
Alex Riesen
Christian Couder
Dmitry Potapov
Jeff King
Johannes Schindelin
Johannes Sixt
Junio C Hamano
Linus Torvalds
Matthias Kestenholz
Michal Ostrowski
Miklos Vajna
Petr Baudis
Pierre Habouzit
René Scharfe
Samuel Tardieu
Shawn O. Pearce
Steffen Prohaska
Steve Haslam
Thanks guys!
The full history of the files can be found in the upstream Git commits.

View file

@ -13,13 +13,25 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
This command displays the performance counter profile information recorded This command displays the performance counter profile information recorded
via perf report. via perf record.
OPTIONS OPTIONS
------- -------
-i:: -i::
--input=:: --input=::
Input file name. (default: perf.data) Input file name. (default: perf.data)
-d::
--dsos=::
Only consider symbols in these dsos. CSV that understands
file://filename entries.
-C::
--comms=::
Only consider symbols in these comms. CSV that understands
file://filename entries.
-S::
--symbols=::
Only consider these symbols. CSV that understands
file://filename entries.
SEE ALSO SEE ALSO
-------- --------

View file

@ -8,8 +8,8 @@ perf-stat - Run a command and gather performance counter statistics
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'perf stat' [-e <EVENT> | --event=EVENT] [-l] [-a] <command> 'perf stat' [-e <EVENT> | --event=EVENT] [-S] [-a] <command>
'perf stat' [-e <EVENT> | --event=EVENT] [-l] [-a] -- <command> [<options>] 'perf stat' [-e <EVENT> | --event=EVENT] [-S] [-a] -- <command> [<options>]
DESCRIPTION DESCRIPTION
----------- -----------
@ -40,7 +40,7 @@ OPTIONS
-a:: -a::
system-wide collection system-wide collection
-l:: -S::
scale counter values scale counter values
EXAMPLES EXAMPLES

View file

@ -290,7 +290,7 @@ LIB_FILE=libperf.a
LIB_H += ../../include/linux/perf_counter.h LIB_H += ../../include/linux/perf_counter.h
LIB_H += perf.h LIB_H += perf.h
LIB_H += types.h LIB_H += util/types.h
LIB_H += util/list.h LIB_H += util/list.h
LIB_H += util/rbtree.h LIB_H += util/rbtree.h
LIB_H += util/levenshtein.h LIB_H += util/levenshtein.h
@ -301,6 +301,7 @@ LIB_H += util/util.h
LIB_H += util/help.h LIB_H += util/help.h
LIB_H += util/strbuf.h LIB_H += util/strbuf.h
LIB_H += util/string.h LIB_H += util/string.h
LIB_H += util/strlist.h
LIB_H += util/run-command.h LIB_H += util/run-command.h
LIB_H += util/sigchain.h LIB_H += util/sigchain.h
LIB_H += util/symbol.h LIB_H += util/symbol.h
@ -322,12 +323,15 @@ LIB_OBJS += util/run-command.o
LIB_OBJS += util/quote.o LIB_OBJS += util/quote.o
LIB_OBJS += util/strbuf.o LIB_OBJS += util/strbuf.o
LIB_OBJS += util/string.o LIB_OBJS += util/string.o
LIB_OBJS += util/strlist.o
LIB_OBJS += util/usage.o LIB_OBJS += util/usage.o
LIB_OBJS += util/wrapper.o LIB_OBJS += util/wrapper.o
LIB_OBJS += util/sigchain.o LIB_OBJS += util/sigchain.o
LIB_OBJS += util/symbol.o LIB_OBJS += util/symbol.o
LIB_OBJS += util/color.o LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o
LIB_OBJS += util/callchain.o
BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-help.o

View file

@ -855,7 +855,7 @@ static unsigned long total = 0,
total_unknown = 0; total_unknown = 0;
static int static int
process_overflow_event(event_t *event, unsigned long offset, unsigned long head) process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{ {
char level; char level;
int show = 0; int show = 0;
@ -1013,10 +1013,10 @@ process_period_event(event_t *event, unsigned long offset, unsigned long head)
static int static int
process_event(event_t *event, unsigned long offset, unsigned long head) process_event(event_t *event, unsigned long offset, unsigned long head)
{ {
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
return process_overflow_event(event, offset, head);
switch (event->header.type) { switch (event->header.type) {
case PERF_EVENT_SAMPLE:
return process_sample_event(event, offset, head);
case PERF_EVENT_MMAP: case PERF_EVENT_MMAP:
return process_mmap_event(event, offset, head); return process_mmap_event(event, offset, head);

View file

@ -14,6 +14,8 @@
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/string.h" #include "util/string.h"
#include "util/header.h"
#include <unistd.h> #include <unistd.h>
#include <sched.h> #include <sched.h>
@ -39,6 +41,8 @@ static int force = 0;
static int append_file = 0; static int append_file = 0;
static int call_graph = 0; static int call_graph = 0;
static int verbose = 0; static int verbose = 0;
static int inherit_stat = 0;
static int no_samples = 0;
static long samples; static long samples;
static struct timeval last_read; static struct timeval last_read;
@ -52,7 +56,8 @@ static int nr_poll;
static int nr_cpu; static int nr_cpu;
static int file_new = 1; static int file_new = 1;
static struct perf_file_header file_header;
struct perf_header *header;
struct mmap_event { struct mmap_event {
struct perf_event_header header; struct perf_event_header header;
@ -306,12 +311,11 @@ static void pid_synthesize_mmap_samples(pid_t pid)
continue; continue;
pbf += n + 3; pbf += n + 3;
if (*pbf == 'x') { /* vm_exec */ if (*pbf == 'x') { /* vm_exec */
char *execname = strrchr(bf, ' '); char *execname = strchr(bf, '/');
if (execname == NULL || execname[1] != '/') if (execname == NULL)
continue; continue;
execname += 1;
size = strlen(execname); size = strlen(execname);
execname[size - 1] = '\0'; /* Remove \n */ execname[size - 1] = '\0'; /* Remove \n */
memcpy(mmap_ev.filename, execname, size); memcpy(mmap_ev.filename, execname, size);
@ -329,7 +333,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
fclose(fp); fclose(fp);
} }
static void synthesize_samples(void) static void synthesize_all(void)
{ {
DIR *proc; DIR *proc;
struct dirent dirent, *next; struct dirent dirent, *next;
@ -353,10 +357,35 @@ static void synthesize_samples(void)
static int group_fd; static int group_fd;
static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr)
{
struct perf_header_attr *h_attr;
if (nr < header->attrs) {
h_attr = header->attr[nr];
} else {
h_attr = perf_header_attr__new(a);
perf_header__add_attr(header, h_attr);
}
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)
{ {
struct perf_counter_attr *attr = attrs + counter; struct perf_counter_attr *attr = attrs + counter;
int track = 1; struct perf_header_attr *h_attr;
int track = !counter; /* only the first counter needs these */
struct {
u64 count;
u64 time_enabled;
u64 time_running;
u64 id;
} read_data;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
@ -366,25 +395,20 @@ static void create_counter(int counter, int cpu, pid_t pid)
attr->sample_freq = freq; attr->sample_freq = freq;
} }
if (no_samples)
attr->sample_freq = 0;
if (inherit_stat)
attr->inherit_stat = 1;
if (call_graph) if (call_graph)
attr->sample_type |= PERF_SAMPLE_CALLCHAIN; attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
if (file_new) {
file_header.sample_type = attr->sample_type;
} else {
if (file_header.sample_type != attr->sample_type) {
fprintf(stderr, "incompatible append\n");
exit(-1);
}
}
attr->mmap = track; attr->mmap = track;
attr->comm = track; attr->comm = track;
attr->inherit = (cpu < 0) && inherit; attr->inherit = (cpu < 0) && inherit;
attr->disabled = 1; attr->disabled = 1;
track = 0; /* only the first counter needs these */
try_again: try_again:
fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0);
@ -415,6 +439,22 @@ try_again:
exit(-1); exit(-1);
} }
h_attr = get_header_attr(attr, counter);
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);
}
perf_header_attr__add_id(h_attr, read_data.id);
assert(fd[nr_cpu][counter] >= 0); assert(fd[nr_cpu][counter] >= 0);
fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
@ -445,11 +485,6 @@ static void open_counters(int cpu, pid_t pid)
{ {
int counter; int counter;
if (pid > 0) {
pid_synthesize_comm_event(pid, 0);
pid_synthesize_mmap_samples(pid);
}
group_fd = -1; group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++)
create_counter(counter, cpu, pid); create_counter(counter, cpu, pid);
@ -459,17 +494,16 @@ static void open_counters(int cpu, pid_t pid)
static void atexit_header(void) static void atexit_header(void)
{ {
file_header.data_size += bytes_written; header->data_size += bytes_written;
if (pwrite(output, &file_header, sizeof(file_header), 0) == -1) perf_header__write(header, output);
perror("failed to write on file headers");
} }
static int __cmd_record(int argc, const char **argv) static int __cmd_record(int argc, const char **argv)
{ {
int i, counter; int i, counter;
struct stat st; struct stat st;
pid_t pid; pid_t pid = 0;
int flags; int flags;
int ret; int ret;
@ -500,22 +534,31 @@ static int __cmd_record(int argc, const char **argv)
exit(-1); exit(-1);
} }
if (!file_new) { if (!file_new)
if (read(output, &file_header, sizeof(file_header)) == -1) { header = perf_header__read(output);
perror("failed to read file headers"); else
exit(-1); header = perf_header__new();
}
lseek(output, file_header.data_size, SEEK_CUR);
}
atexit(atexit_header); atexit(atexit_header);
if (!system_wide) { if (!system_wide) {
open_counters(-1, target_pid != -1 ? target_pid : getpid()); pid = target_pid;
if (pid == -1)
pid = getpid();
open_counters(-1, pid);
} else for (i = 0; i < nr_cpus; i++) } else for (i = 0; i < nr_cpus; i++)
open_counters(i, target_pid); open_counters(i, target_pid);
if (file_new)
perf_header__write(header, output);
if (!system_wide) {
pid_synthesize_comm_event(pid, 0);
pid_synthesize_mmap_samples(pid);
} else
synthesize_all();
if (target_pid == -1 && argc) { if (target_pid == -1 && argc) {
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
@ -539,10 +582,7 @@ static int __cmd_record(int argc, const char **argv)
} }
} }
if (system_wide) for (;;) {
synthesize_samples();
while (!done) {
int hits = samples; int hits = samples;
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < nr_cpu; i++) {
@ -550,8 +590,11 @@ static int __cmd_record(int argc, const char **argv)
mmap_read(&mmap_array[i][counter]); mmap_read(&mmap_array[i][counter]);
} }
if (hits == samples) if (hits == samples) {
if (done)
break;
ret = poll(event_array, nr_poll, 100); ret = poll(event_array, nr_poll, 100);
}
} }
/* /*
@ -600,6 +643,10 @@ static const struct option options[] = {
"do call-graph (stack chain/backtrace) recording"), "do call-graph (stack chain/backtrace) recording"),
OPT_BOOLEAN('v', "verbose", &verbose, OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"), "be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('s', "stat", &inherit_stat,
"per thread counts"),
OPT_BOOLEAN('n', "no-samples", &no_samples,
"don't sample"),
OPT_END() OPT_END()
}; };

View file

@ -15,8 +15,11 @@
#include "util/rbtree.h" #include "util/rbtree.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/string.h" #include "util/string.h"
#include "util/callchain.h"
#include "util/strlist.h"
#include "perf.h" #include "perf.h"
#include "util/header.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" #include "util/parse-events.h"
@ -30,6 +33,8 @@ static char *vmlinux = NULL;
static char default_sort_order[] = "comm,dso"; static char default_sort_order[] = "comm,dso";
static char *sort_order = default_sort_order; static char *sort_order = default_sort_order;
static char *dso_list_str, *comm_list_str, *sym_list_str;
static struct strlist *dso_list, *comm_list, *sym_list;
static int input; static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@ -51,6 +56,9 @@ static char *parent_pattern = default_parent_pattern;
static regex_t parent_regex; static regex_t parent_regex;
static int exclude_other = 1; static int exclude_other = 1;
static int callchain;
static u64 sample_type;
struct ip_event { struct ip_event {
struct perf_event_header header; struct perf_event_header header;
@ -59,11 +67,6 @@ struct ip_event {
unsigned char __more_data[]; unsigned char __more_data[];
}; };
struct ip_callchain {
u64 nr;
u64 ips[0];
};
struct mmap_event { struct mmap_event {
struct perf_event_header header; struct perf_event_header header;
u32 pid, tid; u32 pid, tid;
@ -97,6 +100,13 @@ struct lost_event {
u64 lost; u64 lost;
}; };
struct read_event {
struct perf_event_header header;
u32 pid,tid;
u64 value;
u64 format[3];
};
typedef union event_union { typedef union event_union {
struct perf_event_header header; struct perf_event_header header;
struct ip_event ip; struct ip_event ip;
@ -105,6 +115,7 @@ typedef union event_union {
struct fork_event fork; struct fork_event fork;
struct period_event period; struct period_event period;
struct lost_event lost; struct lost_event lost;
struct read_event read;
} event_t; } event_t;
static LIST_HEAD(dsos); static LIST_HEAD(dsos);
@ -229,7 +240,7 @@ static u64 vdso__map_ip(struct map *map, u64 ip)
static inline int is_anon_memory(const char *filename) static inline int is_anon_memory(const char *filename)
{ {
return strcmp(filename, "//anon") == 0; return strcmp(filename, "//anon") == 0;
} }
static struct map *map__new(struct mmap_event *event) static struct map *map__new(struct mmap_event *event)
@ -400,9 +411,27 @@ static void thread__insert_map(struct thread *self, struct map *map)
list_for_each_entry_safe(pos, tmp, &self->maps, node) { list_for_each_entry_safe(pos, tmp, &self->maps, node) {
if (map__overlap(pos, map)) { if (map__overlap(pos, map)) {
list_del_init(&pos->node); if (verbose >= 2) {
/* XXX leaks dsos */ printf("overlapping maps:\n");
free(pos); map__fprintf(map, stdout);
map__fprintf(pos, stdout);
}
if (map->start <= pos->start && map->end > pos->start)
pos->start = map->end;
if (map->end >= pos->end && map->start < pos->end)
pos->end = map->start;
if (verbose >= 2) {
printf("after collision:\n");
map__fprintf(pos, stdout);
}
if (pos->start >= pos->end) {
list_del_init(&pos->node);
free(pos);
}
} }
} }
@ -464,17 +493,19 @@ static size_t threads__fprintf(FILE *fp)
static struct rb_root hist; static struct rb_root hist;
struct hist_entry { struct hist_entry {
struct rb_node rb_node; struct rb_node rb_node;
struct thread *thread; struct thread *thread;
struct map *map; struct map *map;
struct dso *dso; struct dso *dso;
struct symbol *sym; struct symbol *sym;
struct symbol *parent; struct symbol *parent;
u64 ip; u64 ip;
char level; char level;
struct callchain_node callchain;
struct rb_root sorted_chain;
u64 count; u64 count;
}; };
/* /*
@ -744,6 +775,48 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
return cmp; return cmp;
} }
static size_t
callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples)
{
struct callchain_list *chain;
size_t ret = 0;
if (!self)
return 0;
ret += callchain__fprintf(fp, self->parent, total_samples);
list_for_each_entry(chain, &self->val, list)
ret += fprintf(fp, " %p\n", (void *)chain->ip);
return ret;
}
static size_t
hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
u64 total_samples)
{
struct rb_node *rb_node;
struct callchain_node *chain;
size_t ret = 0;
rb_node = rb_first(&self->sorted_chain);
while (rb_node) {
double percent;
chain = rb_entry(rb_node, struct callchain_node, rb_node);
percent = chain->hit * 100.0 / total_samples;
ret += fprintf(fp, " %6.2f%%\n", percent);
ret += callchain__fprintf(fp, chain, total_samples);
ret += fprintf(fp, "\n");
rb_node = rb_next(rb_node);
}
return ret;
}
static size_t static size_t
hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
{ {
@ -784,6 +857,9 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
ret += fprintf(fp, "\n"); ret += fprintf(fp, "\n");
if (callchain)
hist_entry_callchain__fprintf(fp, self, total_samples);
return ret; return ret;
} }
@ -797,7 +873,7 @@ resolve_symbol(struct thread *thread, struct map **mapp,
{ {
struct dso *dso = dsop ? *dsop : NULL; struct dso *dso = dsop ? *dsop : NULL;
struct map *map = mapp ? *mapp : NULL; struct map *map = mapp ? *mapp : NULL;
uint64_t ip = *ipp; u64 ip = *ipp;
if (!thread) if (!thread)
return NULL; return NULL;
@ -814,7 +890,6 @@ resolve_symbol(struct thread *thread, struct map **mapp,
*mapp = map; *mapp = map;
got_map: got_map:
ip = map->map_ip(map, ip); ip = map->map_ip(map, ip);
*ipp = ip;
dso = map->dso; dso = map->dso;
} else { } else {
@ -828,6 +903,8 @@ got_map:
dso = kernel_dso; dso = kernel_dso;
} }
dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
*ipp = ip;
if (dsop) if (dsop)
*dsop = dso; *dsop = dso;
@ -867,6 +944,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
.level = level, .level = level,
.count = count, .count = count,
.parent = NULL, .parent = NULL,
.sorted_chain = RB_ROOT
}; };
int cmp; int cmp;
@ -909,6 +987,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
if (!cmp) { if (!cmp) {
he->count += count; he->count += count;
if (callchain)
append_chain(&he->callchain, chain);
return 0; return 0;
} }
@ -922,6 +1002,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
if (!he) if (!he)
return -ENOMEM; return -ENOMEM;
*he = entry; *he = entry;
if (callchain) {
callchain_init(&he->callchain);
append_chain(&he->callchain, chain);
}
rb_link_node(&he->rb_node, parent, p); rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, &hist); rb_insert_color(&he->rb_node, &hist);
@ -998,6 +1082,9 @@ static void output__insert_entry(struct hist_entry *he)
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct hist_entry *iter; struct hist_entry *iter;
if (callchain)
sort_chain_to_rbtree(&he->sorted_chain, &he->callchain);
while (*p != NULL) { while (*p != NULL) {
parent = *p; parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node); iter = rb_entry(parent, struct hist_entry, rb_node);
@ -1115,7 +1202,7 @@ static int validate_chain(struct ip_callchain *chain, event_t *event)
} }
static int static int
process_overflow_event(event_t *event, unsigned long offset, unsigned long head) process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{ {
char level; char level;
int show = 0; int show = 0;
@ -1127,12 +1214,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
void *more_data = event->ip.__more_data; void *more_data = event->ip.__more_data;
struct ip_callchain *chain = NULL; struct ip_callchain *chain = NULL;
if (event->header.type & PERF_SAMPLE_PERIOD) { if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data; period = *(u64 *)more_data;
more_data += sizeof(u64); more_data += sizeof(u64);
} }
dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p period: %Ld\n", dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n",
(void *)(offset + head), (void *)(offset + head),
(void *)(long)(event->header.size), (void *)(long)(event->header.size),
event->header.misc, event->header.misc,
@ -1140,7 +1227,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
(void *)(long)ip, (void *)(long)ip,
(long long)period); (long long)period);
if (event->header.type & PERF_SAMPLE_CALLCHAIN) { if (sample_type & PERF_SAMPLE_CALLCHAIN) {
int i; int i;
chain = (void *)more_data; chain = (void *)more_data;
@ -1166,6 +1253,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
return -1; return -1;
} }
if (comm_list && !strlist__has_entry(comm_list, thread->comm))
return 0;
if (event->header.misc & PERF_EVENT_MISC_KERNEL) { if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
show = SHOW_KERNEL; show = SHOW_KERNEL;
level = 'k'; level = 'k';
@ -1188,6 +1278,12 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
if (show & show_mask) { if (show & show_mask) {
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name))
return 0;
if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
return 0;
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
eprintf("problem incrementing symbol count, skipping event\n"); eprintf("problem incrementing symbol count, skipping event\n");
return -1; return -1;
@ -1327,15 +1423,28 @@ static void trace_event(event_t *event)
dprintf(".\n"); dprintf(".\n");
} }
static int
process_read_event(event_t *event, unsigned long offset, unsigned long head)
{
dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->read.pid,
event->read.tid,
event->read.value);
return 0;
}
static int static int
process_event(event_t *event, unsigned long offset, unsigned long head) process_event(event_t *event, unsigned long offset, unsigned long head)
{ {
trace_event(event); trace_event(event);
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
return process_overflow_event(event, offset, head);
switch (event->header.type) { switch (event->header.type) {
case PERF_EVENT_SAMPLE:
return process_sample_event(event, offset, head);
case PERF_EVENT_MMAP: case PERF_EVENT_MMAP:
return process_mmap_event(event, offset, head); return process_mmap_event(event, offset, head);
@ -1351,6 +1460,9 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
case PERF_EVENT_LOST: case PERF_EVENT_LOST:
return process_lost_event(event, offset, head); return process_lost_event(event, offset, head);
case PERF_EVENT_READ:
return process_read_event(event, offset, head);
/* /*
* We dont process them right now but they are fine: * We dont process them right now but they are fine:
*/ */
@ -1366,13 +1478,30 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0; return 0;
} }
static struct perf_file_header file_header; static struct perf_header *header;
static u64 perf_header__sample_type(void)
{
u64 sample_type = 0;
int i;
for (i = 0; i < header->attrs; i++) {
struct perf_header_attr *attr = header->attr[i];
if (!sample_type)
sample_type = attr->attr.sample_type;
else if (sample_type != attr->attr.sample_type)
die("non matching sample_type");
}
return sample_type;
}
static int __cmd_report(void) static int __cmd_report(void)
{ {
int ret, rc = EXIT_FAILURE; int ret, rc = EXIT_FAILURE;
unsigned long offset = 0; unsigned long offset = 0;
unsigned long head = sizeof(file_header); unsigned long head, shift;
struct stat stat; struct stat stat;
event_t *event; event_t *event;
uint32_t size; uint32_t size;
@ -1400,13 +1529,12 @@ static int __cmd_report(void)
exit(0); exit(0);
} }
if (read(input, &file_header, sizeof(file_header)) == -1) { header = perf_header__read(input);
perror("failed to read file headers"); head = header->data_offset;
exit(-1);
}
if (sort__has_parent && sample_type = perf_header__sample_type();
!(file_header.sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
fprintf(stderr, "selected --sort parent, but no callchain data\n"); fprintf(stderr, "selected --sort parent, but no callchain data\n");
exit(-1); exit(-1);
} }
@ -1426,6 +1554,11 @@ static int __cmd_report(void)
cwd = NULL; cwd = NULL;
cwdlen = 0; cwdlen = 0;
} }
shift = page_size * (head / page_size);
offset += shift;
head -= shift;
remap: remap:
buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset); MAP_SHARED, input, offset);
@ -1442,9 +1575,10 @@ more:
size = 8; size = 8;
if (head + event->header.size >= page_size * mmap_window) { if (head + event->header.size >= page_size * mmap_window) {
unsigned long shift = page_size * (head / page_size);
int ret; int ret;
shift = page_size * (head / page_size);
ret = munmap(buf, page_size * mmap_window); ret = munmap(buf, page_size * mmap_window);
assert(ret == 0); assert(ret == 0);
@ -1482,7 +1616,7 @@ more:
head += size; head += size;
if (offset + head >= sizeof(file_header) + file_header.data_size) if (offset + head >= header->data_offset + header->data_size)
goto done; goto done;
if (offset + head < stat.st_size) if (offset + head < stat.st_size)
@ -1536,6 +1670,13 @@ static const struct option options[] = {
"regex filter to identify parent, see: '--sort parent'"), "regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &exclude_other, OPT_BOOLEAN('x', "exclude-other", &exclude_other,
"Only display entries with parent-match"), "Only display entries with parent-match"),
OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"),
OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
OPT_END() OPT_END()
}; };
@ -1554,6 +1695,19 @@ static void setup_sorting(void)
free(str); free(str);
} }
static void setup_list(struct strlist **list, const char *list_str,
const char *list_name)
{
if (list_str) {
*list = strlist__new(true, list_str);
if (!*list) {
fprintf(stderr, "problems parsing %s list\n",
list_name);
exit(129);
}
}
}
int cmd_report(int argc, const char **argv, const char *prefix) int cmd_report(int argc, const char **argv, const char *prefix)
{ {
symbol__init(); symbol__init();
@ -1575,6 +1729,10 @@ int cmd_report(int argc, const char **argv, const char *prefix)
if (argc) if (argc)
usage_with_options(report_usage, options); usage_with_options(report_usage, options);
setup_list(&dso_list, dso_list_str, "dso");
setup_list(&comm_list, comm_list_str, "comm");
setup_list(&sym_list, sym_list_str, "symbol");
setup_pager(); setup_pager();
return __cmd_report(); return __cmd_report();

View file

@ -32,6 +32,7 @@
* Wu Fengguang <fengguang.wu@intel.com> * Wu Fengguang <fengguang.wu@intel.com>
* Mike Galbraith <efault@gmx.de> * Mike Galbraith <efault@gmx.de>
* Paul Mackerras <paulus@samba.org> * Paul Mackerras <paulus@samba.org>
* Jaswinder Singh Rajput <jaswinder@kernel.org>
* *
* Released under the GPL v2. (and only v2, not any later version) * Released under the GPL v2. (and only v2, not any later version)
*/ */
@ -45,7 +46,7 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <math.h> #include <math.h>
static struct perf_counter_attr default_attrs[MAX_COUNTERS] = { static struct perf_counter_attr default_attrs[] = {
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES}, { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES},
@ -59,42 +60,28 @@ static struct perf_counter_attr default_attrs[MAX_COUNTERS] = {
}; };
#define MAX_RUN 100
static int system_wide = 0; static int system_wide = 0;
static int inherit = 1;
static int verbose = 0; static int verbose = 0;
static int nr_cpus = 0;
static int run_idx = 0;
static int run_count = 1;
static int inherit = 1;
static int scale = 1;
static int target_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 target_pid = -1;
static int nr_cpus = 0;
static unsigned int page_size;
static int scale = 1;
static const unsigned int default_count[] = {
1000000,
1000000,
10000,
10000,
1000000,
10000,
};
#define MAX_RUN 100
static int run_count = 1;
static int run_idx = 0;
static u64 event_res[MAX_RUN][MAX_COUNTERS][3];
static u64 event_scaled[MAX_RUN][MAX_COUNTERS];
//static u64 event_hist[MAX_RUN][MAX_COUNTERS][3];
static u64 runtime_nsecs[MAX_RUN]; static u64 runtime_nsecs[MAX_RUN];
static u64 walltime_nsecs[MAX_RUN]; static u64 walltime_nsecs[MAX_RUN];
static u64 runtime_cycles[MAX_RUN]; static u64 runtime_cycles[MAX_RUN];
static u64 event_res[MAX_RUN][MAX_COUNTERS][3];
static u64 event_scaled[MAX_RUN][MAX_COUNTERS];
static u64 event_res_avg[MAX_COUNTERS][3]; static u64 event_res_avg[MAX_COUNTERS][3];
static u64 event_res_noise[MAX_COUNTERS][3]; static u64 event_res_noise[MAX_COUNTERS][3];
@ -109,7 +96,10 @@ static u64 walltime_nsecs_noise;
static u64 runtime_cycles_avg; static u64 runtime_cycles_avg;
static u64 runtime_cycles_noise; static u64 runtime_cycles_noise;
static void create_perf_stat_counter(int counter) #define ERR_PERF_OPEN \
"Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n"
static void create_perf_stat_counter(int counter, int pid)
{ {
struct perf_counter_attr *attr = attrs + counter; struct perf_counter_attr *attr = attrs + counter;
@ -119,20 +109,21 @@ static void create_perf_stat_counter(int counter)
if (system_wide) { if (system_wide) {
int cpu; int cpu;
for (cpu = 0; cpu < nr_cpus; cpu ++) { for (cpu = 0; cpu < nr_cpus; cpu++) {
fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0); fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0);
if (fd[cpu][counter] < 0 && verbose) { if (fd[cpu][counter] < 0 && verbose)
printf("Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n", counter, fd[cpu][counter], strerror(errno)); fprintf(stderr, ERR_PERF_OPEN, counter,
} fd[cpu][counter], strerror(errno));
} }
} else { } else {
attr->inherit = inherit; attr->inherit = inherit;
attr->disabled = 1; attr->disabled = 1;
attr->enable_on_exec = 1;
fd[0][counter] = sys_perf_counter_open(attr, 0, -1, -1, 0); fd[0][counter] = sys_perf_counter_open(attr, pid, -1, -1, 0);
if (fd[0][counter] < 0 && verbose) { if (fd[0][counter] < 0 && verbose)
printf("Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n", counter, fd[0][counter], strerror(errno)); fprintf(stderr, ERR_PERF_OPEN, counter,
} fd[0][counter], strerror(errno));
} }
} }
@ -168,7 +159,7 @@ static void read_counter(int counter)
count[0] = count[1] = count[2] = 0; count[0] = count[1] = count[2] = 0;
nv = scale ? 3 : 1; nv = scale ? 3 : 1;
for (cpu = 0; cpu < nr_cpus; cpu ++) { for (cpu = 0; cpu < nr_cpus; cpu++) {
if (fd[cpu][counter] < 0) if (fd[cpu][counter] < 0)
continue; continue;
@ -215,32 +206,67 @@ static int run_perf_stat(int argc, const char **argv)
int status = 0; int status = 0;
int counter; int counter;
int pid; int pid;
int child_ready_pipe[2], go_pipe[2];
char buf;
if (!system_wide) if (!system_wide)
nr_cpus = 1; nr_cpus = 1;
for (counter = 0; counter < nr_counters; counter++) if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) {
create_perf_stat_counter(counter); perror("failed to create pipes");
exit(1);
/* }
* Enable counters and exec the command:
*/
t0 = rdclock();
prctl(PR_TASK_PERF_COUNTERS_ENABLE);
if ((pid = fork()) < 0) if ((pid = fork()) < 0)
perror("failed to fork"); perror("failed to fork");
if (!pid) { if (!pid) {
if (execvp(argv[0], (char **)argv)) { close(child_ready_pipe[0]);
perror(argv[0]); close(go_pipe[1]);
exit(-1); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
}
/*
* Do a dummy execvp to get the PLT entry resolved,
* so we avoid the resolver overhead on the real
* execvp call.
*/
execvp("", (char **)argv);
/*
* Tell the parent we're ready to go
*/
close(child_ready_pipe[1]);
/*
* Wait until the parent tells us to go.
*/
read(go_pipe[0], &buf, 1);
execvp(argv[0], (char **)argv);
perror(argv[0]);
exit(-1);
} }
/*
* Wait for the child to be ready to exec.
*/
close(child_ready_pipe[1]);
close(go_pipe[0]);
read(child_ready_pipe[0], &buf, 1);
close(child_ready_pipe[0]);
for (counter = 0; counter < nr_counters; counter++)
create_perf_stat_counter(counter, pid);
/*
* Enable counters and exec the command:
*/
t0 = rdclock();
close(go_pipe[1]);
wait(&status); wait(&status);
prctl(PR_TASK_PERF_COUNTERS_DISABLE);
t1 = rdclock(); t1 = rdclock();
walltime_nsecs[run_idx] = t1 - t0; walltime_nsecs[run_idx] = t1 - t0;
@ -262,7 +288,7 @@ static void nsec_printout(int counter, u64 *count, u64 *noise)
{ {
double msecs = (double)count[0] / 1000000; double msecs = (double)count[0] / 1000000;
fprintf(stderr, " %14.6f %-20s", msecs, event_name(counter)); fprintf(stderr, " %14.6f %-24s", msecs, event_name(counter));
if (attrs[counter].type == PERF_TYPE_SOFTWARE && if (attrs[counter].type == PERF_TYPE_SOFTWARE &&
attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) { attrs[counter].config == PERF_COUNT_SW_TASK_CLOCK) {
@ -276,7 +302,7 @@ static void nsec_printout(int counter, u64 *count, u64 *noise)
static void abs_printout(int counter, u64 *count, u64 *noise) static void abs_printout(int counter, u64 *count, u64 *noise)
{ {
fprintf(stderr, " %14Ld %-20s", count[0], event_name(counter)); fprintf(stderr, " %14Ld %-24s", count[0], event_name(counter));
if (runtime_cycles_avg && if (runtime_cycles_avg &&
attrs[counter].type == PERF_TYPE_HARDWARE && attrs[counter].type == PERF_TYPE_HARDWARE &&
@ -306,7 +332,7 @@ static void print_counter(int counter)
scaled = event_scaled_avg[counter]; scaled = event_scaled_avg[counter];
if (scaled == -1) { if (scaled == -1) {
fprintf(stderr, " %14s %-20s\n", fprintf(stderr, " %14s %-24s\n",
"<not counted>", event_name(counter)); "<not counted>", event_name(counter));
return; return;
} }
@ -364,8 +390,11 @@ static void calc_avg(void)
event_res_avg[j]+1, event_res[i][j]+1); event_res_avg[j]+1, event_res[i][j]+1);
update_avg("counter/2", j, update_avg("counter/2", j,
event_res_avg[j]+2, event_res[i][j]+2); event_res_avg[j]+2, event_res[i][j]+2);
update_avg("scaled", j, if (event_scaled[i][j] != -1)
event_scaled_avg + j, event_scaled[i]+j); update_avg("scaled", j,
event_scaled_avg + j, event_scaled[i]+j);
else
event_scaled_avg[j] = -1;
} }
} }
runtime_nsecs_avg /= run_count; runtime_nsecs_avg /= run_count;
@ -429,11 +458,14 @@ static void print_stat(int argc, const char **argv)
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++)
print_counter(counter); print_counter(counter);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " %14.9f seconds time elapsed.\n", fprintf(stderr, " %14.9f seconds time elapsed",
(double)walltime_nsecs_avg/1e9); (double)walltime_nsecs_avg/1e9);
fprintf(stderr, "\n"); if (run_count > 1) {
fprintf(stderr, " ( +- %7.3f%% )",
100.0*(double)walltime_nsecs_noise/(double)walltime_nsecs_avg);
}
fprintf(stderr, "\n\n");
} }
static volatile int signr = -1; static volatile int signr = -1;
@ -466,13 +498,15 @@ static const struct option options[] = {
OPT_INTEGER('p', "pid", &target_pid, OPT_INTEGER('p', "pid", &target_pid,
"stat events on existing pid"), "stat events on existing pid"),
OPT_BOOLEAN('a', "all-cpus", &system_wide, OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"), "system-wide collection from all CPUs"),
OPT_BOOLEAN('S', "scale", &scale, OPT_BOOLEAN('S', "scale", &scale,
"scale/normalize counters"), "scale/normalize counters"),
OPT_BOOLEAN('v', "verbose", &verbose, OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"), "be more verbose (show counter open errors, etc)"),
OPT_INTEGER('r', "repeat", &run_count, OPT_INTEGER('r', "repeat", &run_count,
"repeat command and print average + stddev (max: 100)"), "repeat command and print average + stddev (max: 100)"),
OPT_BOOLEAN('n', "null", &null_run,
"null run - dont start any counters"),
OPT_END() OPT_END()
}; };
@ -480,18 +514,17 @@ int cmd_stat(int argc, const char **argv, const char *prefix)
{ {
int status; int status;
page_size = sysconf(_SC_PAGE_SIZE);
memcpy(attrs, default_attrs, sizeof(attrs));
argc = parse_options(argc, argv, options, stat_usage, 0); argc = parse_options(argc, argv, options, stat_usage, 0);
if (!argc) if (!argc)
usage_with_options(stat_usage, options); usage_with_options(stat_usage, options);
if (run_count <= 0 || run_count > MAX_RUN) if (run_count <= 0 || run_count > MAX_RUN)
usage_with_options(stat_usage, options); usage_with_options(stat_usage, options);
if (!nr_counters) /* Set attrs and nr_counters if no event is selected and !null_run */
nr_counters = 8; if (!null_run && !nr_counters) {
memcpy(attrs, default_attrs, sizeof(default_attrs));
nr_counters = ARRAY_SIZE(default_attrs);
}
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
assert(nr_cpus <= MAX_NR_CPUS); assert(nr_cpus <= MAX_NR_CPUS);
@ -511,7 +544,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix)
status = 0; status = 0;
for (run_idx = 0; run_idx < run_count; run_idx++) { for (run_idx = 0; run_idx < run_count; run_idx++) {
if (run_count != 1 && verbose) if (run_count != 1 && verbose)
fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx+1); fprintf(stderr, "[ perf stat: executing run #%d ... ]\n", run_idx + 1);
status = run_perf_stat(argc, argv); status = run_perf_stat(argc, argv);
} }

View file

@ -392,11 +392,11 @@ static void record_ip(u64 ip, int counter)
samples--; samples--;
} }
static void process_event(u64 ip, int counter) static void process_event(u64 ip, int counter, int user)
{ {
samples++; samples++;
if (ip < min_ip || ip > max_ip) { if (user) {
userspace_samples++; userspace_samples++;
return; return;
} }
@ -509,9 +509,10 @@ static void mmap_read_counter(struct mmap_data *md)
old += size; old += size;
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { if (event->header.type == PERF_EVENT_SAMPLE) {
if (event->header.type & PERF_SAMPLE_IP) int user =
process_event(event->ip.ip, md->counter); (event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK) == PERF_EVENT_MISC_USER;
process_event(event->ip.ip, md->counter, user);
} }
} }

View file

@ -25,7 +25,7 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include "../../include/linux/perf_counter.h" #include "../../include/linux/perf_counter.h"
#include "types.h" #include "util/types.h"
/* /*
* prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all
@ -72,10 +72,9 @@ sys_perf_counter_open(struct perf_counter_attr *attr,
#define MAX_COUNTERS 256 #define MAX_COUNTERS 256
#define MAX_NR_CPUS 256 #define MAX_NR_CPUS 256
struct perf_file_header { struct ip_callchain {
u64 version; u64 nr;
u64 sample_type; u64 ips[0];
u64 data_size;
}; };
#endif #endif

174
tools/perf/util/callchain.c Normal file
View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com>
*
* Handle the callchains from the stream in an ad-hoc radix tree and then
* sort them in an rbtree.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include "callchain.h"
static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct callchain_node *rnode;
while (*p) {
parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node);
if (rnode->hit < chain->hit)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&chain->rb_node, parent, p);
rb_insert_color(&chain->rb_node, root);
}
/*
* Once we get every callchains from the stream, we can now
* sort them by hit
*/
void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node)
{
struct callchain_node *child;
list_for_each_entry(child, &node->children, brothers)
sort_chain_to_rbtree(rb_root, child);
if (node->hit)
rb_insert_callchain(rb_root, node);
}
static struct callchain_node *create_child(struct callchain_node *parent)
{
struct callchain_node *new;
new = malloc(sizeof(*new));
if (!new) {
perror("not enough memory to create child for code path tree");
return NULL;
}
new->parent = parent;
INIT_LIST_HEAD(&new->children);
INIT_LIST_HEAD(&new->val);
list_add_tail(&new->brothers, &parent->children);
return new;
}
static void
fill_node(struct callchain_node *node, struct ip_callchain *chain, int start)
{
int i;
for (i = start; i < chain->nr; i++) {
struct callchain_list *call;
call = malloc(sizeof(*chain));
if (!call) {
perror("not enough memory for the code path tree");
return;
}
call->ip = chain->ips[i];
list_add_tail(&call->list, &node->val);
}
node->val_nr = i - start;
}
static void add_child(struct callchain_node *parent, struct ip_callchain *chain)
{
struct callchain_node *new;
new = create_child(parent);
fill_node(new, chain, parent->val_nr);
new->hit = 1;
}
static void
split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
struct callchain_list *to_split, int idx)
{
struct callchain_node *new;
/* split */
new = create_child(parent);
list_move_tail(&to_split->list, &new->val);
new->hit = parent->hit;
parent->hit = 0;
parent->val_nr = idx;
/* create the new one */
add_child(parent, chain);
}
static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
int start);
static int
__append_chain_children(struct callchain_node *root, struct ip_callchain *chain)
{
struct callchain_node *rnode;
/* lookup in childrens */
list_for_each_entry(rnode, &root->children, brothers) {
int ret = __append_chain(rnode, chain, root->val_nr);
if (!ret)
return 0;
}
return -1;
}
static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
int start)
{
struct callchain_list *cnode;
int i = start;
bool found = false;
/* lookup in the current node */
list_for_each_entry(cnode, &root->val, list) {
if (cnode->ip != chain->ips[i++])
break;
if (!found)
found = true;
if (i == chain->nr)
break;
}
/* matches not, relay on the parent */
if (!found)
return -1;
/* we match only a part of the node. Split it and add the new chain */
if (i < root->val_nr) {
split_add_child(root, chain, cnode, i);
return 0;
}
/* we match 100% of the path, increment the hit */
if (i == root->val_nr) {
root->hit++;
return 0;
}
return __append_chain_children(root, chain);
}
void append_chain(struct callchain_node *root, struct ip_callchain *chain)
{
if (__append_chain_children(root, chain) == -1)
add_child(root, chain);
}

View file

@ -0,0 +1,33 @@
#ifndef __PERF_CALLCHAIN_H
#define __PERF_CALLCHAIN_H
#include "../perf.h"
#include "list.h"
#include "rbtree.h"
struct callchain_node {
struct callchain_node *parent;
struct list_head brothers;
struct list_head children;
struct list_head val;
struct rb_node rb_node;
int val_nr;
int hit;
};
struct callchain_list {
unsigned long ip;
struct list_head list;
};
static inline void callchain_init(struct callchain_node *node)
{
INIT_LIST_HEAD(&node->brothers);
INIT_LIST_HEAD(&node->children);
INIT_LIST_HEAD(&node->val);
}
void append_chain(struct callchain_node *root, struct ip_callchain *chain);
void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node);
#endif

242
tools/perf/util/header.c Normal file
View file

@ -0,0 +1,242 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "util.h"
#include "header.h"
/*
*
*/
struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr)
{
struct perf_header_attr *self = malloc(sizeof(*self));
if (!self)
die("nomem");
self->attr = *attr;
self->ids = 0;
self->size = 1;
self->id = malloc(sizeof(u64));
if (!self->id)
die("nomem");
return self;
}
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
{
int pos = self->ids;
self->ids++;
if (self->ids > self->size) {
self->size *= 2;
self->id = realloc(self->id, self->size * sizeof(u64));
if (!self->id)
die("nomem");
}
self->id[pos] = id;
}
/*
*
*/
struct perf_header *perf_header__new(void)
{
struct perf_header *self = malloc(sizeof(*self));
if (!self)
die("nomem");
self->frozen = 0;
self->attrs = 0;
self->size = 1;
self->attr = malloc(sizeof(void *));
if (!self->attr)
die("nomem");
self->data_offset = 0;
self->data_size = 0;
return self;
}
void perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr)
{
int pos = self->attrs;
if (self->frozen)
die("frozen");
self->attrs++;
if (self->attrs > self->size) {
self->size *= 2;
self->attr = realloc(self->attr, self->size * sizeof(void *));
if (!self->attr)
die("nomem");
}
self->attr[pos] = attr;
}
static const char *__perf_magic = "PERFFILE";
#define PERF_MAGIC (*(u64 *)__perf_magic)
struct perf_file_section {
u64 offset;
u64 size;
};
struct perf_file_attr {
struct perf_counter_attr attr;
struct perf_file_section ids;
};
struct perf_file_header {
u64 magic;
u64 size;
u64 attr_size;
struct perf_file_section attrs;
struct perf_file_section data;
};
static void do_write(int fd, void *buf, size_t size)
{
while (size) {
int ret = write(fd, buf, size);
if (ret < 0)
die("failed to write");
size -= ret;
buf += ret;
}
}
void perf_header__write(struct perf_header *self, int fd)
{
struct perf_file_header f_header;
struct perf_file_attr f_attr;
struct perf_header_attr *attr;
int i;
lseek(fd, sizeof(f_header), SEEK_SET);
for (i = 0; i < self->attrs; i++) {
attr = self->attr[i];
attr->id_offset = lseek(fd, 0, SEEK_CUR);
do_write(fd, attr->id, attr->ids * sizeof(u64));
}
self->attr_offset = lseek(fd, 0, SEEK_CUR);
for (i = 0; i < self->attrs; i++) {
attr = self->attr[i];
f_attr = (struct perf_file_attr){
.attr = attr->attr,
.ids = {
.offset = attr->id_offset,
.size = attr->ids * sizeof(u64),
}
};
do_write(fd, &f_attr, sizeof(f_attr));
}
self->data_offset = lseek(fd, 0, SEEK_CUR);
f_header = (struct perf_file_header){
.magic = PERF_MAGIC,
.size = sizeof(f_header),
.attr_size = sizeof(f_attr),
.attrs = {
.offset = self->attr_offset,
.size = self->attrs * sizeof(f_attr),
},
.data = {
.offset = self->data_offset,
.size = self->data_size,
},
};
lseek(fd, 0, SEEK_SET);
do_write(fd, &f_header, sizeof(f_header));
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
self->frozen = 1;
}
static void do_read(int fd, void *buf, size_t size)
{
while (size) {
int ret = read(fd, buf, size);
if (ret < 0)
die("failed to read");
size -= ret;
buf += ret;
}
}
struct perf_header *perf_header__read(int fd)
{
struct perf_header *self = perf_header__new();
struct perf_file_header f_header;
struct perf_file_attr f_attr;
u64 f_id;
int nr_attrs, nr_ids, i, j;
lseek(fd, 0, SEEK_SET);
do_read(fd, &f_header, sizeof(f_header));
if (f_header.magic != PERF_MAGIC ||
f_header.size != sizeof(f_header) ||
f_header.attr_size != sizeof(f_attr))
die("incompatible file format");
nr_attrs = f_header.attrs.size / sizeof(f_attr);
lseek(fd, f_header.attrs.offset, SEEK_SET);
for (i = 0; i < nr_attrs; i++) {
struct perf_header_attr *attr;
off_t tmp = lseek(fd, 0, SEEK_CUR);
do_read(fd, &f_attr, sizeof(f_attr));
attr = perf_header_attr__new(&f_attr.attr);
nr_ids = f_attr.ids.size / sizeof(u64);
lseek(fd, f_attr.ids.offset, SEEK_SET);
for (j = 0; j < nr_ids; j++) {
do_read(fd, &f_id, sizeof(f_id));
perf_header_attr__add_id(attr, f_id);
}
perf_header__add_attr(self, attr);
lseek(fd, tmp, SEEK_SET);
}
self->data_offset = f_header.data.offset;
self->data_size = f_header.data.size;
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
self->frozen = 1;
return self;
}

37
tools/perf/util/header.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _PERF_HEADER_H
#define _PERF_HEADER_H
#include "../../../include/linux/perf_counter.h"
#include <sys/types.h>
#include "types.h"
struct perf_header_attr {
struct perf_counter_attr attr;
int ids, size;
u64 *id;
off_t id_offset;
};
struct perf_header {
int frozen;
int attrs, size;
struct perf_header_attr **attr;
off_t attr_offset;
u64 data_offset;
u64 data_size;
};
struct perf_header *perf_header__read(int fd);
void perf_header__write(struct perf_header *self, int fd);
void perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr);
struct perf_header_attr *
perf_header_attr__new(struct perf_counter_attr *attr);
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
struct perf_header *perf_header__new(void);
#endif /* _PERF_HEADER_H */

View file

@ -126,21 +126,6 @@ static int is_executable(const char *name)
!S_ISREG(st.st_mode)) !S_ISREG(st.st_mode))
return 0; return 0;
#ifdef __MINGW32__
/* cannot trust the executable bit, peek into the file instead */
char buf[3] = { 0 };
int n;
int fd = open(name, O_RDONLY);
st.st_mode &= ~S_IXUSR;
if (fd >= 0) {
n = read(fd, buf, 2);
if (n == 2)
/* DOS executables start with "MZ" */
if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
st.st_mode |= S_IXUSR;
close(fd);
}
#endif
return st.st_mode & S_IXUSR; return st.st_mode & S_IXUSR;
} }

View file

@ -9,7 +9,6 @@
static int spawned_pager; static int spawned_pager;
#ifndef __MINGW32__
static void pager_preexec(void) static void pager_preexec(void)
{ {
/* /*
@ -24,7 +23,6 @@ static void pager_preexec(void)
setenv("LESS", "FRSX", 0); setenv("LESS", "FRSX", 0);
} }
#endif
static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
static struct child_process pager_process; static struct child_process pager_process;
@ -70,9 +68,8 @@ void setup_pager(void)
pager_argv[2] = pager; pager_argv[2] = pager;
pager_process.argv = pager_argv; pager_process.argv = pager_argv;
pager_process.in = -1; pager_process.in = -1;
#ifndef __MINGW32__
pager_process.preexec_cb = pager_preexec; pager_process.preexec_cb = pager_preexec;
#endif
if (start_command(&pager_process)) if (start_command(&pager_process))
return; return;

View file

@ -16,32 +16,28 @@ struct event_symbol {
u8 type; u8 type;
u64 config; u64 config;
char *symbol; char *symbol;
char *alias;
}; };
#define C(x, y) .type = PERF_TYPE_##x, .config = PERF_COUNT_##y #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CR(x, y) .type = PERF_TYPE_##x, .config = y #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
static struct event_symbol event_symbols[] = { static struct event_symbol event_symbols[] = {
{ C(HARDWARE, HW_CPU_CYCLES), "cpu-cycles", }, { CHW(CPU_CYCLES), "cpu-cycles", "cycles" },
{ C(HARDWARE, HW_CPU_CYCLES), "cycles", }, { CHW(INSTRUCTIONS), "instructions", "" },
{ C(HARDWARE, HW_INSTRUCTIONS), "instructions", }, { CHW(CACHE_REFERENCES), "cache-references", "" },
{ C(HARDWARE, HW_CACHE_REFERENCES), "cache-references", }, { CHW(CACHE_MISSES), "cache-misses", "" },
{ C(HARDWARE, HW_CACHE_MISSES), "cache-misses", }, { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" },
{ C(HARDWARE, HW_BRANCH_INSTRUCTIONS),"branch-instructions", }, { CHW(BRANCH_MISSES), "branch-misses", "" },
{ C(HARDWARE, HW_BRANCH_INSTRUCTIONS),"branches", }, { CHW(BUS_CYCLES), "bus-cycles", "" },
{ C(HARDWARE, HW_BRANCH_MISSES), "branch-misses", },
{ C(HARDWARE, HW_BUS_CYCLES), "bus-cycles", },
{ C(SOFTWARE, SW_CPU_CLOCK), "cpu-clock", }, { CSW(CPU_CLOCK), "cpu-clock", "" },
{ C(SOFTWARE, SW_TASK_CLOCK), "task-clock", }, { CSW(TASK_CLOCK), "task-clock", "" },
{ C(SOFTWARE, SW_PAGE_FAULTS), "page-faults", }, { CSW(PAGE_FAULTS), "page-faults", "faults" },
{ C(SOFTWARE, SW_PAGE_FAULTS), "faults", }, { CSW(PAGE_FAULTS_MIN), "minor-faults", "" },
{ C(SOFTWARE, SW_PAGE_FAULTS_MIN), "minor-faults", }, { CSW(PAGE_FAULTS_MAJ), "major-faults", "" },
{ C(SOFTWARE, SW_PAGE_FAULTS_MAJ), "major-faults", }, { CSW(CONTEXT_SWITCHES), "context-switches", "cs" },
{ C(SOFTWARE, SW_CONTEXT_SWITCHES), "context-switches", }, { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" },
{ C(SOFTWARE, SW_CONTEXT_SWITCHES), "cs", },
{ C(SOFTWARE, SW_CPU_MIGRATIONS), "cpu-migrations", },
{ C(SOFTWARE, SW_CPU_MIGRATIONS), "migrations", },
}; };
#define __PERF_COUNTER_FIELD(config, name) \ #define __PERF_COUNTER_FIELD(config, name) \
@ -74,26 +70,70 @@ static char *sw_event_names[] = {
#define MAX_ALIASES 8 #define MAX_ALIASES 8
static char *hw_cache [][MAX_ALIASES] = { static char *hw_cache[][MAX_ALIASES] = {
{ "L1-data" , "l1-d", "l1d" }, { "L1-d$", "l1-d", "l1d", "L1-data", },
{ "L1-instruction" , "l1-i", "l1i" }, { "L1-i$", "l1-i", "l1i", "L1-instruction", },
{ "L2" , "l2" }, { "LLC", "L2" },
{ "Data-TLB" , "dtlb", "d-tlb" }, { "dTLB", "d-tlb", "Data-TLB", },
{ "Instruction-TLB" , "itlb", "i-tlb" }, { "iTLB", "i-tlb", "Instruction-TLB", },
{ "Branch" , "bpu" , "btb", "bpc" }, { "branch", "branches", "bpu", "btb", "bpc", },
}; };
static char *hw_cache_op [][MAX_ALIASES] = { static char *hw_cache_op[][MAX_ALIASES] = {
{ "Load" , "read" }, { "load", "loads", "read", },
{ "Store" , "write" }, { "store", "stores", "write", },
{ "Prefetch" , "speculative-read", "speculative-load" }, { "prefetch", "prefetches", "speculative-read", "speculative-load", },
}; };
static char *hw_cache_result [][MAX_ALIASES] = { static char *hw_cache_result[][MAX_ALIASES] = {
{ "Reference" , "ops", "access" }, { "refs", "Reference", "ops", "access", },
{ "Miss" }, { "misses", "miss", },
}; };
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_READ (1 << C(OP_READ))
#define CACHE_WRITE (1 << C(OP_WRITE))
#define CACHE_PREFETCH (1 << C(OP_PREFETCH))
#define COP(x) (1 << x)
/*
* cache operartion stat
* L1I : Read and prefetch only
* ITLB and BPU : Read-only
*/
static unsigned long hw_cache_stat[C(MAX)] = {
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
[C(ITLB)] = (CACHE_READ),
[C(BPU)] = (CACHE_READ),
};
static int is_cache_op_valid(u8 cache_type, u8 cache_op)
{
if (hw_cache_stat[cache_type] & COP(cache_op))
return 1; /* valid */
else
return 0; /* invalid */
}
static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
{
static char name[50];
if (cache_result) {
sprintf(name, "%s-%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][0],
hw_cache_result[cache_result][0]);
} else {
sprintf(name, "%s-%s", hw_cache[cache_type][0],
hw_cache_op[cache_op][1]);
}
return name;
}
char *event_name(int counter) char *event_name(int counter)
{ {
u64 config = attrs[counter].config; u64 config = attrs[counter].config;
@ -113,7 +153,6 @@ char *event_name(int counter)
case PERF_TYPE_HW_CACHE: { case PERF_TYPE_HW_CACHE: {
u8 cache_type, cache_op, cache_result; u8 cache_type, cache_op, cache_result;
static char name[100];
cache_type = (config >> 0) & 0xff; cache_type = (config >> 0) & 0xff;
if (cache_type > PERF_COUNT_HW_CACHE_MAX) if (cache_type > PERF_COUNT_HW_CACHE_MAX)
@ -127,12 +166,10 @@ char *event_name(int counter)
if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX)
return "unknown-ext-hardware-cache-result"; return "unknown-ext-hardware-cache-result";
sprintf(name, "%s-Cache-%s-%ses", if (!is_cache_op_valid(cache_type, cache_op))
hw_cache[cache_type][0], return "invalid-cache";
hw_cache_op[cache_op][0],
hw_cache_result[cache_result][0]);
return name; return event_cache_name(cache_type, cache_op, cache_result);
} }
case PERF_TYPE_SOFTWARE: case PERF_TYPE_SOFTWARE:
@ -163,7 +200,8 @@ static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size)
return -1; return -1;
} }
static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr) static int
parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr)
{ {
int cache_type = -1, cache_op = 0, cache_result = 0; int cache_type = -1, cache_op = 0, cache_result = 0;
@ -182,6 +220,9 @@ static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *a
if (cache_op == -1) if (cache_op == -1)
cache_op = PERF_COUNT_HW_CACHE_OP_READ; cache_op = PERF_COUNT_HW_CACHE_OP_READ;
if (!is_cache_op_valid(cache_type, cache_op))
return -EINVAL;
cache_result = parse_aliases(str, hw_cache_result, cache_result = parse_aliases(str, hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX); PERF_COUNT_HW_CACHE_RESULT_MAX);
/* /*
@ -196,6 +237,19 @@ static int parse_generic_hw_symbols(const char *str, struct perf_counter_attr *a
return 0; return 0;
} }
static int check_events(const char *str, unsigned int i)
{
if (!strncmp(str, event_symbols[i].symbol,
strlen(event_symbols[i].symbol)))
return 1;
if (strlen(event_symbols[i].alias))
if (!strncmp(str, event_symbols[i].alias,
strlen(event_symbols[i].alias)))
return 1;
return 0;
}
/* /*
* Each event can have multiple symbolic names. * Each event can have multiple symbolic names.
* Symbolic names are (almost) exactly matched. * Symbolic names are (almost) exactly matched.
@ -235,9 +289,7 @@ static int parse_event_symbols(const char *str, struct perf_counter_attr *attr)
} }
for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
if (!strncmp(str, event_symbols[i].symbol, if (check_events(str, i)) {
strlen(event_symbols[i].symbol))) {
attr->type = event_symbols[i].type; attr->type = event_symbols[i].type;
attr->config = event_symbols[i].config; attr->config = event_symbols[i].config;
@ -289,6 +341,7 @@ void print_events(void)
{ {
struct event_symbol *syms = event_symbols; struct event_symbol *syms = event_symbols;
unsigned int i, type, prev_type = -1; unsigned int i, type, prev_type = -1;
char name[40];
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); fprintf(stderr, "List of pre-defined events (to be used in -e):\n");
@ -301,14 +354,18 @@ void print_events(void)
if (type != prev_type) if (type != prev_type)
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " %-30s [%s]\n", syms->symbol, if (strlen(syms->alias))
sprintf(name, "%s OR %s", syms->symbol, syms->alias);
else
strcpy(name, syms->symbol);
fprintf(stderr, " %-40s [%s]\n", name,
event_type_descriptors[type]); event_type_descriptors[type]);
prev_type = type; prev_type = type;
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " %-30s [raw hardware event descriptor]\n", fprintf(stderr, " %-40s [raw hardware event descriptor]\n",
"rNNN"); "rNNN");
fprintf(stderr, "\n"); fprintf(stderr, "\n");

View file

@ -65,7 +65,6 @@ int start_command(struct child_process *cmd)
cmd->err = fderr[0]; cmd->err = fderr[0];
} }
#ifndef __MINGW32__
fflush(NULL); fflush(NULL);
cmd->pid = fork(); cmd->pid = fork();
if (!cmd->pid) { if (!cmd->pid) {
@ -118,71 +117,6 @@ int start_command(struct child_process *cmd)
} }
exit(127); exit(127);
} }
#else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
const char **sargv = cmd->argv;
char **env = environ;
if (cmd->no_stdin) {
s0 = dup(0);
dup_devnull(0);
} else if (need_in) {
s0 = dup(0);
dup2(fdin[0], 0);
} else if (cmd->in) {
s0 = dup(0);
dup2(cmd->in, 0);
}
if (cmd->no_stderr) {
s2 = dup(2);
dup_devnull(2);
} else if (need_err) {
s2 = dup(2);
dup2(fderr[1], 2);
}
if (cmd->no_stdout) {
s1 = dup(1);
dup_devnull(1);
} else if (cmd->stdout_to_stderr) {
s1 = dup(1);
dup2(2, 1);
} else if (need_out) {
s1 = dup(1);
dup2(fdout[1], 1);
} else if (cmd->out > 1) {
s1 = dup(1);
dup2(cmd->out, 1);
}
if (cmd->dir)
die("chdir in start_command() not implemented");
if (cmd->env) {
env = copy_environ();
for (; *cmd->env; cmd->env++)
env = env_setenv(env, *cmd->env);
}
if (cmd->perf_cmd) {
cmd->argv = prepare_perf_cmd(cmd->argv);
}
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
if (cmd->env)
free_environ(env);
if (cmd->perf_cmd)
free(cmd->argv);
cmd->argv = sargv;
if (s0 >= 0)
dup2(s0, 0), close(s0);
if (s1 >= 0)
dup2(s1, 1), close(s1);
if (s2 >= 0)
dup2(s2, 2), close(s2);
#endif
if (cmd->pid < 0) { if (cmd->pid < 0) {
int err = errno; int err = errno;
@ -288,14 +222,6 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
return run_command(&cmd); return run_command(&cmd);
} }
#ifdef __MINGW32__
static __stdcall unsigned run_thread(void *data)
{
struct async *async = data;
return async->proc(async->fd_for_proc, async->data);
}
#endif
int start_async(struct async *async) int start_async(struct async *async)
{ {
int pipe_out[2]; int pipe_out[2];
@ -304,7 +230,6 @@ int start_async(struct async *async)
return error("cannot create pipe: %s", strerror(errno)); return error("cannot create pipe: %s", strerror(errno));
async->out = pipe_out[0]; async->out = pipe_out[0];
#ifndef __MINGW32__
/* Flush stdio before fork() to avoid cloning buffers */ /* Flush stdio before fork() to avoid cloning buffers */
fflush(NULL); fflush(NULL);
@ -319,33 +244,17 @@ int start_async(struct async *async)
exit(!!async->proc(pipe_out[1], async->data)); exit(!!async->proc(pipe_out[1], async->data));
} }
close(pipe_out[1]); close(pipe_out[1]);
#else
async->fd_for_proc = pipe_out[1];
async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
if (!async->tid) {
error("cannot create thread: %s", strerror(errno));
close_pair(pipe_out);
return -1;
}
#endif
return 0; return 0;
} }
int finish_async(struct async *async) int finish_async(struct async *async)
{ {
#ifndef __MINGW32__
int ret = 0; int ret = 0;
if (wait_or_whine(async->pid)) if (wait_or_whine(async->pid))
ret = error("waitpid (async) failed"); ret = error("waitpid (async) failed");
#else
DWORD ret = 0;
if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
ret = error("waiting for thread failed: %lu", GetLastError());
else if (!GetExitCodeThread(async->tid, &ret))
ret = error("cannot get thread exit code: %lu", GetLastError());
CloseHandle(async->tid);
#endif
return ret; return ret;
} }

View file

@ -79,12 +79,7 @@ struct async {
int (*proc)(int fd, void *data); int (*proc)(int fd, void *data);
void *data; void *data;
int out; /* caller reads from here and closes it */ int out; /* caller reads from here and closes it */
#ifndef __MINGW32__
pid_t pid; pid_t pid;
#else
HANDLE tid;
int fd_for_proc;
#endif
}; };
int start_async(struct async *async); int start_async(struct async *async);

View file

@ -259,7 +259,7 @@ size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
res = fread(sb->buf + sb->len, 1, size, f); res = fread(sb->buf + sb->len, 1, size, f);
if (res > 0) if (res > 0)
strbuf_setlen(sb, sb->len + res); strbuf_setlen(sb, sb->len + res);
else if (res < 0 && oldalloc == 0) else if (oldalloc == 0)
strbuf_release(sb); strbuf_release(sb);
return res; return res;
} }

View file

@ -1,7 +1,7 @@
#ifndef _PERF_STRING_H_ #ifndef _PERF_STRING_H_
#define _PERF_STRING_H_ #define _PERF_STRING_H_
#include "../types.h" #include "types.h"
int hex2u64(const char *ptr, u64 *val); int hex2u64(const char *ptr, u64 *val);

184
tools/perf/util/strlist.c Normal file
View file

@ -0,0 +1,184 @@
/*
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Licensed under the GPLv2.
*/
#include "strlist.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct str_node *str_node__new(const char *s, bool dupstr)
{
struct str_node *self = malloc(sizeof(*self));
if (self != NULL) {
if (dupstr) {
s = strdup(s);
if (s == NULL)
goto out_delete;
}
self->s = s;
}
return self;
out_delete:
free(self);
return NULL;
}
static void str_node__delete(struct str_node *self, bool dupstr)
{
if (dupstr)
free((void *)self->s);
free(self);
}
int strlist__add(struct strlist *self, const char *new_entry)
{
struct rb_node **p = &self->entries.rb_node;
struct rb_node *parent = NULL;
struct str_node *sn;
while (*p != NULL) {
int rc;
parent = *p;
sn = rb_entry(parent, struct str_node, rb_node);
rc = strcmp(sn->s, new_entry);
if (rc > 0)
p = &(*p)->rb_left;
else if (rc < 0)
p = &(*p)->rb_right;
else
return -EEXIST;
}
sn = str_node__new(new_entry, self->dupstr);
if (sn == NULL)
return -ENOMEM;
rb_link_node(&sn->rb_node, parent, p);
rb_insert_color(&sn->rb_node, &self->entries);
return 0;
}
int strlist__load(struct strlist *self, const char *filename)
{
char entry[1024];
int err;
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return errno;
while (fgets(entry, sizeof(entry), fp) != NULL) {
const size_t len = strlen(entry);
if (len == 0)
continue;
entry[len - 1] = '\0';
err = strlist__add(self, entry);
if (err != 0)
goto out;
}
err = 0;
out:
fclose(fp);
return err;
}
void strlist__remove(struct strlist *self, struct str_node *sn)
{
rb_erase(&sn->rb_node, &self->entries);
str_node__delete(sn, self->dupstr);
}
bool strlist__has_entry(struct strlist *self, const char *entry)
{
struct rb_node **p = &self->entries.rb_node;
struct rb_node *parent = NULL;
while (*p != NULL) {
struct str_node *sn;
int rc;
parent = *p;
sn = rb_entry(parent, struct str_node, rb_node);
rc = strcmp(sn->s, entry);
if (rc > 0)
p = &(*p)->rb_left;
else if (rc < 0)
p = &(*p)->rb_right;
else
return true;
}
return false;
}
static int strlist__parse_list_entry(struct strlist *self, const char *s)
{
if (strncmp(s, "file://", 7) == 0)
return strlist__load(self, s + 7);
return strlist__add(self, s);
}
int strlist__parse_list(struct strlist *self, const char *s)
{
char *sep;
int err;
while ((sep = strchr(s, ',')) != NULL) {
*sep = '\0';
err = strlist__parse_list_entry(self, s);
*sep = ',';
if (err != 0)
return err;
s = sep + 1;
}
return *s ? strlist__parse_list_entry(self, s) : 0;
}
struct strlist *strlist__new(bool dupstr, const char *slist)
{
struct strlist *self = malloc(sizeof(*self));
if (self != NULL) {
self->entries = RB_ROOT;
self->dupstr = dupstr;
if (slist && strlist__parse_list(self, slist) != 0)
goto out_error;
}
return self;
out_error:
free(self);
return NULL;
}
void strlist__delete(struct strlist *self)
{
if (self != NULL) {
struct str_node *pos;
struct rb_node *next = rb_first(&self->entries);
while (next) {
pos = rb_entry(next, struct str_node, rb_node);
next = rb_next(&pos->rb_node);
strlist__remove(self, pos);
}
self->entries = RB_ROOT;
free(self);
}
}

32
tools/perf/util/strlist.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef STRLIST_H_
#define STRLIST_H_
#include "rbtree.h"
#include <stdbool.h>
struct str_node {
struct rb_node rb_node;
const char *s;
};
struct strlist {
struct rb_root entries;
bool dupstr;
};
struct strlist *strlist__new(bool dupstr, const char *slist);
void strlist__delete(struct strlist *self);
void strlist__remove(struct strlist *self, struct str_node *sn);
int strlist__load(struct strlist *self, const char *filename);
int strlist__add(struct strlist *self, const char *str);
bool strlist__has_entry(struct strlist *self, const char *entry);
static inline bool strlist__empty(const struct strlist *self)
{
return rb_first(&self->entries) == NULL;
}
int strlist__parse_list(struct strlist *self, const char *s);
#endif /* STRLIST_H_ */

View file

@ -520,7 +520,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
nr_syms = shdr.sh_size / shdr.sh_entsize; nr_syms = shdr.sh_size / shdr.sh_entsize;
memset(&sym, 0, sizeof(sym)); memset(&sym, 0, sizeof(sym));
self->prelinked = elf_section_by_name(elf, &ehdr, &shdr,
".gnu.prelink_undo",
NULL) != NULL;
elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
struct symbol *f; struct symbol *f;
u64 obj_start; u64 obj_start;
@ -535,7 +537,13 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
gelf_getshdr(sec, &shdr); gelf_getshdr(sec, &shdr);
obj_start = sym.st_value; obj_start = sym.st_value;
sym.st_value -= shdr.sh_addr - shdr.sh_offset; if (self->prelinked) {
if (verbose >= 2)
printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",
(u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset);
sym.st_value -= shdr.sh_addr - shdr.sh_offset;
}
f = symbol__new(sym.st_value, sym.st_size, f = symbol__new(sym.st_value, sym.st_size,
elf_sym__name(&sym, symstrs), elf_sym__name(&sym, symstrs),
@ -569,6 +577,8 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
if (!name) if (!name)
return -1; return -1;
self->prelinked = 0;
if (strncmp(self->name, "/tmp/perf-", 10) == 0) if (strncmp(self->name, "/tmp/perf-", 10) == 0)
return dso__load_perf_map(self, filter, verbose); return dso__load_perf_map(self, filter, verbose);
@ -629,7 +639,7 @@ int dso__load_kernel(struct dso *self, const char *vmlinux,
if (vmlinux) if (vmlinux)
err = dso__load_vmlinux(self, vmlinux, filter, verbose); err = dso__load_vmlinux(self, vmlinux, filter, verbose);
if (err) if (err < 0)
err = dso__load_kallsyms(self, filter, verbose); err = dso__load_kallsyms(self, filter, verbose);
return err; return err;

View file

@ -2,7 +2,7 @@
#define _PERF_SYMBOL_ 1 #define _PERF_SYMBOL_ 1
#include <linux/types.h> #include <linux/types.h>
#include "../types.h" #include "types.h"
#include "list.h" #include "list.h"
#include "rbtree.h" #include "rbtree.h"
@ -20,8 +20,9 @@ struct symbol {
struct dso { struct dso {
struct list_head node; struct list_head node;
struct rb_root syms; struct rb_root syms;
unsigned int sym_priv_size;
struct symbol *(*find_symbol)(struct dso *, u64 ip); struct symbol *(*find_symbol)(struct dso *, u64 ip);
unsigned int sym_priv_size;
unsigned char prelinked;
char name[0]; char name[0];
}; };

View file

@ -67,7 +67,6 @@
#include <assert.h> #include <assert.h>
#include <regex.h> #include <regex.h>
#include <utime.h> #include <utime.h>
#ifndef __MINGW32__
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -81,20 +80,6 @@
#include <netdb.h> #include <netdb.h>
#include <pwd.h> #include <pwd.h>
#include <inttypes.h> #include <inttypes.h>
#if defined(__CYGWIN__)
#undef _XOPEN_SOURCE
#include <grp.h>
#define _XOPEN_SOURCE 600
#include "compat/cygwin.h"
#else
#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
#include <grp.h>
#define _ALL_SOURCE 1
#endif
#else /* __MINGW32__ */
/* pull in Windows compatibility stuff */
#include "compat/mingw.h"
#endif /* __MINGW32__ */
#ifndef NO_ICONV #ifndef NO_ICONV
#include <iconv.h> #include <iconv.h>