Commit 4f0ac854 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perfcounters-core-for-linus' of...

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

* 'perfcounters-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (60 commits)
  perf tools: Avoid unnecessary work in directory lookups
  perf stat: Clean up statistics calculations a bit more
  perf stat: More advanced variance computation
  perf stat: Use stddev_mean in stead of stddev
  perf stat: Remove the limit on repeat
  perf stat: Change noise calculation to use stddev
  x86, perf_counter, bts: Do not allow kernel BTS tracing for now
  x86, perf_counter, bts: Correct pointer-to-u64 casts
  x86, perf_counter, bts: Fail if BTS is not available
  perf_counter: Fix output-sharing error path
  perf trace: Fix read_string()
  perf trace: Print out in nanoseconds
  perf tools: Seek to the end of the header area
  perf trace: Fix parsing of perf.data
  perf trace: Sample timestamps as well
  perf_counter: Introduce new (non-)paranoia level to allow raw tracepoint access
  perf trace: Sample the CPU too
  perf tools: Work around strict aliasing related warnings
  perf tools: Clean up warnings list in the Makefile
  perf tools: Complete support for dynamic strings
  ...
parents b9356c53 6b58e7f1
...@@ -104,8 +104,8 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, ...@@ -104,8 +104,8 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
else else
pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte)); pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte));
#elif defined(CONFIG_PPC32) && defined(CONFIG_PTE_64BIT) && defined(CONFIG_SMP) #elif defined(CONFIG_PPC32) && defined(CONFIG_PTE_64BIT)
/* Second case is 32-bit with 64-bit PTE in SMP mode. In this case, we /* Second case is 32-bit with 64-bit PTE. In this case, we
* can just store as long as we do the two halves in the right order * can just store as long as we do the two halves in the right order
* with a barrier in between. This is possible because we take care, * with a barrier in between. This is possible because we take care,
* in the hash code, to pre-invalidate if the PTE was already hashed, * in the hash code, to pre-invalidate if the PTE was already hashed,
...@@ -140,7 +140,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, ...@@ -140,7 +140,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
#else #else
/* Anything else just stores the PTE normally. That covers all 64-bit /* Anything else just stores the PTE normally. That covers all 64-bit
* cases, and 32-bit non-hash with 64-bit PTEs in UP mode * cases, and 32-bit non-hash with 32-bit PTEs.
*/ */
*ptep = pte; *ptep = pte;
#endif #endif
......
...@@ -97,7 +97,7 @@ obj64-$(CONFIG_AUDIT) += compat_audit.o ...@@ -97,7 +97,7 @@ obj64-$(CONFIG_AUDIT) += compat_audit.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_PPC_PERF_CTRS) += perf_counter.o obj-$(CONFIG_PPC_PERF_CTRS) += perf_counter.o perf_callchain.o
obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \ obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \
power5+-pmu.o power6-pmu.o power7-pmu.o power5+-pmu.o power6-pmu.o power7-pmu.o
obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o
......
...@@ -67,6 +67,8 @@ int main(void) ...@@ -67,6 +67,8 @@ int main(void)
DEFINE(MMCONTEXTID, offsetof(struct mm_struct, context.id)); DEFINE(MMCONTEXTID, offsetof(struct mm_struct, context.id));
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context)); DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context));
DEFINE(SIGSEGV, SIGSEGV);
DEFINE(NMI_MASK, NMI_MASK);
#else #else
DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); DEFINE(THREAD_INFO, offsetof(struct task_struct, stack));
#endif /* CONFIG_PPC64 */ #endif /* CONFIG_PPC64 */
......
...@@ -729,6 +729,11 @@ BEGIN_FTR_SECTION ...@@ -729,6 +729,11 @@ BEGIN_FTR_SECTION
bne- do_ste_alloc /* If so handle it */ bne- do_ste_alloc /* If so handle it */
END_FTR_SECTION_IFCLR(CPU_FTR_SLB) END_FTR_SECTION_IFCLR(CPU_FTR_SLB)
clrrdi r11,r1,THREAD_SHIFT
lwz r0,TI_PREEMPT(r11) /* If we're in an "NMI" */
andis. r0,r0,NMI_MASK@h /* (i.e. an irq when soft-disabled) */
bne 77f /* then don't call hash_page now */
/* /*
* On iSeries, we soft-disable interrupts here, then * On iSeries, we soft-disable interrupts here, then
* hard-enable interrupts so that the hash_page code can spin on * hard-enable interrupts so that the hash_page code can spin on
...@@ -833,6 +838,20 @@ handle_page_fault: ...@@ -833,6 +838,20 @@ handle_page_fault:
bl .low_hash_fault bl .low_hash_fault
b .ret_from_except b .ret_from_except
/*
* We come here as a result of a DSI at a point where we don't want
* to call hash_page, such as when we are accessing memory (possibly
* user memory) inside a PMU interrupt that occurred while interrupts
* were soft-disabled. We want to invoke the exception handler for
* the access, or panic if there isn't a handler.
*/
77: bl .save_nvgprs
mr r4,r3
addi r3,r1,STACK_FRAME_OVERHEAD
li r5,SIGSEGV
bl .bad_page_fault
b .ret_from_except
/* here we have a segment miss */ /* here we have a segment miss */
do_ste_alloc: do_ste_alloc:
bl .ste_allocate /* try to insert stab entry */ bl .ste_allocate /* try to insert stab entry */
......
This diff is collapsed.
...@@ -92,15 +92,13 @@ static inline void create_shadowed_slbe(unsigned long ea, int ssize, ...@@ -92,15 +92,13 @@ static inline void create_shadowed_slbe(unsigned long ea, int ssize,
: "memory" ); : "memory" );
} }
void slb_flush_and_rebolt(void) static void __slb_flush_and_rebolt(void)
{ {
/* If you change this make sure you change SLB_NUM_BOLTED /* If you change this make sure you change SLB_NUM_BOLTED
* appropriately too. */ * appropriately too. */
unsigned long linear_llp, vmalloc_llp, lflags, vflags; unsigned long linear_llp, vmalloc_llp, lflags, vflags;
unsigned long ksp_esid_data, ksp_vsid_data; unsigned long ksp_esid_data, ksp_vsid_data;
WARN_ON(!irqs_disabled());
linear_llp = mmu_psize_defs[mmu_linear_psize].sllp; linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp; vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp;
lflags = SLB_VSID_KERNEL | linear_llp; lflags = SLB_VSID_KERNEL | linear_llp;
...@@ -117,12 +115,6 @@ void slb_flush_and_rebolt(void) ...@@ -117,12 +115,6 @@ void slb_flush_and_rebolt(void)
ksp_vsid_data = get_slb_shadow()->save_area[2].vsid; ksp_vsid_data = get_slb_shadow()->save_area[2].vsid;
} }
/*
* We can't take a PMU exception in the following code, so hard
* disable interrupts.
*/
hard_irq_disable();
/* We need to do this all in asm, so we're sure we don't touch /* We need to do this all in asm, so we're sure we don't touch
* the stack between the slbia and rebolting it. */ * the stack between the slbia and rebolting it. */
asm volatile("isync\n" asm volatile("isync\n"
...@@ -139,6 +131,21 @@ void slb_flush_and_rebolt(void) ...@@ -139,6 +131,21 @@ void slb_flush_and_rebolt(void)
: "memory"); : "memory");
} }
void slb_flush_and_rebolt(void)
{
WARN_ON(!irqs_disabled());
/*
* We can't take a PMU exception in the following code, so hard
* disable interrupts.
*/
hard_irq_disable();
__slb_flush_and_rebolt();
get_paca()->slb_cache_ptr = 0;
}
void slb_vmalloc_update(void) void slb_vmalloc_update(void)
{ {
unsigned long vflags; unsigned long vflags;
...@@ -180,12 +187,20 @@ static inline int esids_match(unsigned long addr1, unsigned long addr2) ...@@ -180,12 +187,20 @@ static inline int esids_match(unsigned long addr1, unsigned long addr2)
/* Flush all user entries from the segment table of the current processor. */ /* Flush all user entries from the segment table of the current processor. */
void switch_slb(struct task_struct *tsk, struct mm_struct *mm) void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
{ {
unsigned long offset = get_paca()->slb_cache_ptr; unsigned long offset;
unsigned long slbie_data = 0; unsigned long slbie_data = 0;
unsigned long pc = KSTK_EIP(tsk); unsigned long pc = KSTK_EIP(tsk);
unsigned long stack = KSTK_ESP(tsk); unsigned long stack = KSTK_ESP(tsk);
unsigned long unmapped_base; unsigned long unmapped_base;
/*
* We need interrupts hard-disabled here, not just soft-disabled,
* so that a PMU interrupt can't occur, which might try to access
* user memory (to get a stack trace) and possible cause an SLB miss
* which would update the slb_cache/slb_cache_ptr fields in the PACA.
*/
hard_irq_disable();
offset = get_paca()->slb_cache_ptr;
if (!cpu_has_feature(CPU_FTR_NO_SLBIE_B) && if (!cpu_has_feature(CPU_FTR_NO_SLBIE_B) &&
offset <= SLB_CACHE_ENTRIES) { offset <= SLB_CACHE_ENTRIES) {
int i; int i;
...@@ -200,7 +215,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) ...@@ -200,7 +215,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
} }
asm volatile("isync" : : : "memory"); asm volatile("isync" : : : "memory");
} else { } else {
slb_flush_and_rebolt(); __slb_flush_and_rebolt();
} }
/* Workaround POWER5 < DD2.1 issue */ /* Workaround POWER5 < DD2.1 issue */
......
...@@ -164,7 +164,7 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm) ...@@ -164,7 +164,7 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm)
{ {
struct stab_entry *stab = (struct stab_entry *) get_paca()->stab_addr; struct stab_entry *stab = (struct stab_entry *) get_paca()->stab_addr;
struct stab_entry *ste; struct stab_entry *ste;
unsigned long offset = __get_cpu_var(stab_cache_ptr); unsigned long offset;
unsigned long pc = KSTK_EIP(tsk); unsigned long pc = KSTK_EIP(tsk);
unsigned long stack = KSTK_ESP(tsk); unsigned long stack = KSTK_ESP(tsk);
unsigned long unmapped_base; unsigned long unmapped_base;
...@@ -172,6 +172,15 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm) ...@@ -172,6 +172,15 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm)
/* Force previous translations to complete. DRENG */ /* Force previous translations to complete. DRENG */
asm volatile("isync" : : : "memory"); asm volatile("isync" : : : "memory");
/*
* We need interrupts hard-disabled here, not just soft-disabled,
* so that a PMU interrupt can't occur, which might try to access
* user memory (to get a stack trace) and possible cause an STAB miss
* which would update the stab_cache/stab_cache_ptr per-cpu variables.
*/
hard_irq_disable();
offset = __get_cpu_var(stab_cache_ptr);
if (offset <= NR_STAB_CACHE_ENTRIES) { if (offset <= NR_STAB_CACHE_ENTRIES) {
int i; int i;
......
...@@ -84,6 +84,16 @@ union cpuid10_edx { ...@@ -84,6 +84,16 @@ union cpuid10_edx {
#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b
#define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) #define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2)
/*
* We model BTS tracing as another fixed-mode PMC.
*
* We choose a value in the middle of the fixed counter range, since lower
* values are used by actual fixed counters and higher values are used
* to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr.
*/
#define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16)
#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);
......
This diff is collapsed.
...@@ -216,6 +216,7 @@ struct perf_counter_attr { ...@@ -216,6 +216,7 @@ struct perf_counter_attr {
#define PERF_COUNTER_IOC_REFRESH _IO ('$', 2) #define PERF_COUNTER_IOC_REFRESH _IO ('$', 2)
#define PERF_COUNTER_IOC_RESET _IO ('$', 3) #define PERF_COUNTER_IOC_RESET _IO ('$', 3)
#define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64) #define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64)
#define PERF_COUNTER_IOC_SET_OUTPUT _IO ('$', 5)
enum perf_counter_ioc_flags { enum perf_counter_ioc_flags {
PERF_IOC_FLAG_GROUP = 1U << 0, PERF_IOC_FLAG_GROUP = 1U << 0,
...@@ -415,6 +416,9 @@ enum perf_callchain_context { ...@@ -415,6 +416,9 @@ enum perf_callchain_context {
PERF_CONTEXT_MAX = (__u64)-4095, PERF_CONTEXT_MAX = (__u64)-4095,
}; };
#define PERF_FLAG_FD_NO_GROUP (1U << 0)
#define PERF_FLAG_FD_OUTPUT (1U << 1)
#ifdef __KERNEL__ #ifdef __KERNEL__
/* /*
* Kernel-internal data types and definitions: * Kernel-internal data types and definitions:
...@@ -536,6 +540,7 @@ struct perf_counter { ...@@ -536,6 +540,7 @@ struct perf_counter {
struct list_head sibling_list; struct list_head sibling_list;
int nr_siblings; int nr_siblings;
struct perf_counter *group_leader; struct perf_counter *group_leader;
struct perf_counter *output;
const struct pmu *pmu; const struct pmu *pmu;
enum perf_counter_active_state state; enum perf_counter_active_state state;
......
...@@ -46,12 +46,18 @@ static atomic_t nr_task_counters __read_mostly; ...@@ -46,12 +46,18 @@ static atomic_t nr_task_counters __read_mostly;
/* /*
* perf counter paranoia level: * perf counter paranoia level:
* 0 - not paranoid * -1 - not paranoid at all
* 1 - disallow cpu counters to unpriv * 0 - disallow raw tracepoint access for unpriv
* 2 - disallow kernel profiling to unpriv * 1 - disallow cpu counters for unpriv
* 2 - disallow kernel profiling for unpriv
*/ */
int sysctl_perf_counter_paranoid __read_mostly = 1; int sysctl_perf_counter_paranoid __read_mostly = 1;
static inline bool perf_paranoid_tracepoint_raw(void)
{
return sysctl_perf_counter_paranoid > -1;
}
static inline bool perf_paranoid_cpu(void) static inline bool perf_paranoid_cpu(void)
{ {
return sysctl_perf_counter_paranoid > 0; return sysctl_perf_counter_paranoid > 0;
...@@ -469,7 +475,8 @@ static void update_counter_times(struct perf_counter *counter) ...@@ -469,7 +475,8 @@ static void update_counter_times(struct perf_counter *counter)
struct perf_counter_context *ctx = counter->ctx; struct perf_counter_context *ctx = counter->ctx;
u64 run_end; u64 run_end;
if (counter->state < PERF_COUNTER_STATE_INACTIVE) if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
counter->group_leader->state < PERF_COUNTER_STATE_INACTIVE)
return; return;
counter->total_time_enabled = ctx->time - counter->tstamp_enabled; counter->total_time_enabled = ctx->time - counter->tstamp_enabled;
...@@ -518,7 +525,7 @@ static void __perf_counter_disable(void *info) ...@@ -518,7 +525,7 @@ static void __perf_counter_disable(void *info)
*/ */
if (counter->state >= PERF_COUNTER_STATE_INACTIVE) { if (counter->state >= PERF_COUNTER_STATE_INACTIVE) {
update_context_time(ctx); update_context_time(ctx);
update_counter_times(counter); update_group_times(counter);
if (counter == counter->group_leader) if (counter == counter->group_leader)
group_sched_out(counter, cpuctx, ctx); group_sched_out(counter, cpuctx, ctx);
else else
...@@ -573,7 +580,7 @@ static void perf_counter_disable(struct perf_counter *counter) ...@@ -573,7 +580,7 @@ static void perf_counter_disable(struct perf_counter *counter)
* in, so we can change the state safely. * in, so we can change the state safely.
*/ */
if (counter->state == PERF_COUNTER_STATE_INACTIVE) { if (counter->state == PERF_COUNTER_STATE_INACTIVE) {
update_counter_times(counter); update_group_times(counter);
counter->state = PERF_COUNTER_STATE_OFF; counter->state = PERF_COUNTER_STATE_OFF;
} }
...@@ -850,6 +857,27 @@ perf_install_in_context(struct perf_counter_context *ctx, ...@@ -850,6 +857,27 @@ perf_install_in_context(struct perf_counter_context *ctx,
spin_unlock_irq(&ctx->lock); spin_unlock_irq(&ctx->lock);
} }
/*
* Put a counter into inactive state and update time fields.
* Enabling the leader of a group effectively enables all
* the group members that aren't explicitly disabled, so we
* have to update their ->tstamp_enabled also.
* Note: this works for group members as well as group leaders
* since the non-leader members' sibling_lists will be empty.
*/
static void __perf_counter_mark_enabled(struct perf_counter *counter,
struct perf_counter_context *ctx)
{
struct perf_counter *sub;
counter->state = PERF_COUNTER_STATE_INACTIVE;
counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
list_for_each_entry(sub, &counter->sibling_list, list_entry)
if (sub->state >= PERF_COUNTER_STATE_INACTIVE)
sub->tstamp_enabled =
ctx->time - sub->total_time_enabled;
}
/* /*
* Cross CPU call to enable a performance counter * Cross CPU call to enable a performance counter
*/ */
...@@ -877,8 +905,7 @@ static void __perf_counter_enable(void *info) ...@@ -877,8 +905,7 @@ static void __perf_counter_enable(void *info)
if (counter->state >= PERF_COUNTER_STATE_INACTIVE) if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
goto unlock; goto unlock;
counter->state = PERF_COUNTER_STATE_INACTIVE; __perf_counter_mark_enabled(counter, ctx);
counter->tstamp_enabled = ctx->time - counter->total_time_enabled;
/* /*
* If the counter is in a group and isn't the group leader, * If the counter is in a group and isn't the group leader,
...@@ -971,11 +998,9 @@ static void perf_counter_enable(struct perf_counter *counter) ...@@ -971,11 +998,9 @@ static void perf_counter_enable(struct perf_counter *counter)
* Since we have the lock this context can't be scheduled * Since we have the lock this context can't be scheduled
* in, so we can change the state safely. * in, so we can change the state safely.
*/ */
if (counter->state == PERF_COUNTER_STATE_OFF) { if (counter->state == PERF_COUNTER_STATE_OFF)
counter->state = PERF_COUNTER_STATE_INACTIVE; __perf_counter_mark_enabled(counter, ctx);
counter->tstamp_enabled =
ctx->time - counter->total_time_enabled;
}
out: out:
spin_unlock_irq(&ctx->lock); spin_unlock_irq(&ctx->lock);
} }
...@@ -1479,9 +1504,7 @@ static void perf_counter_enable_on_exec(struct task_struct *task) ...@@ -1479,9 +1504,7 @@ static void perf_counter_enable_on_exec(struct task_struct *task)
counter->attr.enable_on_exec = 0; counter->attr.enable_on_exec = 0;
if (counter->state >= PERF_COUNTER_STATE_INACTIVE) if (counter->state >= PERF_COUNTER_STATE_INACTIVE)
continue; continue;
counter->state = PERF_COUNTER_STATE_INACTIVE; __perf_counter_mark_enabled(counter, ctx);
counter->tstamp_enabled =
ctx->time - counter->total_time_enabled;
enabled = 1; enabled = 1;
} }
...@@ -1675,6 +1698,11 @@ static void free_counter(struct perf_counter *counter) ...@@ -1675,6 +1698,11 @@ static void free_counter(struct perf_counter *counter)
atomic_dec(&nr_task_counters); atomic_dec(&nr_task_counters);
} }
if (counter->output) {
fput(counter->output->filp);
counter->output = NULL;
}
if (counter->destroy) if (counter->destroy)
counter->destroy(counter); counter->destroy(counter);
...@@ -1960,6 +1988,8 @@ static int perf_counter_period(struct perf_counter *counter, u64 __user *arg) ...@@ -1960,6 +1988,8 @@ static int perf_counter_period(struct perf_counter *counter, u64 __user *arg)
return ret; return ret;
} }
int perf_counter_set_output(struct perf_counter *counter, int output_fd);
static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct perf_counter *counter = file->private_data; struct perf_counter *counter = file->private_data;
...@@ -1983,6 +2013,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -1983,6 +2013,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PERF_COUNTER_IOC_PERIOD: case PERF_COUNTER_IOC_PERIOD:
return perf_counter_period(counter, (u64 __user *)arg); return perf_counter_period(counter, (u64 __user *)arg);
case PERF_COUNTER_IOC_SET_OUTPUT:
return perf_counter_set_output(counter, arg);
default: default:
return -ENOTTY; return -ENOTTY;
} }
...@@ -2253,6 +2286,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -2253,6 +2286,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
WARN_ON_ONCE(counter->ctx->parent_ctx); WARN_ON_ONCE(counter->ctx->parent_ctx);
mutex_lock(&counter->mmap_mutex); mutex_lock(&counter->mmap_mutex);
if (counter->output) {
ret = -EINVAL;
goto unlock;
}
if (atomic_inc_not_zero(&counter->mmap_count)) { if (atomic_inc_not_zero(&counter->mmap_count)) {
if (nr_pages != counter->data->nr_pages) if (nr_pages != counter->data->nr_pages)
ret = -EINVAL; ret = -EINVAL;
...@@ -2638,6 +2676,7 @@ static int perf_output_begin(struct perf_output_handle *handle, ...@@ -2638,6 +2676,7 @@ static int perf_output_begin(struct perf_output_handle *handle,
struct perf_counter *counter, unsigned int size, struct perf_counter *counter, unsigned int size,
int nmi, int sample) int nmi, int sample)
{ {
struct perf_counter *output_counter;
struct perf_mmap_data *data; struct perf_mmap_data *data;
unsigned int offset, head; unsigned int offset, head;
int have_lost; int have_lost;
...@@ -2647,13 +2686,17 @@ static int perf_output_begin(struct perf_output_handle *handle, ...@@ -2647,13 +2686,17 @@ static int perf_output_begin(struct perf_output_handle *handle,
u64 lost; u64 lost;
} lost_event; } lost_event;
rcu_read_lock();
/* /*
* For inherited counters we send all the output towards the parent. * For inherited counters we send all the output towards the parent.
*/ */
if (counter->parent) if (counter->parent)
counter = counter->parent; counter = counter->parent;
rcu_read_lock(); output_counter = rcu_dereference(counter->output);
if (output_counter)
counter = output_counter;
data = rcu_dereference(counter->data); data = rcu_dereference(counter->data);
if (!data) if (!data)
goto out; goto out;
...@@ -3934,6 +3977,7 @@ static const struct pmu *tp_perf_counter_init(struct perf_counter *counter) ...@@ -3934,6 +3977,7 @@ static const struct pmu *tp_perf_counter_init(struct perf_counter *counter)
* have these. * have these.
*/ */
if ((counter->attr.sample_type & PERF_SAMPLE_RAW) && if ((counter->attr.sample_type & PERF_SAMPLE_RAW) &&
perf_paranoid_tracepoint_raw() &&
!capable(CAP_SYS_ADMIN)) !capable(CAP_SYS_ADMIN))
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
...@@ -4202,6 +4246,57 @@ static int perf_copy_attr(struct perf_counter_attr __user *uattr, ...@@ -4202,6 +4246,57 @@ static int perf_copy_attr(struct perf_counter_attr __user *uattr,
goto out; goto out;
} }
int perf_counter_set_output(struct perf_counter *counter, int output_fd)
{
struct perf_counter *output_counter = NULL;
struct file *output_file = NULL;
struct perf_counter *old_output;
int fput_needed = 0;
int ret = -EINVAL;
if (!output_fd)
goto set;
output_file = fget_light(output_fd, &fput_needed);
if (!output_file)
return -EBADF;
if (output_file->f_op != &perf_fops)
goto out;
output_counter = output_file->private_data;
/* Don't chain output fds */
if (output_counter->output)
goto out;
/* Don't set an output fd when we already have an output channel */
if (counter->data)
goto out;
atomic_long_inc(&output_file->f_count);
set:
mutex_lock(&counter->mmap_mutex);
old_output = counter->output;
rcu_assign_pointer(counter->output, output_counter);
mutex_unlock(&counter->mmap_mutex);
if (old_output) {
/*
* we need to make sure no existing perf_output_*()
* is still referencing this counter.
*/
synchronize_rcu();
fput(old_output->filp);
}
ret = 0;
out:
fput_light(output_file, fput_needed);
return ret;
}
/** /**
* sys_perf_counter_open - open a performance counter, associate it to a task/cpu * sys_perf_counter_open - open a performance counter, associate it to a task/cpu
* *
...@@ -4221,15 +4316,15 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4221,15 +4316,15 @@ SYSCALL_DEFINE5(perf_counter_open,
struct file *group_file = NULL; struct file *group_file = NULL;
int fput_needed = 0; int fput_needed = 0;
int fput_needed2 = 0; int fput_needed2 = 0;
int ret; int err;
/* for future expandability... */ /* for future expandability... */
if (flags) if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
return -EINVAL; return -EINVAL;
ret = perf_copy_attr(attr_uptr, &attr); err = perf_copy_attr(attr_uptr, &attr);
if (ret) if (err)
return ret; return err;
if (!attr.exclude_kernel) { if (!attr.exclude_kernel) {
if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
...@@ -4252,8 +4347,8 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4252,8 +4347,8 @@ SYSCALL_DEFINE5(perf_counter_open,
* Look up the group leader (we will attach this counter to it): * Look up the group leader (we will attach this counter to it):
*/ */
group_leader = NULL; group_leader = NULL;
if (group_fd != -1) { if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) {
ret = -EINVAL; err = -EINVAL;
group_file = fget_light(group_fd, &fput_needed); group_file = fget_light(group_fd, &fput_needed);
if (!group_file) if (!group_file)
goto err_put_context; goto err_put_context;
...@@ -4282,18 +4377,24 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4282,18 +4377,24 @@ SYSCALL_DEFINE5(perf_counter_open,
counter = perf_counter_alloc(&attr, cpu, ctx, group_leader, counter = perf_counter_alloc(&attr, cpu, ctx, group_leader,
NULL, GFP_KERNEL); NULL, GFP_KERNEL);
ret = PTR_ERR(counter); err = PTR_ERR(counter);
if (IS_ERR(counter)) if (IS_ERR(counter))
goto err_put_context; goto err_put_context;
ret = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0); err = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0);
if (ret < 0) if (err < 0)
goto err_free_put_context; goto err_free_put_context;
counter_file = fget_light(ret, &fput_needed2); counter_file = fget_light(err, &fput_needed2);
if (!counter_file) if (!counter_file)
goto err_free_put_context; goto err_free_put_context;
if (flags & PERF_FLAG_FD_OUTPUT) {
err = perf_counter_set_output(counter, group_fd);
if (err)
goto err_fput_free_put_context;
}
counter->filp = counter_file; counter->filp = counter_file;
WARN_ON_ONCE(ctx->parent_ctx); WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex); mutex_lock(&ctx->mutex);
...@@ -4307,20 +4408,20 @@ SYSCALL_DEFINE5(perf_counter_open, ...@@ -4307,20 +4408,20 @@ SYSCALL_DEFINE5(perf_counter_open,
list_add_tail(&counter->owner_entry, &current->perf_counter_list); list_add_tail(&counter->owner_entry, &current->perf_counter_list);
mutex_unlock(&current->perf_counter_mutex); mutex_unlock(&current->perf_counter_mutex);
err_fput_free_put_context:
fput_light(counter_file, fput_needed2); fput_light(counter_file, fput_needed2);
out_fput:
fput_light(group_file, fput_needed);
return ret;
err_free_put_context: err_free_put_context:
if (err < 0)
kfree(counter); kfree(counter);
err_put_context: err_put_context:
if (err < 0)
put_ctx(ctx); put_ctx(ctx);
goto out_fput; fput_light(group_file, fput_needed);
return err;
} }
/* /*
......
...@@ -91,6 +91,10 @@ OPTIONS ...@@ -91,6 +91,10 @@ OPTIONS
--no-samples:: --no-samples::
Don't sample. Don't sample.
-R::
--raw-samples::
Collect raw sample records from all opened counters (typically for tracepoint counters).
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]
...@@ -27,6 +27,9 @@ OPTIONS ...@@ -27,6 +27,9 @@ OPTIONS
-n -n
--show-nr-samples --show-nr-samples
Show the number of samples for each symbol Show the number of samples for each symbol
-T
--threads
Show per-thread event counters
-C:: -C::
--comms=:: --comms=::
Only consider symbols in these comms. CSV that understands Only consider symbols in these comms. CSV that understands
...@@ -48,6 +51,16 @@ OPTIONS ...@@ -48,6 +51,16 @@ OPTIONS
all occurances of this separator in symbol names (and other output) all occurances of this separator in symbol names (and other output)
with a '.' character, that thus it's the only non valid separator. with a '.' character, that thus it's the only non valid separator.
-g [type,min]::
--call-graph::
Display callchains using type and min percent threshold.
type can be either:
- flat: single column, linear exposure of callchains.
- graph: use a graph tree, displaying absolute overhead rates.
- fractal: like graph, but displays relative rates. Each branch of
the tree is considered as a new profiled object. +
Default: fractal,0.5.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1] linkperf:perf-stat[1]
...@@ -166,7 +166,35 @@ endif ...@@ -166,7 +166,35 @@ endif
# CFLAGS and LDFLAGS are for the users to override from the command line. # CFLAGS and LDFLAGS are for the users to override from the command line.
CFLAGS = $(M64) -ggdb3 -Wall -Wextra -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6 #
# Include saner warnings here, which can catch bugs:
#
EXTRA_WARNINGS := -Wcast-align
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstack-protector
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wvolatile-register-var
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement
CFLAGS = $(M64) -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -fstack-protector-all -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS)
LDFLAGS = -lpthread -lrt -lelf -lm LDFLAGS = -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS) ALL_CFLAGS = $(CFLAGS)
ALL_LDFLAGS = $(LDFLAGS) ALL_LDFLAGS = $(LDFLAGS)
...@@ -310,6 +338,7 @@ LIB_H += util/sigchain.h ...@@ -310,6 +338,7 @@ LIB_H += util/sigchain.h
LIB_H += util/symbol.h LIB_H += util/symbol.h
LIB_H += util/module.h LIB_H += util/module.h
LIB_H += util/color.h LIB_H += util/color.h
LIB_H += util/values.h
LIB_OBJS += util/abspath.o LIB_OBJS += util/abspath.o
LIB_OBJS += util/alias.o LIB_OBJS += util/alias.o
...@@ -337,6 +366,13 @@ LIB_OBJS += util/color.o ...@@ -337,6 +366,13 @@ LIB_OBJS += util/color.o
LIB_OBJS += util/pager.o LIB_OBJS += util/pager.o
LIB_OBJS += util/header.o LIB_OBJS += util/header.o
LIB_OBJS += util/callchain.o LIB_OBJS += util/callchain.o
LIB_OBJS += util/values.o
LIB_OBJS += util/debug.o
LIB_OBJS += util/map.o
LIB_OBJS += util/thread.o
LIB_OBJS += util/trace-event-parse.o
LIB_OBJS += util/trace-event-read.o
LIB_OBJS += util/trace-event-info.o
BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-help.o
...@@ -345,6 +381,7 @@ BUILTIN_OBJS += builtin-record.o ...@@ -345,6 +381,7 @@ BUILTIN_OBJS += builtin-record.o
BUILTIN_OBJS += builtin-report.o BUILTIN_OBJS += builtin-report.o
BUILTIN_OBJS += builtin-stat.o BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-top.o BUILTIN_OBJS += builtin-top.o
BUILTIN_OBJS += builtin-trace.o
PERFLIBS = $(LIB_FILE) PERFLIBS = $(LIB_FILE)
......
This diff is collapsed.
...@@ -456,6 +456,7 @@ int cmd_help(int argc, const char **argv, const char *prefix __used) ...@@ -456,6 +456,7 @@ int cmd_help(int argc, const char **argv, const char *prefix __used)
break; break;
case HELP_FORMAT_WEB: case HELP_FORMAT_WEB:
show_html_page(argv[0]); show_html_page(argv[0]);
default:
break; break;
} }
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
#include "util/string.h" #include "util/string.h"
#include "util/header.h" #include "util/header.h"
#include "util/event.h"
#include "util/debug.h"
#include "util/trace-event.h"
#include <unistd.h> #include <unistd.h>
#include <sched.h> #include <sched.h>
...@@ -42,7 +45,6 @@ static int inherit = 1; ...@@ -42,7 +45,6 @@ static int inherit = 1;
static int force = 0; 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 inherit_stat = 0; static int inherit_stat = 0;
static int no_samples = 0; static int no_samples = 0;
static int sample_address = 0; static int sample_address = 0;
...@@ -62,24 +64,6 @@ static int file_new = 1; ...@@ -62,24 +64,6 @@ static int file_new = 1;
struct perf_header *header; struct perf_header *header;
struct mmap_event {
struct perf_event_header header;
u32 pid;
u32 tid;
u64 start;
u64 len;
u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
u32 pid;
u32 tid;
char comm[16];
};
struct mmap_data { struct mmap_data {
int counter; int counter;
void *base; void *base;
...@@ -419,8 +403,11 @@ static void create_counter(int counter, int cpu, pid_t pid) ...@@ -419,8 +403,11 @@ static void create_counter(int counter, int cpu, pid_t pid)
if (call_graph) if (call_graph)
attr->sample_type |= PERF_SAMPLE_CALLCHAIN; attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
if (raw_samples) if (raw_samples) {
attr->sample_type |= PERF_SAMPLE_TIME;
attr->sample_type |= PERF_SAMPLE_RAW; attr->sample_type |= PERF_SAMPLE_RAW;
attr->sample_type |= PERF_SAMPLE_CPU;
}
attr->mmap = track; attr->mmap = track;
attr->comm = track; attr->comm = track;
...@@ -563,6 +550,17 @@ static int __cmd_record(int argc, const char **argv) ...@@ -563,6 +550,17 @@ static int __cmd_record(int argc, const char **argv)
else else
header = perf_header__new(); header = perf_header__new();
if (raw_samples) {
read_tracing_data(attrs, nr_counters);
} else {
for (i = 0; i < nr_counters; i++) {
if (attrs[i].sample_type & PERF_SAMPLE_RAW) {
read_tracing_data(attrs, nr_counters);
break;
}
}
}
atexit(atexit_header); atexit(atexit_header);
if (!system_wide) { if (!system_wide) {
......
This diff is collapsed.
...@@ -42,6 +42,8 @@ ...@@ -42,6 +42,8 @@
#include "util/util.h" #include "util/util.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/event.h"
#include "util/debug.h"
#include <sys/prctl.h> #include <sys/prctl.h>
#include <math.h> #include <math.h>
...@@ -60,10 +62,7 @@ static struct perf_counter_attr default_attrs[] = { ...@@ -60,10 +62,7 @@ static struct perf_counter_attr default_attrs[] = {
}; };
#define MAX_RUN 100
static int system_wide = 0; static int system_wide = 0;
static int verbose = 0;
static unsigned int nr_cpus = 0; static unsigned int nr_cpus = 0;
static int run_idx = 0; static int run_idx = 0;
...@@ -75,26 +74,56 @@ static int null_run = 0; ...@@ -75,26 +74,56 @@ static int null_run = 0;
static int fd[MAX_NR_CPUS][MAX_COUNTERS]; static int fd[MAX_NR_CPUS][MAX_COUNTERS];
static u64 runtime_nsecs[MAX_RUN]; static int event_scaled[MAX_COUNTERS];
static u64 walltime_nsecs[MAX_RUN];
static u64 runtime_cycles[MAX_RUN];
static u64 event_res[MAX_RUN][MAX_COUNTERS][3]; struct stats
static u64 event_scaled[MAX_RUN][MAX_COUNTERS]; {
double n, mean, M2;
};
static u64 event_res_avg[MAX_COUNTERS][3]; static void update_stats(struct stats *stats, u64 val)
static u64 event_res_noise[MAX_COUNTERS][3]; {
double delta;
static u64 event_scaled_avg[MAX_COUNTERS]; stats->n++;
delta = val - stats->mean;
stats->mean += delta / stats->n;
stats->M2 += delta*(val - stats->mean);
}
static u64 runtime_nsecs_avg; static double avg_stats(struct stats *stats)
static u64 runtime_nsecs_noise; {
return stats->mean;
}
static u64 walltime_nsecs_avg; /*
static u64 walltime_nsecs_noise; * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
*
* (\Sum n_i^2) - ((\Sum n_i)^2)/n
* s^2 = -------------------------------
* n - 1
*
* http://en.wikipedia.org/wiki/Stddev
*
* The std dev of the mean is related to the std dev by:
*
* s
* s_mean = -------
* sqrt(n)
*
*/
static double stddev_stats(struct stats *stats)
{
double variance = stats->M2 / (stats->n - 1);
double variance_mean = variance / stats->n;
static u64 runtime_cycles_avg; return sqrt(variance_mean);
static u64 runtime_cycles_noise; }
struct stats event_res_stats[MAX_COUNTERS][3];
struct stats runtime_nsecs_stats;
struct stats walltime_nsecs_stats;
struct stats runtime_cycles_stats;
#define MATCH_EVENT(t, c, counter) \ #define MATCH_EVENT(t, c, counter) \
(attrs[counter].type == PERF_TYPE_##t && \ (attrs[counter].type == PERF_TYPE_##t && \
...@@ -149,12 +178,11 @@ static inline int nsec_counter(int counter) ...@@ -149,12 +178,11 @@ static inline int nsec_counter(int counter)
*/ */
static void read_counter(int counter) static void read_counter(int counter)
{ {
u64 *count, single_count[3]; u64 count[3], single_count[3];
unsigned int cpu; unsigned int cpu;
size_t res, nv; size_t res, nv;
int scaled; int scaled;
int i;
count = event_res[run_idx][counter];
count[0] = count[1] = count[2] = 0; count[0] = count[1] = count[2] = 0;
...@@ -179,24 +207,33 @@ static void read_counter(int counter) ...@@ -179,24 +207,33 @@ static void read_counter(int counter)
scaled = 0; scaled = 0;
if (scale) { if (scale) {
if (count[2] == 0) { if (count[2] == 0) {
event_scaled[run_idx][counter] = -1; event_scaled[counter] = -1;
count[0] = 0; count[0] = 0;
return; return;
} }
if (count[2] < count[1]) { if (count[2] < count[1]) {
event_scaled[run_idx][counter] = 1; event_scaled[counter] = 1;
count[0] = (unsigned long long) count[0] = (unsigned long long)
((double)count[0] * count[1] / count[2] + 0.5); ((double)count[0] * count[1] / count[2] + 0.5);
} }
} }
for (i = 0; i < 3; i++)
update_stats(&event_res_stats[counter][i], count[i]);
if (verbose) {
fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter),
count[0], count[1], count[2]);
}
/* /*
* Save the full runtime - to allow normalization during printout: * Save the full runtime - to allow normalization during printout:
*/ */
if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))
runtime_nsecs[run_idx] = count[0]; update_stats(&runtime_nsecs_stats, count[0]);
if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter)) if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter))
runtime_cycles[run_idx] = count[0]; update_stats(&runtime_cycles_stats, count[0]);
} }
static int run_perf_stat(int argc __used, const char **argv) static int run_perf_stat(int argc __used, const char **argv)
...@@ -270,7 +307,7 @@ static int run_perf_stat(int argc __used, const char **argv) ...@@ -270,7 +307,7 @@ static int run_perf_stat(int argc __used, const char **argv)
t1 = rdclock(); t1 = rdclock();
walltime_nsecs[run_idx] = t1 - t0; update_stats(&walltime_nsecs_stats, t1 - t0);
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++)
read_counter(counter); read_counter(counter);
...@@ -278,42 +315,38 @@ static int run_perf_stat(int argc __used, const char **argv) ...@@ -278,42 +315,38 @@ static int run_perf_stat(int argc __used, const char **argv)
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
static void print_noise(u64 *count, u64 *noise) static void print_noise(int counter, double avg)
{ {
if (run_count > 1) if (run_count == 1)
return;
fprintf(stderr, " ( +- %7.3f%% )", fprintf(stderr, " ( +- %7.3f%% )",
(double)noise[0]/(count[0]+1)*100.0); 100 * stddev_stats(&event_res_stats[counter][0]) / avg);
} }
static void nsec_printout(int counter, u64 *count, u64 *noise) static void nsec_printout(int counter, double avg)
{ {
double msecs = (double)count[0] / 1000000; double msecs = avg / 1e6;
fprintf(stderr, " %14.6f %-24s", msecs, event_name(counter)); fprintf(stderr, " %14.6f %-24s", msecs, event_name(counter));
if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) { if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) {
if (walltime_nsecs_avg)
fprintf(stderr, " # %10.3f CPUs ", fprintf(stderr, " # %10.3f CPUs ",
(double)count[0] / (double)walltime_nsecs_avg); avg / avg_stats(&walltime_nsecs_stats));
} }
print_noise(count, noise);
} }
static void abs_printout(int counter, u64 *count, u64 *noise) static void abs_printout(int counter, double avg)
{ {
fprintf(stderr, " %14Ld %-24s", count[0], event_name(counter)); fprintf(stderr, " %14.0f %-24s", avg, event_name(counter));
if (runtime_cycles_avg && if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {
MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {
fprintf(stderr, " # %10.3f IPC ", fprintf(stderr, " # %10.3f IPC ",
(double)count[0] / (double)runtime_cycles_avg); avg / avg_stats(&runtime_cycles_stats));
} else { } else {
if (runtime_nsecs_avg) {
fprintf(stderr, " # %10.3f M/sec", fprintf(stderr, " # %10.3f M/sec",
(double)count[0]/runtime_nsecs_avg*1000.0); 1000.0 * avg / avg_stats(&runtime_nsecs_stats));
}
} }
print_noise(count, noise);
} }
/* /*
...@@ -321,12 +354,8 @@ static void abs_printout(int counter, u64 *count, u64 *noise) ...@@ -321,12 +354,8 @@ static void abs_printout(int counter, u64 *count, u64 *noise)
*/ */
static void print_counter(int counter) static void print_counter(int counter)
{ {
u64 *count, *noise; double avg = avg_stats(&event_res_stats[counter][0]);
int scaled; int scaled = event_scaled[counter];
count = event_res_avg[counter];
noise = event_res_noise[counter];
scaled = event_scaled_avg[counter];
if (scaled == -1) { if (scaled == -1) {
fprintf(stderr, " %14s %-24s\n", fprintf(stderr, " %14s %-24s\n",
...@@ -335,110 +364,29 @@ static void print_counter(int counter) ...@@ -335,110 +364,29 @@ static void print_counter(int counter)
} }
if (nsec_counter(counter)) if (nsec_counter(counter))
nsec_printout(counter, count, noise); nsec_printout(counter, avg);
else else
abs_printout(counter, count, noise); abs_printout(counter, avg);
if (scaled) print_noise(counter, avg);
fprintf(stderr, " (scaled from %.2f%%)",
(double) count[2] / count[1] * 100);
fprintf(stderr, "\n"); if (scaled) {
} double avg_enabled, avg_running;
/*
* normalize_noise noise values down to stddev:
*/
static void normalize_noise(u64 *val)
{
double res;
res = (double)*val / (run_count * sqrt((double)run_count));
*val = (u64)res;
}
static void update_avg(const char *name, int idx, u64 *avg, u64 *val)
{
*avg += *val;
if (verbose > 1)
fprintf(stderr, "debug: %20s[%d]: %Ld\n", name, idx, *val);
}
/*
* Calculate the averages and noises:
*/
static void calc_avg(void)
{
int i, j;
if (verbose > 1)
fprintf(stderr, "\n");
for (i = 0; i < run_count; i++) { avg_enabled = avg_stats(&event_res_stats[counter][1]);
update_avg("runtime", 0, &runtime_nsecs_avg, runtime_nsecs + i); avg_running = avg_stats(&event_res_stats[counter][2]);
update_avg("walltime", 0, &walltime_nsecs_avg, walltime_nsecs + i);
update_avg("runtime_cycles", 0, &runtime_cycles_avg, runtime_cycles + i);
for (j = 0; j < nr_counters; j++) {
update_avg("counter/0", j,
event_res_avg[j]+0, event_res[i][j]+0);
update_avg("counter/1", j,
event_res_avg[j]+1, event_res[i][j]+1);
update_avg("counter/2", j,
event_res_avg[j]+2, event_res[i][j]+2);
if (event_scaled[i][j] != (u64)-1)
update_avg("scaled", j,
event_scaled_avg + j, event_scaled[i]+j);
else
event_scaled_avg[j] = -1;
}
}
runtime_nsecs_avg /= run_count;
walltime_nsecs_avg /= run_count;
runtime_cycles_avg /= run_count;
for (j = 0; j < nr_counters; j++) {
event_res_avg[j][0] /= run_count;
event_res_avg[j][1] /= run_count;
event_res_avg[j][2] /= run_count;
}
for (i = 0; i < run_count; i++) { fprintf(stderr, " (scaled from %.2f%%)",
runtime_nsecs_noise += 100 * avg_running / avg_enabled);
abs((s64)(runtime_nsecs[i] - runtime_nsecs_avg));
walltime_nsecs_noise +=
abs((s64)(walltime_nsecs[i] - walltime_nsecs_avg));
runtime_cycles_noise +=
abs((s64)(runtime_cycles[i] - runtime_cycles_avg));
for (j = 0; j < nr_counters; j++) {
event_res_noise[j][0] +=
abs((s64)(event_res[i][j][0] - event_res_avg[j][0]));
event_res_noise[j][1] +=
abs((s64)(event_res[i][j][1] - event_res_avg[j][1]));
event_res_noise[j][2] +=
abs((s64)(event_res[i][j][2] - event_res_avg[j][2]));
}
} }
normalize_noise(&runtime_nsecs_noise); fprintf(stderr, "\n");
normalize_noise(&walltime_nsecs_noise);
normalize_noise(&runtime_cycles_noise);
for (j = 0; j < nr_counters; j++) {
normalize_noise(&event_res_noise[j][0]);
normalize_noise(&event_res_noise[j][1]);
normalize_noise(&event_res_noise[j][2]);
}
} }
static void print_stat(int argc, const char **argv) static void print_stat(int argc, const char **argv)
{ {
int i, counter; int i, counter;
calc_avg();
fflush(stdout); fflush(stdout);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
...@@ -457,10 +405,11 @@ static void print_stat(int argc, const char **argv) ...@@ -457,10 +405,11 @@ static void print_stat(int argc, const char **argv)
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, " %14.9f seconds time elapsed", fprintf(stderr, " %14.9f seconds time elapsed",
(double)walltime_nsecs_avg/1e9); avg_stats(&walltime_nsecs_stats)/1e9);
if (run_count > 1) { if (run_count > 1) {
fprintf(stderr, " ( +- %7.3f%% )", fprintf(stderr, " ( +- %7.3f%% )",
100.0*(double)walltime_nsecs_noise/(double)walltime_nsecs_avg); 100*stddev_stats(&walltime_nsecs_stats) /
avg_stats(&walltime_nsecs_stats));
} }
fprintf(stderr, "\n\n"); fprintf(stderr, "\n\n");
} }
...@@ -515,7 +464,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -515,7 +464,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
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)
usage_with_options(stat_usage, options); usage_with_options(stat_usage, options);
/* Set attrs and nr_counters if no event is selected and !null_run */ /* Set attrs and nr_counters if no event is selected and !null_run */
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/debug.h"
#include <assert.h> #include <assert.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -68,8 +70,6 @@ static int group = 0; ...@@ -68,8 +70,6 @@ static int group = 0;
static unsigned int page_size; static unsigned int page_size;
static unsigned int mmap_pages = 16; static unsigned int mmap_pages = 16;
static int freq = 0; static int freq = 0;
static int verbose = 0;
static char *vmlinux = NULL;
static int delay_secs = 2; static int delay_secs = 2;
static int zero; static int zero;
...@@ -122,7 +122,8 @@ static void parse_source(struct sym_entry *syme) ...@@ -122,7 +122,8 @@ static void parse_source(struct sym_entry *syme)
struct module *module; struct module *module;
struct section *section = NULL; struct section *section = NULL;
FILE *file; FILE *file;
char command[PATH_MAX*2], *path = vmlinux; char command[PATH_MAX*2];
const char *path = vmlinux_name;
u64 start, end, len; u64 start, end, len;
if (!syme) if (!syme)
...@@ -338,8 +339,6 @@ static void show_details(struct sym_entry *syme) ...@@ -338,8 +339,6 @@ static void show_details(struct sym_entry *syme)
printf("%d lines not displayed, maybe increase display entries [e]\n", more); printf("%d lines not displayed, maybe increase display entries [e]\n", more);
} }
struct dso *kernel_dso;
/* /*
* Symbols will be added here in record_ip and will get out * Symbols will be added here in record_ip and will get out
* after decayed. * after decayed.
...@@ -486,15 +485,22 @@ static void print_sym_table(void) ...@@ -486,15 +485,22 @@ static void print_sym_table(void)
else else
printf(" weight samples pcnt"); printf(" weight samples pcnt");
printf(" RIP kernel function\n" if (verbose)
" ______ _______ _____ ________________ _______________\n\n" printf(" RIP ");
); printf(" kernel function\n");
printf(" %s _______ _____",
nr_counters == 1 ? " " : "______");
if (verbose)
printf(" ________________");
printf(" _______________\n\n");
for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) {
struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); struct symbol *sym;
struct symbol *sym = (struct symbol *)(syme + 1);
double pcnt; double pcnt;
syme = rb_entry(nd, struct sym_entry, rb_node);
sym = (struct symbol *)(syme + 1);
if (++printed > print_entries || (int)syme->snap_count < count_filter) if (++printed > print_entries || (int)syme->snap_count < count_filter)
continue; continue;
...@@ -507,7 +513,9 @@ static void print_sym_table(void) ...@@ -507,7 +513,9 @@ static void print_sym_table(void)
printf("%9.1f %10ld - ", syme->weight, syme->snap_count); printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
percent_color_fprintf(stdout, "%4.1f%%", pcnt); percent_color_fprintf(stdout, "%4.1f%%", pcnt);
printf(" - %016llx : %s", sym->start, sym->name); if (verbose)
printf(" - %016llx", sym->start);
printf(" : %s", sym->name);
if (sym->module) if (sym->module)
printf("\t[%s]", sym->module->name); printf("\t[%s]", sym->module->name);
printf("\n"); printf("\n");
...@@ -613,7 +621,7 @@ static void print_mapped_keys(void) ...@@ -613,7 +621,7 @@ static void print_mapped_keys(void)
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
if (vmlinux) { if (vmlinux_name) {
fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
fprintf(stdout, "\t[S] stop annotation.\n"); fprintf(stdout, "\t[S] stop annotation.\n");
...@@ -642,7 +650,9 @@ static int key_mapped(int c) ...@@ -642,7 +650,9 @@ static int key_mapped(int c)
case 'F': case 'F':
case 's': case 's':
case 'S': case 'S':
return vmlinux ? 1 : 0; return vmlinux_name ? 1 : 0;
default:
break;
} }
return 0; return 0;
...@@ -728,6 +738,8 @@ static void handle_keypress(int c) ...@@ -728,6 +738,8 @@ static void handle_keypress(int c)
case 'z': case 'z':
zero = ~zero; zero = ~zero;
break; break;
default:
break;
} }
} }
...@@ -816,13 +828,13 @@ static int parse_symbols(void) ...@@ -816,13 +828,13 @@ static int parse_symbols(void)
{ {
struct rb_node *node; struct rb_node *node;
struct symbol *sym; struct symbol *sym;
int modules = vmlinux ? 1 : 0; int use_modules = vmlinux_name ? 1 : 0;
kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry));
if (kernel_dso == NULL) if (kernel_dso == NULL)
return -1; return -1;
if (dso__load_kernel(kernel_dso, vmlinux, symbol_filter, verbose, modules) <= 0) if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0)
goto out_delete_dso; goto out_delete_dso;
node = rb_first(&kernel_dso->syms); node = rb_first(&kernel_dso->syms);
...@@ -937,26 +949,6 @@ static void mmap_read_counter(struct mmap_data *md) ...@@ -937,26 +949,6 @@ static void mmap_read_counter(struct mmap_data *md)
last_read = this_read; last_read = this_read;
for (; old != head;) { for (; old != head;) {
struct ip_event {
struct perf_event_header header;
u64 ip;
u32 pid, target_pid;
};
struct mmap_event {
struct perf_event_header header;
u32 pid, target_pid;
u64 start;
u64 len;
u64 pgoff;
char filename[PATH_MAX];
};
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
struct mmap_event mmap;
} event_t;
event_t *event = (event_t *)&data[old & md->mask]; event_t *event = (event_t *)&data[old & md->mask];
event_t event_copy; event_t event_copy;
...@@ -1138,7 +1130,7 @@ static const struct option options[] = { ...@@ -1138,7 +1130,7 @@ static const struct option options[] = {
"system-wide collection from all CPUs"), "system-wide collection from all CPUs"),
OPT_INTEGER('C', "CPU", &profile_cpu, OPT_INTEGER('C', "CPU", &profile_cpu,
"CPU to profile on"), "CPU to profile on"),
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages, OPT_INTEGER('m', "mmap-pages", &mmap_pages,
"number of mmap data pages"), "number of mmap data pages"),
OPT_INTEGER('r', "realtime", &realtime_prio, OPT_INTEGER('r', "realtime", &realtime_prio,
......
#include "builtin.h"
#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
#include "util/parse-options.h"
#include "perf.h"
#include "util/debug.h"
#include "util/trace-event.h"
static char const *input_name = "perf.data";
static int input;
static unsigned long page_size;
static unsigned long mmap_window = 32;
static unsigned long total = 0;
static unsigned long total_comm = 0;
static struct rb_root threads;
static struct thread *last_match;
static struct perf_header *header;
static u64 sample_type;
static int
process_comm_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread;
thread = threads__findnew(event->comm.pid, &threads, &last_match);
dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->comm.comm, event->comm.pid);
if (thread == NULL ||
thread__set_comm(thread, event->comm.comm)) {
dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n");
return -1;
}
total_comm++;
return 0;
}
static int
process_sample_event(event_t *event, unsigned long offset, unsigned long head)
{
char level;
int show = 0;
struct dso *dso = NULL;
struct thread *thread;
u64 ip = event->ip.ip;
u64 timestamp = -1;
u32 cpu = -1;
u64 period = 1;
void *more_data = event->ip.__more_data;
int cpumode;
thread = threads__findnew(event->ip.pid, &threads, &last_match);
if (sample_type & PERF_SAMPLE_TIME) {
timestamp = *(u64 *)more_data;
more_data += sizeof(u64);
}
if (sample_type & PERF_SAMPLE_CPU) {
cpu = *(u32 *)more_data;
more_data += sizeof(u32);
more_data += sizeof(u32); /* reserved */
}
if (sample_type & PERF_SAMPLE_PERIOD) {
period = *(u64 *)more_data;
more_data += sizeof(u64);
}
dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.misc,
event->ip.pid, event->ip.tid,
(void *)(long)ip,
(long long)period);
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
if (thread == NULL) {
eprintf("problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}
cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK;
if (cpumode == PERF_EVENT_MISC_KERNEL) {
show = SHOW_KERNEL;
level = 'k';
dso = kernel_dso;
dump_printf(" ...... dso: %s\n", dso->name);
} else if (cpumode == PERF_EVENT_MISC_USER) {
show = SHOW_USER;
level = '.';
} else {
show = SHOW_HV;
level = 'H';
dso = hypervisor_dso;
dump_printf(" ...... dso: [hypervisor]\n");
}
if (sample_type & PERF_SAMPLE_RAW) {
struct {
u32 size;
char data[0];
} *raw = more_data;
/*
* FIXME: better resolve from pid from the struct trace_entry
* field, although it should be the same than this perf
* event pid
*/
print_event(cpu, raw->data, raw->size, timestamp, thread->comm);
}
total += period;
return 0;
}
static int
process_event(event_t *event, unsigned long offset, unsigned long head)
{
trace_event(event);
switch (event->header.type) {
case PERF_EVENT_MMAP ... PERF_EVENT_LOST:
return 0;
case PERF_EVENT_COMM:
return process_comm_event(event, offset, head);
case PERF_EVENT_EXIT ... PERF_EVENT_READ:
return 0;
case PERF_EVENT_SAMPLE:
return process_sample_event(event, offset, head);
case PERF_EVENT_MAX:
default:
return -1;
}
return 0;
}
static int __cmd_trace(void)
{
int ret, rc = EXIT_FAILURE;
unsigned long offset = 0;
unsigned long head = 0;
struct stat perf_stat;
event_t *event;
uint32_t size;
char *buf;
trace_report();
register_idle_thread(&threads, &last_match);
input = open(input_name, O_RDONLY);
if (input < 0) {
perror("failed to open file");
exit(-1);
}
ret = fstat(input, &perf_stat);
if (ret < 0) {
perror("failed to stat file");
exit(-1);
}
if (!perf_stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
exit(0);
}
header = perf_header__read(input);
head = header->data_offset;
sample_type = perf_header__sample_type(header);
if (!(sample_type & PERF_SAMPLE_RAW))
die("No trace sample to read. Did you call perf record "
"without -R?");
if (load_kernel() < 0) {
perror("failed to load kernel symbols");
return EXIT_FAILURE;
}
remap:
buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
MAP_SHARED, input, offset);
if (buf == MAP_FAILED) {
perror("failed to mmap file");
exit(-1);
}
more:
event = (event_t *)(buf + head);
size = event->header.size;
if (!size)
size = 8;
if (head + event->header.size >= page_size * mmap_window) {
unsigned long shift = page_size * (head / page_size);
int res;
res = munmap(buf, page_size * mmap_window);
assert(res == 0);
offset += shift;
head -= shift;
goto remap;
}
size = event->header.size;
if (!size || process_event(event, offset, head) < 0) {
/*
* assume we lost track of the stream, check alignment, and
* increment a single u64 in the hope to catch on again 'soon'.
*/
if (unlikely(head & 7))
head &= ~7ULL;
size = 8;
}
head += size;
if (offset + head < (unsigned long)perf_stat.st_size)
goto more;
rc = EXIT_SUCCESS;
close(input);
return rc;
}
static const char * const annotate_usage[] = {
"perf trace [<options>] <command>",
NULL
};
static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_END()
};
int cmd_trace(int argc, const char **argv, const char *prefix __used)
{
symbol__init();
page_size = getpagesize();
argc = parse_options(argc, argv, options, annotate_usage, 0);
if (argc) {
/*
* Special case: if there's an argument left then assume tha
* it's a symbol filter:
*/
if (argc > 1)
usage_with_options(annotate_usage, options);
}
setup_pager();
return __cmd_trace();
}
...@@ -22,5 +22,6 @@ extern int cmd_stat(int argc, const char **argv, const char *prefix); ...@@ -22,5 +22,6 @@ extern int cmd_stat(int argc, const char **argv, const char *prefix);
extern int cmd_top(int argc, const char **argv, const char *prefix); extern int cmd_top(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_list(int argc, const char **argv, const char *prefix); extern int cmd_list(int argc, const char **argv, const char *prefix);
extern int cmd_trace(int argc, const char **argv, const char *prefix);
#endif #endif
...@@ -292,6 +292,7 @@ static void handle_internal_command(int argc, const char **argv) ...@@ -292,6 +292,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "top", cmd_top, 0 }, { "top", cmd_top, 0 },
{ "annotate", cmd_annotate, 0 }, { "annotate", cmd_annotate, 0 },
{ "version", cmd_version, 0 }, { "version", cmd_version, 0 },
{ "trace", cmd_trace, 0 },
}; };
unsigned int i; unsigned int i;
static const char ext[] = STRIP_EXTENSION; static const char ext[] = STRIP_EXTENSION;
......
...@@ -50,7 +50,8 @@ const char *make_absolute_path(const char *path) ...@@ -50,7 +50,8 @@ const char *make_absolute_path(const char *path)
die ("Could not get current working directory"); die ("Could not get current working directory");
if (last_elem) { if (last_elem) {
int len = strlen(buf); len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX) if (len + strlen(last_elem) + 2 > PATH_MAX)
die ("Too long path name: '%s/%s'", die ("Too long path name: '%s/%s'",
buf, last_elem); buf, last_elem);
......
...@@ -52,7 +52,6 @@ extern const char *perf_mailmap_file; ...@@ -52,7 +52,6 @@ extern const char *perf_mailmap_file;
extern void maybe_flush_or_die(FILE *, const char *); extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd); extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode); extern int copy_file(const char *dst, const char *src, int mode);
extern ssize_t read_in_full(int fd, void *buf, size_t count);
extern ssize_t write_in_full(int fd, const void *buf, size_t count); extern ssize_t write_in_full(int fd, const void *buf, size_t count);
extern void write_or_die(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
......
...@@ -50,6 +50,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, ...@@ -50,6 +50,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
else else
p = &(*p)->rb_right; p = &(*p)->rb_right;
break; break;
case CHAIN_NONE:
default: default:
break; break;
} }
...@@ -143,6 +144,7 @@ int register_callchain_param(struct callchain_param *param) ...@@ -143,6 +144,7 @@ int register_callchain_param(struct callchain_param *param)
case CHAIN_FLAT: case CHAIN_FLAT:
param->sort = sort_chain_flat; param->sort = sort_chain_flat;
break; break;
case CHAIN_NONE:
default: default:
return -1; return -1;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "../perf.h" #include "../perf.h"
#include <linux/list.h> #include <linux/list.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include "util.h"
#include "symbol.h" #include "symbol.h"
enum chain_mode { enum chain_mode {
......
...@@ -166,7 +166,7 @@ int perf_color_default_config(const char *var, const char *value, void *cb) ...@@ -166,7 +166,7 @@ int perf_color_default_config(const char *var, const char *value, void *cb)
return perf_default_config(var, value, cb); return perf_default_config(var, value, cb);
} }
static int color_vfprintf(FILE *fp, const char *color, const char *fmt, static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
va_list args, const char *trail) va_list args, const char *trail)
{ {
int r = 0; int r = 0;
...@@ -191,6 +191,10 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt, ...@@ -191,6 +191,10 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt,
return r; return r;
} }
int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
{
return __color_vfprintf(fp, color, fmt, args, NULL);
}
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
...@@ -199,7 +203,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) ...@@ -199,7 +203,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
int r; int r;
va_start(args, fmt); va_start(args, fmt);
r = color_vfprintf(fp, color, fmt, args, NULL); r = color_vfprintf(fp, color, fmt, args);
va_end(args); va_end(args);
return r; return r;
} }
...@@ -209,7 +213,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) ...@@ -209,7 +213,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
va_list args; va_list args;
int r; int r;
va_start(args, fmt); va_start(args, fmt);
r = color_vfprintf(fp, color, fmt, args, "\n"); r = __color_vfprintf(fp, color, fmt, args, "\n");
va_end(args); va_end(args);
return r; return r;
} }
...@@ -242,9 +246,9 @@ int color_fwrite_lines(FILE *fp, const char *color, ...@@ -242,9 +246,9 @@ int color_fwrite_lines(FILE *fp, const char *color,
return 0; return 0;
} }
char *get_percent_color(double percent) const char *get_percent_color(double percent)
{ {
char *color = PERF_COLOR_NORMAL; const char *color = PERF_COLOR_NORMAL;
/* /*
* We color high-overhead entries in red, mid-overhead * We color high-overhead entries in red, mid-overhead
...@@ -263,7 +267,7 @@ char *get_percent_color(double percent) ...@@ -263,7 +267,7 @@ char *get_percent_color(double percent)
int percent_color_fprintf(FILE *fp, const char *fmt, double percent) int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
{ {
int r; int r;
char *color; const char *color;
color = get_percent_color(percent); color = get_percent_color(percent);
r = color_fprintf(fp, color, fmt, percent); r = color_fprintf(fp, color, fmt, percent);
......
...@@ -32,10 +32,11 @@ int perf_color_default_config(const char *var, const char *value, void *cb); ...@@ -32,10 +32,11 @@ int perf_color_default_config(const char *var, const char *value, void *cb);
int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
void color_parse(const char *value, const char *var, char *dst); void color_parse(const char *value, const char *var, char *dst);
void color_parse_mem(const char *value, int len, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst);
int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
int percent_color_fprintf(FILE *fp, const char *fmt, double percent); int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
char *get_percent_color(double percent); const char *get_percent_color(double percent);
#endif /* COLOR_H */ #endif /* COLOR_H */
...@@ -160,17 +160,18 @@ static int get_extended_base_var(char *name, int baselen, int c) ...@@ -160,17 +160,18 @@ static int get_extended_base_var(char *name, int baselen, int c)
name[baselen++] = '.'; name[baselen++] = '.';
for (;;) { for (;;) {
int c = get_next_char(); int ch = get_next_char();
if (c == '\n')
if (ch == '\n')
return -1; return -1;
if (c == '"') if (ch == '"')
break; break;
if (c == '\\') { if (ch == '\\') {
c = get_next_char(); ch = get_next_char();
if (c == '\n') if (ch == '\n')
return -1; return -1;
} }
name[baselen++] = c; name[baselen++] = ch;
if (baselen > MAXNAME / 2) if (baselen > MAXNAME / 2)
return -1; return -1;
} }
...@@ -530,6 +531,8 @@ static int store_aux(const char* key, const char* value, void *cb __used) ...@@ -530,6 +531,8 @@ static int store_aux(const char* key, const char* value, void *cb __used)
store.offset[store.seen] = ftell(config_file); store.offset[store.seen] = ftell(config_file);
} }
} }
default:
break;
} }
return 0; return 0;
} }
...@@ -619,6 +622,7 @@ static ssize_t find_beginning_of_line(const char* contents, size_t size, ...@@ -619,6 +622,7 @@ static ssize_t find_beginning_of_line(const char* contents, size_t size,
switch (contents[offset]) { switch (contents[offset]) {
case '=': equal_offset = offset; break; case '=': equal_offset = offset; break;
case ']': bracket_offset = offset; break; case ']': bracket_offset = offset; break;
default: break;
} }
if (offset > 0 && contents[offset-1] == '\\') { if (offset > 0 && contents[offset-1] == '\\') {
offset_ = offset; offset_ = offset;
...@@ -742,9 +746,9 @@ int perf_config_set_multivar(const char* key, const char* value, ...@@ -742,9 +746,9 @@ int perf_config_set_multivar(const char* key, const char* value,
goto write_err_out; goto write_err_out;
} else { } else {
struct stat st; struct stat st;
char* contents; char *contents;
ssize_t contents_sz, copy_begin, copy_end; ssize_t contents_sz, copy_begin, copy_end;
int i, new_line = 0; int new_line = 0;
if (value_regex == NULL) if (value_regex == NULL)
store.value_regex = NULL; store.value_regex = NULL;
......
/* For general debugging purposes */
#include "../perf.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "color.h"
#include "event.h"
#include "debug.h"
int verbose = 0;
int dump_trace = 0;
int eprintf(const char *fmt, ...)
{
va_list args;
int ret = 0;
if (verbose) {
va_start(args, fmt);
ret = vfprintf(stderr, fmt, args);
va_end(args);
}
return ret;
}
int dump_printf(const char *fmt, ...)
{
va_list args;
int ret = 0;
if (dump_trace) {
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
}
return ret;
}
static int dump_printf_color(const char *fmt, const char *color, ...)
{
va_list args;
int ret = 0;
if (dump_trace) {
va_start(args, color);
ret = color_vfprintf(stdout, color, fmt, args);
va_end(args);
}
return ret;
}
void trace_event(event_t *event)
{
unsigned char *raw_event = (void *)event;
const char *color = PERF_COLOR_BLUE;
int i, j;
if (!dump_trace)
return;
dump_printf(".");
dump_printf_color("\n. ... raw event: size %d bytes\n", color,
event->header.size);
for (i = 0; i < event->header.size; i++) {
if ((i & 15) == 0) {
dump_printf(".");
dump_printf_color(" %04x: ", color, i);
}
dump_printf_color(" %02x", color, raw_event[i]);
if (((i & 15) == 15) || i == event->header.size-1) {
dump_printf_color(" ", color);
for (j = 0; j < 15-(i & 15); j++)
dump_printf_color(" ", color);
for (j = 0; j < (i & 15); j++) {
if (isprint(raw_event[i-15+j]))
dump_printf_color("%c", color,
raw_event[i-15+j]);
else
dump_printf_color(".", color);
}
dump_printf_color("\n", color);
}
}
dump_printf(".\n");
}
/* For debugging general purposes */
extern int verbose;
extern int dump_trace;
int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(event_t *event);
#ifndef __PERF_EVENT_H
#define __PERF_EVENT_H
#include "../perf.h"
#include "util.h"
#include <linux/list.h>
enum {
SHOW_KERNEL = 1,
SHOW_USER = 2,
SHOW_HV = 4,
};
/*
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
*/
struct ip_event {
struct perf_event_header header;
u64 ip;
u32 pid, tid;
unsigned char __more_data[];
};
struct mmap_event {
struct perf_event_header header;
u32 pid, tid;
u64 start;
u64 len;
u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
u32 pid, tid;
char comm[16];
};
struct fork_event {
struct perf_event_header header;
u32 pid, ppid;
u32 tid, ptid;
};
struct lost_event {
struct perf_event_header header;
u64 id;
u64 lost;
};
/*
* PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
*/
struct read_event {
struct perf_event_header header;
u32 pid,tid;
u64 value;
u64 time_enabled;
u64 time_running;
u64 id;
};
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
struct mmap_event mmap;
struct comm_event comm;
struct fork_event fork;
struct lost_event lost;
struct read_event read;
} event_t;
struct map {
struct list_head node;
u64 start;
u64 end;
u64 pgoff;
u64 (*map_ip)(struct map *, u64);
struct dso *dso;
};
static inline u64 map__map_ip(struct map *map, u64 ip)
{
return ip - map->start + map->pgoff;
}
static inline u64 vdso__map_ip(struct map *map __used, u64 ip)
{
return ip;
}
struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
#endif
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define MAX_ARGS 32 #define MAX_ARGS 32
extern char **environ;
static const char *argv_exec_path; static const char *argv_exec_path;
static const char *argv0_path; static const char *argv0_path;
......
...@@ -237,9 +237,44 @@ struct perf_header *perf_header__read(int fd) ...@@ -237,9 +237,44 @@ struct perf_header *perf_header__read(int fd)
self->data_offset = f_header.data.offset; self->data_offset = f_header.data.offset;
self->data_size = f_header.data.size; self->data_size = f_header.data.size;
lseek(fd, self->data_offset + self->data_size, SEEK_SET); lseek(fd, self->data_offset, SEEK_SET);
self->frozen = 1; self->frozen = 1;
return self; return self;
} }
u64 perf_header__sample_type(struct perf_header *header)
{
u64 type = 0;
int i;
for (i = 0; i < header->attrs; i++) {
struct perf_header_attr *attr = header->attr[i];
if (!type)
type = attr->attr.sample_type;
else if (type != attr->attr.sample_type)
die("non matching sample_type");
}
return type;
}
struct perf_counter_attr *
perf_header__find_attr(u64 id, struct perf_header *header)
{
int i;
for (i = 0; i < header->attrs; i++) {
struct perf_header_attr *attr = header->attr[i];
int j;
for (j = 0; j < attr->ids; j++) {
if (attr->id[j] == id)
return &attr->attr;
}
}
return NULL;
}
...@@ -31,6 +31,10 @@ struct perf_header_attr * ...@@ -31,6 +31,10 @@ struct perf_header_attr *
perf_header_attr__new(struct perf_counter_attr *attr); perf_header_attr__new(struct perf_counter_attr *attr);
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
u64 perf_header__sample_type(struct perf_header *header);
struct perf_counter_attr *
perf_header__find_attr(u64 id, struct perf_header *header);
struct perf_header *perf_header__new(void); struct perf_header *perf_header__new(void);
......
#include "event.h"
#include "symbol.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static inline int is_anon_memory(const char *filename)
{
return strcmp(filename, "//anon") == 0;
}
static int strcommon(const char *pathname, char *cwd, int cwdlen)
{
int n = 0;
while (n < cwdlen && pathname[n] == cwd[n])
++n;
return n;
}
struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen)
{
struct map *self = malloc(sizeof(*self));
if (self != NULL) {
const char *filename = event->filename;
char newfilename[PATH_MAX];
int anon;
if (cwd) {
int n = strcommon(filename, cwd, cwdlen);
if (n == cwdlen) {
snprintf(newfilename, sizeof(newfilename),
".%s", filename + n);
filename = newfilename;
}
}
anon = is_anon_memory(filename);
if (anon) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid);
filename = newfilename;
}
self->start = event->start;
self->end = event->start + event->len;
self->pgoff = event->pgoff;
self->dso = dsos__findnew(filename);
if (self->dso == NULL)
goto out_delete;
if (self->dso == vdso || anon)
self->map_ip = vdso__map_ip;
else
self->map_ip = map__map_ip;
}
return self;
out_delete:
free(self);
return NULL;
}
struct map *map__clone(struct map *self)
{
struct map *map = malloc(sizeof(*self));
if (!map)
return NULL;
memcpy(map, self, sizeof(*self));
return map;
}
int map__overlap(struct map *l, struct map *r)
{
if (l->start > r->start) {
struct map *t = l;
l = r;
r = t;
}
if (l->end > r->start)
return 1;
return 0;
}
size_t map__fprintf(struct map *self, FILE *fp)
{
return fprintf(fp, " %Lx-%Lx %Lx %s\n",
self->start, self->end, self->pgoff, self->dso->name);
}
...@@ -436,9 +436,9 @@ static int mod_dso__load_module_paths(struct mod_dso *self) ...@@ -436,9 +436,9 @@ static int mod_dso__load_module_paths(struct mod_dso *self)
goto out_failure; goto out_failure;
while (!feof(file)) { while (!feof(file)) {
char *path, *name, *tmp; char *name, *tmp;
struct module *module; struct module *module;
int line_len, len; int line_len;
line_len = getline(&line, &n, file); line_len = getline(&line, &n, file);
if (line_len < 0) if (line_len < 0)
......
#include "../perf.h"
#include "util.h" #include "util.h"
#include "../perf.h"
#include "parse-options.h" #include "parse-options.h"
#include "parse-events.h" #include "parse-events.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "string.h" #include "string.h"
#include "cache.h" #include "cache.h"
extern char *strcasestr(const char *haystack, const char *needle);
int nr_counters; int nr_counters;
struct perf_counter_attr attrs[MAX_COUNTERS]; struct perf_counter_attr attrs[MAX_COUNTERS];
...@@ -16,8 +14,8 @@ struct perf_counter_attr attrs[MAX_COUNTERS]; ...@@ -16,8 +14,8 @@ struct perf_counter_attr attrs[MAX_COUNTERS];
struct event_symbol { struct event_symbol {
u8 type; u8 type;
u64 config; u64 config;
char *symbol; const char *symbol;
char *alias; const char *alias;
}; };
char debugfs_path[MAXPATHLEN]; char debugfs_path[MAXPATHLEN];
...@@ -51,7 +49,7 @@ static struct event_symbol event_symbols[] = { ...@@ -51,7 +49,7 @@ static struct event_symbol event_symbols[] = {
#define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE)
#define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT)
static char *hw_event_names[] = { static const char *hw_event_names[] = {
"cycles", "cycles",
"instructions", "instructions",
"cache-references", "cache-references",
...@@ -61,7 +59,7 @@ static char *hw_event_names[] = { ...@@ -61,7 +59,7 @@ static char *hw_event_names[] = {
"bus-cycles", "bus-cycles",
}; };
static char *sw_event_names[] = { static const char *sw_event_names[] = {
"cpu-clock-msecs", "cpu-clock-msecs",
"task-clock-msecs", "task-clock-msecs",
"page-faults", "page-faults",
...@@ -73,7 +71,7 @@ static char *sw_event_names[] = { ...@@ -73,7 +71,7 @@ static char *sw_event_names[] = {
#define MAX_ALIASES 8 #define MAX_ALIASES 8
static char *hw_cache[][MAX_ALIASES] = { static const char *hw_cache[][MAX_ALIASES] = {
{ "L1-dcache", "l1-d", "l1d", "L1-data", }, { "L1-dcache", "l1-d", "l1d", "L1-data", },
{ "L1-icache", "l1-i", "l1i", "L1-instruction", }, { "L1-icache", "l1-i", "l1i", "L1-instruction", },
{ "LLC", "L2" }, { "LLC", "L2" },
...@@ -82,13 +80,13 @@ static char *hw_cache[][MAX_ALIASES] = { ...@@ -82,13 +80,13 @@ static char *hw_cache[][MAX_ALIASES] = {
{ "branch", "branches", "bpu", "btb", "bpc", }, { "branch", "branches", "bpu", "btb", "bpc", },
}; };
static char *hw_cache_op[][MAX_ALIASES] = { static const char *hw_cache_op[][MAX_ALIASES] = {
{ "load", "loads", "read", }, { "load", "loads", "read", },
{ "store", "stores", "write", }, { "store", "stores", "write", },
{ "prefetch", "prefetches", "speculative-read", "speculative-load", }, { "prefetch", "prefetches", "speculative-read", "speculative-load", },
}; };
static char *hw_cache_result[][MAX_ALIASES] = { static const char *hw_cache_result[][MAX_ALIASES] = {
{ "refs", "Reference", "ops", "access", }, { "refs", "Reference", "ops", "access", },
{ "misses", "miss", }, { "misses", "miss", },
}; };
...@@ -113,11 +111,9 @@ static unsigned long hw_cache_stat[C(MAX)] = { ...@@ -113,11 +111,9 @@ static unsigned long hw_cache_stat[C(MAX)] = {
[C(BPU)] = (CACHE_READ), [C(BPU)] = (CACHE_READ),
}; };
#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \
if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \ if (sys_dirent.d_type == DT_DIR && \
sys_dirent.d_name) && \
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
(strcmp(sys_dirent.d_name, ".")) && \ (strcmp(sys_dirent.d_name, ".")) && \
(strcmp(sys_dirent.d_name, ".."))) (strcmp(sys_dirent.d_name, "..")))
...@@ -136,11 +132,9 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) ...@@ -136,11 +132,9 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
return 0; return 0;
} }
#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \
while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ if (evt_dirent.d_type == DT_DIR && \
sys_dirent.d_name, evt_dirent.d_name) && \
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
(strcmp(evt_dirent.d_name, ".")) && \ (strcmp(evt_dirent.d_name, ".")) && \
(strcmp(evt_dirent.d_name, "..")) && \ (strcmp(evt_dirent.d_name, "..")) && \
(!tp_event_has_id(&sys_dirent, &evt_dirent))) (!tp_event_has_id(&sys_dirent, &evt_dirent)))
...@@ -158,34 +152,39 @@ int valid_debugfs_mount(const char *debugfs) ...@@ -158,34 +152,39 @@ int valid_debugfs_mount(const char *debugfs)
return 0; return 0;
} }
static char *tracepoint_id_to_name(u64 config) struct tracepoint_path *tracepoint_id_to_path(u64 config)
{ {
static char tracepoint_name[2 * MAX_EVENT_LENGTH]; struct tracepoint_path *path = NULL;
DIR *sys_dir, *evt_dir; DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
struct stat st;
char id_buf[4]; char id_buf[4];
int fd; int sys_dir_fd, fd;
u64 id; u64 id;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
if (valid_debugfs_mount(debugfs_path)) if (valid_debugfs_mount(debugfs_path))
return "unkown"; return NULL;
sys_dir = opendir(debugfs_path); sys_dir = opendir(debugfs_path);
if (!sys_dir) if (!sys_dir)
goto cleanup; goto cleanup;
sys_dir_fd = dirfd(sys_dir);
for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { for_each_subsystem(sys_dir, sys_dirent, sys_next) {
evt_dir = opendir(evt_path); int dfd = openat(sys_dir_fd, sys_dirent.d_name,
if (!evt_dir) O_RDONLY|O_DIRECTORY), evt_dir_fd;
goto cleanup; if (dfd == -1)
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, continue;
evt_path, st) { evt_dir = fdopendir(dfd);
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", if (!evt_dir) {
debugfs_path, sys_dirent.d_name, close(dfd);
continue;
}
evt_dir_fd = dirfd(evt_dir);
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
snprintf(evt_path, MAXPATHLEN, "%s/id",
evt_dirent.d_name); evt_dirent.d_name);
fd = open(evt_path, O_RDONLY); fd = openat(evt_dir_fd, evt_path, O_RDONLY);
if (fd < 0) if (fd < 0)
continue; continue;
if (read(fd, id_buf, sizeof(id_buf)) < 0) { if (read(fd, id_buf, sizeof(id_buf)) < 0) {
...@@ -197,10 +196,23 @@ static char *tracepoint_id_to_name(u64 config) ...@@ -197,10 +196,23 @@ static char *tracepoint_id_to_name(u64 config)
if (id == config) { if (id == config) {
closedir(evt_dir); closedir(evt_dir);
closedir(sys_dir); closedir(sys_dir);
snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, path = calloc(1, sizeof(path));
"%s:%s", sys_dirent.d_name, path->system = malloc(MAX_EVENT_LENGTH);
evt_dirent.d_name); if (!path->system) {
return tracepoint_name; free(path);
return NULL;
}
path->name = malloc(MAX_EVENT_LENGTH);
if (!path->name) {
free(path->system);
free(path);
return NULL;
}
strncpy(path->system, sys_dirent.d_name,
MAX_EVENT_LENGTH);
strncpy(path->name, evt_dirent.d_name,
MAX_EVENT_LENGTH);
return path;
} }
} }
closedir(evt_dir); closedir(evt_dir);
...@@ -208,7 +220,25 @@ static char *tracepoint_id_to_name(u64 config) ...@@ -208,7 +220,25 @@ static char *tracepoint_id_to_name(u64 config)
cleanup: cleanup:
closedir(sys_dir); closedir(sys_dir);
return "unkown"; return NULL;
}
#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1)
static const char *tracepoint_id_to_name(u64 config)
{
static char buf[TP_PATH_LEN];
struct tracepoint_path *path;
path = tracepoint_id_to_path(config);
if (path) {
snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name);
free(path->name);
free(path->system);
free(path);
} else
snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown");
return buf;
} }
static int is_cache_op_valid(u8 cache_type, u8 cache_op) static int is_cache_op_valid(u8 cache_type, u8 cache_op)
...@@ -235,7 +265,7 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) ...@@ -235,7 +265,7 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result)
return name; return name;
} }
char *event_name(int counter) const char *event_name(int counter)
{ {
u64 config = attrs[counter].config; u64 config = attrs[counter].config;
int type = attrs[counter].type; int type = attrs[counter].type;
...@@ -243,7 +273,7 @@ char *event_name(int counter) ...@@ -243,7 +273,7 @@ char *event_name(int counter)
return __event_name(type, config); return __event_name(type, config);
} }
char *__event_name(int type, u64 config) const char *__event_name(int type, u64 config)
{ {
static char buf[32]; static char buf[32];
...@@ -294,7 +324,7 @@ char *__event_name(int type, u64 config) ...@@ -294,7 +324,7 @@ char *__event_name(int type, u64 config)
return "unknown"; return "unknown";
} }
static int parse_aliases(const char **str, char *names[][MAX_ALIASES], int size) static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
{ {
int i, j; int i, j;
int n, longest = -1; int n, longest = -1;
...@@ -598,7 +628,7 @@ static void print_tracepoint_events(void) ...@@ -598,7 +628,7 @@ static void print_tracepoint_events(void)
{ {
DIR *sys_dir, *evt_dir; DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
struct stat st; int sys_dir_fd;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
if (valid_debugfs_mount(debugfs_path)) if (valid_debugfs_mount(debugfs_path))
...@@ -607,13 +637,20 @@ static void print_tracepoint_events(void) ...@@ -607,13 +637,20 @@ static void print_tracepoint_events(void)
sys_dir = opendir(debugfs_path); sys_dir = opendir(debugfs_path);
if (!sys_dir) if (!sys_dir)
goto cleanup; goto cleanup;
sys_dir_fd = dirfd(sys_dir);
for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { for_each_subsystem(sys_dir, sys_dirent, sys_next) {
evt_dir = opendir(evt_path); int dfd = openat(sys_dir_fd, sys_dirent.d_name,
if (!evt_dir) O_RDONLY|O_DIRECTORY), evt_dir_fd;
goto cleanup; if (dfd == -1)
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, continue;
evt_path, st) { evt_dir = fdopendir(dfd);
if (!evt_dir) {
close(dfd);
continue;
}
evt_dir_fd = dirfd(evt_dir);
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) {
snprintf(evt_path, MAXPATHLEN, "%s:%s", snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name); sys_dirent.d_name, evt_dirent.d_name);
fprintf(stderr, " %-40s [%s]\n", evt_path, fprintf(stderr, " %-40s [%s]\n", evt_path,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment