diff options
45 files changed, 2157 insertions, 1155 deletions
diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h index eb17da78112..2a5da069714 100644 --- a/arch/powerpc/include/asm/pgtable.h +++ b/arch/powerpc/include/asm/pgtable.h @@ -104,8 +104,8 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, else pte_update(ptep, ~_PAGE_HASHPTE, pte_val(pte)); -#elif defined(CONFIG_PPC32) && defined(CONFIG_PTE_64BIT) && defined(CONFIG_SMP) - /* Second case is 32-bit with 64-bit PTE in SMP mode. In this case, we +#elif defined(CONFIG_PPC32) && defined(CONFIG_PTE_64BIT) + /* 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 * 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, @@ -140,7 +140,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, #else /* 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; #endif diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index b73396b9390..9619285f64e 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -97,7 +97,7 @@ obj64-$(CONFIG_AUDIT) += compat_audit.o obj-$(CONFIG_DYNAMIC_FTRACE) += 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 \ power5+-pmu.o power6-pmu.o power7-pmu.o obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 561b6465231..197b15646ee 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -67,6 +67,8 @@ int main(void) DEFINE(MMCONTEXTID, offsetof(struct mm_struct, context.id)); #ifdef CONFIG_PPC64 DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context)); + DEFINE(SIGSEGV, SIGSEGV); + DEFINE(NMI_MASK, NMI_MASK); #else DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); #endif /* CONFIG_PPC64 */ diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index eb898112e57..8ac85e08ffa 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -729,6 +729,11 @@ BEGIN_FTR_SECTION bne- do_ste_alloc /* If so handle it */ 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 * hard-enable interrupts so that the hash_page code can spin on @@ -833,6 +838,20 @@ handle_page_fault: bl .low_hash_fault 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 */ do_ste_alloc: bl .ste_allocate /* try to insert stab entry */ diff --git a/arch/powerpc/kernel/perf_callchain.c b/arch/powerpc/kernel/perf_callchain.c new file mode 100644 index 00000000000..f74b62c6751 --- /dev/null +++ b/arch/powerpc/kernel/perf_callchain.c @@ -0,0 +1,527 @@ +/* + * Performance counter callchain support - powerpc architecture code + * + * Copyright © 2009 Paul Mackerras, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/perf_counter.h> +#include <linux/percpu.h> +#include <linux/uaccess.h> +#include <linux/mm.h> +#include <asm/ptrace.h> +#include <asm/pgtable.h> +#include <asm/sigcontext.h> +#include <asm/ucontext.h> +#include <asm/vdso.h> +#ifdef CONFIG_PPC64 +#include "ppc32.h" +#endif + +/* + * Store another value in a callchain_entry. + */ +static inline void callchain_store(struct perf_callchain_entry *entry, u64 ip) +{ + unsigned int nr = entry->nr; + + if (nr < PERF_MAX_STACK_DEPTH) { + entry->ip[nr] = ip; + entry->nr = nr + 1; + } +} + +/* + * Is sp valid as the address of the next kernel stack frame after prev_sp? + * The next frame may be in a different stack area but should not go + * back down in the same stack area. + */ +static int valid_next_sp(unsigned long sp, unsigned long prev_sp) +{ + if (sp & 0xf) + return 0; /* must be 16-byte aligned */ + if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) + return 0; + if (sp >= prev_sp + STACK_FRAME_OVERHEAD) + return 1; + /* + * sp could decrease when we jump off an interrupt stack + * back to the regular process stack. + */ + if ((sp & ~(THREAD_SIZE - 1)) != (prev_sp & ~(THREAD_SIZE - 1))) + return 1; + return 0; +} + +static void perf_callchain_kernel(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long sp, next_sp; + unsigned long next_ip; + unsigned long lr; + long level = 0; + unsigned long *fp; + + lr = regs->link; + sp = regs->gpr[1]; + callchain_store(entry, PERF_CONTEXT_KERNEL); + callchain_store(entry, regs->nip); + + if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) + return; + + for (;;) { + fp = (unsigned long *) sp; + next_sp = fp[0]; + + if (next_sp == sp + STACK_INT_FRAME_SIZE && + fp[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) { + /* + * This looks like an interrupt frame for an + * interrupt that occurred in the kernel + */ + regs = (struct pt_regs *)(sp + STACK_FRAME_OVERHEAD); + next_ip = regs->nip; + lr = regs->link; + level = 0; + callchain_store(entry, PERF_CONTEXT_KERNEL); + + } else { + if (level == 0) + next_ip = lr; + else + next_ip = fp[STACK_FRAME_LR_SAVE]; + + /* + * We can't tell which of the first two addresses + * we get are valid, but we can filter out the + * obviously bogus ones here. We replace them + * with 0 rather than removing them entirely so + * that userspace can tell which is which. + */ + if ((level == 1 && next_ip == lr) || + (level <= 1 && !kernel_text_address(next_ip))) + next_ip = 0; + + ++level; + } + + callchain_store(entry, next_ip); + if (!valid_next_sp(next_sp, sp)) + return; + sp = next_sp; + } +} + +#ifdef CONFIG_PPC64 + +#ifdef CONFIG_HUGETLB_PAGE +#define is_huge_psize(pagesize) (HPAGE_SHIFT && mmu_huge_psizes[pagesize]) +#else +#define is_huge_psize(pagesize) 0 +#endif + +/* + * On 64-bit we don't want to invoke hash_page on user addresses from + * interrupt context, so if the access faults, we read the page tables + * to find which page (if any) is mapped and access it directly. + */ +static int read_user_stack_slow(void __user *ptr, void *ret, int nb) +{ + pgd_t *pgdir; + pte_t *ptep, pte; + int pagesize; + unsigned long addr = (unsigned long) ptr; + unsigned long offset; + unsigned long pfn; + void *kaddr; + + pgdir = current->mm->pgd; + if (!pgdir) + return -EFAULT; + + pagesize = get_slice_psize(current->mm, addr); + + /* align address to page boundary */ + offset = addr & ((1ul << mmu_psize_defs[pagesize].shift) - 1); + addr -= offset; + + if (is_huge_psize(pagesize)) + ptep = huge_pte_offset(current->mm, addr); + else + ptep = find_linux_pte(pgdir, addr); + + if (ptep == NULL) + return -EFAULT; + pte = *ptep; + if (!pte_present(pte) || !(pte_val(pte) & _PAGE_USER)) + return -EFAULT; + pfn = pte_pfn(pte); + if (!page_is_ram(pfn)) + return -EFAULT; + + /* no highmem to worry about here */ + kaddr = pfn_to_kaddr(pfn); + memcpy(ret, kaddr + offset, nb); + return 0; +} + +static int read_user_stack_64(unsigned long __user *ptr, unsigned long *ret) +{ + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned long) || + ((unsigned long)ptr & 7)) + return -EFAULT; + + if (!__get_user_inatomic(*ret, ptr)) + return 0; + + return read_user_stack_slow(ptr, ret, 8); +} + +static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) +{ + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || + ((unsigned long)ptr & 3)) + return -EFAULT; + + if (!__get_user_inatomic(*ret, ptr)) + return 0; + + return read_user_stack_slow(ptr, ret, 4); +} + +static inline int valid_user_sp(unsigned long sp, int is_64) +{ + if (!sp || (sp & 7) || sp > (is_64 ? TASK_SIZE : 0x100000000UL) - 32) + return 0; + return 1; +} + +/* + * 64-bit user processes use the same stack frame for RT and non-RT signals. + */ +struct signal_frame_64 { + char dummy[__SIGNAL_FRAMESIZE]; + struct ucontext uc; + unsigned long unused[2]; + unsigned int tramp[6]; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + char abigap[288]; +}; + +static int is_sigreturn_64_address(unsigned long nip, unsigned long fp) +{ + if (nip == fp + offsetof(struct signal_frame_64, tramp)) + return 1; + if (vdso64_rt_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso64_rt_sigtramp) + return 1; + return 0; +} + +/* + * Do some sanity checking on the signal frame pointed to by sp. + * We check the pinfo and puc pointers in the frame. + */ +static int sane_signal_64_frame(unsigned long sp) +{ + struct signal_frame_64 __user *sf; + unsigned long pinfo, puc; + + sf = (struct signal_frame_64 __user *) sp; + if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) || + read_user_stack_64((unsigned long __user *) &sf->puc, &puc)) + return 0; + return pinfo == (unsigned long) &sf->info && + puc == (unsigned long) &sf->uc; +} + +static void perf_callchain_user_64(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long sp, next_sp; + unsigned long next_ip; + unsigned long lr; + long level = 0; + struct signal_frame_64 __user *sigframe; + unsigned long __user *fp, *uregs; + + next_ip = regs->nip; + lr = regs->link; + sp = regs->gpr[1]; + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, next_ip); + + for (;;) { + fp = (unsigned long __user *) sp; + if (!valid_user_sp(sp, 1) || read_user_stack_64(fp, &next_sp)) + return; + if (level > 0 && read_user_stack_64(&fp[2], &next_ip)) + return; + + /* + * Note: the next_sp - sp >= signal frame size check + * is true when next_sp < sp, which can happen when + * transitioning from an alternate signal stack to the + * normal stack. + */ + if (next_sp - sp >= sizeof(struct signal_frame_64) && + (is_sigreturn_64_address(next_ip, sp) || + (level <= 1 && is_sigreturn_64_address(lr, sp))) && + sane_signal_64_frame(sp)) { + /* + * This looks like an signal frame + */ + sigframe = (struct signal_frame_64 __user *) sp; + uregs = sigframe->uc.uc_mcontext.gp_regs; + if (read_user_stack_64(&uregs[PT_NIP], &next_ip) || + read_user_stack_64(&uregs[PT_LNK], &lr) || + read_user_stack_64(&uregs[PT_R1], &sp)) + return; + level = 0; + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, next_ip); + continue; + } + + if (level == 0) + next_ip = lr; + callchain_store(entry, next_ip); + ++level; + sp = next_sp; + } +} + +static inline int current_is_64bit(void) +{ + /* + * We can't use test_thread_flag() here because we may be on an + * interrupt stack, and the thread flags don't get copied over + * from the thread_info on the main stack to the interrupt stack. + */ + return !test_ti_thread_flag(task_thread_info(current), TIF_32BIT); +} + +#else /* CONFIG_PPC64 */ +/* + * On 32-bit we just access the address and let hash_page create a + * HPTE if necessary, so there is no need to fall back to reading + * the page tables. Since this is called at interrupt level, + * do_page_fault() won't treat a DSI as a page fault. + */ +static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) +{ + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || + ((unsigned long)ptr & 3)) + return -EFAULT; + + return __get_user_inatomic(*ret, ptr); +} + +static inline void perf_callchain_user_64(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ +} + +static inline int current_is_64bit(void) +{ + return 0; +} + +static inline int valid_user_sp(unsigned long sp, int is_64) +{ + if (!sp || (sp & 7) || sp > TASK_SIZE - 32) + return 0; + return 1; +} + +#define __SIGNAL_FRAMESIZE32 __SIGNAL_FRAMESIZE +#define sigcontext32 sigcontext +#define mcontext32 mcontext +#define ucontext32 ucontext +#define compat_siginfo_t struct siginfo + +#endif /* CONFIG_PPC64 */ + +/* + * Layout for non-RT signal frames + */ +struct signal_frame_32 { + char dummy[__SIGNAL_FRAMESIZE32]; + struct sigcontext32 sctx; + struct mcontext32 mctx; + int abigap[56]; +}; + +/* + * Layout for RT signal frames + */ +struct rt_signal_frame_32 { + char dummy[__SIGNAL_FRAMESIZE32 + 16]; + compat_siginfo_t info; + struct ucontext32 uc; + int abigap[56]; +}; + +static int is_sigreturn_32_address(unsigned int nip, unsigned int fp) +{ + if (nip == fp + offsetof(struct signal_frame_32, mctx.mc_pad)) + return 1; + if (vdso32_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso32_sigtramp) + return 1; + return 0; +} + +static int is_rt_sigreturn_32_address(unsigned int nip, unsigned int fp) +{ + if (nip == fp + offsetof(struct rt_signal_frame_32, + uc.uc_mcontext.mc_pad)) + return 1; + if (vdso32_rt_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso32_rt_sigtramp) + return 1; + return 0; +} + +static int sane_signal_32_frame(unsigned int sp) +{ + struct signal_frame_32 __user *sf; + unsigned int regs; + + sf = (struct signal_frame_32 __user *) (unsigned long) sp; + if (read_user_stack_32((unsigned int __user *) &sf->sctx.regs, ®s)) + return 0; + return regs == (unsigned long) &sf->mctx; +} + +static int sane_rt_signal_32_frame(unsigned int sp) +{ + struct rt_signal_frame_32 __user *sf; + unsigned int regs; + + sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; + if (read_user_stack_32((unsigned int __user *) &sf->uc.uc_regs, ®s)) + return 0; + return regs == (unsigned long) &sf->uc.uc_mcontext; +} + +static unsigned int __user *signal_frame_32_regs(unsigned int sp, + unsigned int next_sp, unsigned int next_ip) +{ + struct mcontext32 __user *mctx = NULL; + struct signal_frame_32 __user *sf; + struct rt_signal_frame_32 __user *rt_sf; + + /* + * Note: the next_sp - sp >= signal frame size check + * is true when next_sp < sp, for example, when + * transitioning from an alternate signal stack to the + * normal stack. + */ + if (next_sp - sp >= sizeof(struct signal_frame_32) && + is_sigreturn_32_address(next_ip, sp) && + sane_signal_32_frame(sp)) { + sf = (struct signal_frame_32 __user *) (unsigned long) sp; + mctx = &sf->mctx; + } + + if (!mctx && next_sp - sp >= sizeof(struct rt_signal_frame_32) && + is_rt_sigreturn_32_address(next_ip, sp) && + sane_rt_signal_32_frame(sp)) { + rt_sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; + mctx = &rt_sf->uc.uc_mcontext; + } + + if (!mctx) + return NULL; + return mctx->mc_gregs; +} + +static void perf_callchain_user_32(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned int sp, next_sp; + unsigned int next_ip; + unsigned int lr; + long level = 0; + unsigned int __user *fp, *uregs; + + next_ip = regs->nip; + lr = regs->link; + sp = regs->gpr[1]; + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, next_ip); + + while (entry->nr < PERF_MAX_STACK_DEPTH) { + fp = (unsigned int __user *) (unsigned long) sp; + if (!valid_user_sp(sp, 0) || read_user_stack_32(fp, &next_sp)) + return; + if (level > 0 && read_user_stack_32(&fp[1], &next_ip)) + return; + + uregs = signal_frame_32_regs(sp, next_sp, next_ip); + if (!uregs && level <= 1) + uregs = signal_frame_32_regs(sp, next_sp, lr); + if (uregs) { + /* + * This looks like an signal frame, so restart + * the stack trace with the values in it. + */ + if (read_user_stack_32(&uregs[PT_NIP], &next_ip) || + read_user_stack_32(&uregs[PT_LNK], &lr) || + read_user_stack_32(&uregs[PT_R1], &sp)) + return; + level = 0; + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, next_ip); + continue; + } + + if (level == 0) + next_ip = lr; + callchain_store(entry, next_ip); + ++level; + sp = next_sp; + } +} + +/* + * Since we can't get PMU interrupts inside a PMU interrupt handler, + * we don't need separate irq and nmi entries here. + */ +static DEFINE_PER_CPU(struct perf_callchain_entry, callchain); + +struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +{ + struct perf_callchain_entry *entry = &__get_cpu_var(callchain); + + entry->nr = 0; + + if (current->pid == 0) /* idle task? */ + return entry; + + if (!user_mode(regs)) { + perf_callchain_kernel(regs, entry); + if (current->mm) + regs = task_pt_regs(current); + else + regs = NULL; + } + + if (regs) { + if (current_is_64bit()) + perf_callchain_user_64(regs, entry); + else + perf_callchain_user_32(regs, entry); + } + + return entry; +} diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index 5b7038f248b..a685652effe 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c @@ -92,15 +92,13 @@ static inline void create_shadowed_slbe(unsigned long ea, int ssize, : "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 * appropriately too. */ unsigned long linear_llp, vmalloc_llp, lflags, vflags; unsigned long ksp_esid_data, ksp_vsid_data; - WARN_ON(!irqs_disabled()); - linear_llp = mmu_psize_defs[mmu_linear_psize].sllp; vmalloc_llp = mmu_psize_defs[mmu_vmalloc_psize].sllp; lflags = SLB_VSID_KERNEL | linear_llp; @@ -117,12 +115,6 @@ void slb_flush_and_rebolt(void) 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 * the stack between the slbia and rebolting it. */ asm volatile("isync\n" @@ -139,6 +131,21 @@ void slb_flush_and_rebolt(void) : "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) { unsigned long vflags; @@ -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. */ 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 pc = KSTK_EIP(tsk); unsigned long stack = KSTK_ESP(tsk); 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) && offset <= SLB_CACHE_ENTRIES) { int i; @@ -200,7 +215,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) } asm volatile("isync" : : : "memory"); } else { - slb_flush_and_rebolt(); + __slb_flush_and_rebolt(); } /* Workaround POWER5 < DD2.1 issue */ diff --git a/arch/powerpc/mm/stab.c b/arch/powerpc/mm/stab.c index 98cd1dc2ae7..ab5fb48b3e9 100644 --- a/arch/powerpc/mm/stab.c +++ b/arch/powerpc/mm/stab.c @@ -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 *ste; - unsigned long offset = __get_cpu_var(stab_cache_ptr); + unsigned long offset; unsigned long pc = KSTK_EIP(tsk); unsigned long stack = KSTK_ESP(tsk); unsigned long unmapped_base; @@ -172,6 +172,15 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm) /* Force previous translations to complete. DRENG */ 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) { int i; diff --git a/arch/x86/include/asm/perf_counter.h b/arch/x86/include/asm/perf_counter.h index fa64e401589..e7b7c938ae2 100644 --- a/arch/x86/include/asm/perf_counter.h +++ b/arch/x86/include/asm/perf_counter.h @@ -84,6 +84,16 @@ union cpuid10_edx { #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b #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 extern void init_hw_perf_counters(void); extern void perf_counters_lapic_init(void); diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 900332b800f..396e35db705 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c @@ -6,6 +6,7 @@ * Copyright (C) 2009 Jaswinder Singh Rajput * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> + * Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com> * * For licencing details see kernel-base/COPYING */ @@ -20,6 +21,7 @@ #include <linux/sched.h> #include <linux/uaccess.h> #include <linux/highmem.h> +#include <linux/cpu.h> #include <asm/apic.h> #include <asm/stacktrace.h> @@ -27,12 +29,52 @@ static u64 perf_counter_mask __read_mostly; +/* The maximal number of PEBS counters: */ +#define MAX_PEBS_COUNTERS 4 + +/* The size of a BTS record in bytes: */ +#define BTS_RECORD_SIZE 24 + +/* The size of a per-cpu BTS buffer in bytes: */ +#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 1024) + +/* The BTS overflow threshold in bytes from the end of the buffer: */ +#define BTS_OVFL_TH (BTS_RECORD_SIZE * 64) + + +/* + * Bits in the debugctlmsr controlling branch tracing. + */ +#define X86_DEBUGCTL_TR (1 << 6) +#define X86_DEBUGCTL_BTS (1 << 7) +#define X86_DEBUGCTL_BTINT (1 << 8) +#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) +#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) + +/* + * A debug store configuration. + * + * We only support architectures that use 64bit fields. + */ +struct debug_store { + u64 bts_buffer_base; + u64 bts_index; + u64 bts_absolute_maximum; + u64 bts_interrupt_threshold; + u64 pebs_buffer_base; + u64 pebs_index; + u64 pebs_absolute_maximum; + u64 pebs_interrupt_threshold; + u64 pebs_counter_reset[MAX_PEBS_COUNTERS]; +}; + struct cpu_hw_counters { struct perf_counter *counters[X86_PMC_IDX_MAX]; unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; unsigned long interrupts; int enabled; + struct debug_store *ds; }; /* @@ -58,6 +100,8 @@ struct x86_pmu { int apic; u64 max_period; u64 intel_ctrl; + void (*enable_bts)(u64 config); + void (*disable_bts)(void); }; static struct x86_pmu x86_pmu __read_mostly; @@ -577,6 +621,9 @@ x86_perf_counter_update(struct perf_counter *counter, u64 prev_raw_count, new_raw_count; s64 delta; + if (idx == X86_PMC_IDX_FIXED_BTS) + return 0; + /* * Careful: an NMI might modify the previous counter value. * @@ -666,10 +713,109 @@ static void release_pmc_hardware(void) #endif } +static inline bool bts_available(void) +{ + return x86_pmu.enable_bts != NULL; +} + +static inline void init_debug_store_on_cpu(int cpu) +{ + struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; + + if (!ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, + (u32)((u64)(long)ds), (u32)((u64)(long)ds >> 32)); +} + +static inline void fini_debug_store_on_cpu(int cpu) +{ + if (!per_cpu(cpu_hw_counters, cpu).ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); +} + +static void release_bts_hardware(void) +{ + int cpu; + + if (!bts_available()) + return; + + get_online_cpus(); + + for_each_online_cpu(cpu) + fini_debug_store_on_cpu(cpu); + + for_each_possible_cpu(cpu) { + struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; + + if (!ds) + continue; + + per_cpu(cpu_hw_counters, cpu).ds = NULL; + + kfree((void *)(long)ds->bts_buffer_base); + kfree(ds); + } + + put_online_cpus(); +} + +static int reserve_bts_hardware(void) +{ + int cpu, err = 0; + + if (!bts_available()) + return -EOPNOTSUPP; + + get_online_cpus(); + + for_each_possible_cpu(cpu) { + struct debug_store *ds; + void *buffer; + + err = -ENOMEM; + buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); + if (unlikely(!buffer)) + break; + + ds = kzalloc(sizeof(*ds), GFP_KERNEL); + if (unlikely(!ds)) { + kfree(buffer); + break; + } + + ds->bts_buffer_base = (u64)(long)buffer; + ds->bts_index = ds->bts_buffer_base; + ds->bts_absolute_maximum = + ds->bts_buffer_base + BTS_BUFFER_SIZE; + ds->bts_interrupt_threshold = + ds->bts_absolute_maximum - BTS_OVFL_TH; + + per_cpu(cpu_hw_counters, cpu).ds = ds; + err = 0; + } + + if (err) + release_bts_hardware(); + else { + for_each_online_cpu(cpu) + init_debug_store_on_cpu(cpu); + } + + put_online_cpus(); + + return err; +} + static void hw_perf_counter_destroy(struct perf_counter *counter) { if (atomic_dec_and_mutex_lock(&active_counters, &pmc_reserve_mutex)) { release_pmc_hardware(); + release_bts_hardware(); mutex_unlock(&pmc_reserve_mutex); } } @@ -712,6 +858,42 @@ set_ext_hw_attr(struct hw_perf_counter *hwc, struct perf_counter_attr *attr) return 0; } +static void intel_pmu_enable_bts(u64 config) +{ + unsigned long debugctlmsr; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr |= X86_DEBUGCTL_TR; + debugctlmsr |= X86_DEBUGCTL_BTS; + debugctlmsr |= X86_DEBUGCTL_BTINT; + + if (!(config & ARCH_PERFMON_EVENTSEL_OS)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; + + if (!(config & ARCH_PERFMON_EVENTSEL_USR)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; + + update_debugctlmsr(debugctlmsr); +} + +static void intel_pmu_disable_bts(void) +{ + struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); + unsigned long debugctlmsr; + + if (!cpuc->ds) + return; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr &= + ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | + X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); + + update_debugctlmsr(debugctlmsr); +} + /* * Setup the hardware configuration for a given attr_type */ @@ -728,9 +910,13 @@ static int __hw_perf_counter_init(struct perf_counter *counter) err = 0; if (!atomic_inc_not_zero(&active_counters)) { mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&active_counters) == 0 && !reserve_pmc_hardware()) - err = -EBUSY; - else + if (atomic_read(&active_counters) == 0) { + if (!reserve_pmc_hardware()) + err = -EBUSY; + else + reserve_bts_hardware(); + } + if (!err) atomic_inc(&active_counters); mutex_unlock(&pmc_reserve_mutex); } @@ -817,7 +1003,18 @@ static void p6_pmu_disable_all(void) static void intel_pmu_disable_all(void) { + struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); + + if (!cpuc->enabled) + return; + + cpuc->enabled = 0; + barrier(); + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); + + if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) + intel_pmu_disable_bts(); } static void amd_pmu_disable_all(void) @@ -875,7 +1072,25 @@ static void p6_pmu_enable_all(void) static void intel_pmu_enable_all(void) { + struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); + + if (cpuc->enabled) + return; + + cpuc->enabled = 1; + barrier(); + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); + + if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { + struct perf_counter *counter = + cpuc->counters[X86_PMC_IDX_FIXED_BTS]; + + if (WARN_ON_ONCE(!counter)) + return; + + intel_pmu_enable_bts(counter->hw.config); + } } static void amd_pmu_enable_all(void) @@ -962,6 +1177,11 @@ p6_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) static inline void intel_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) { + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { + intel_pmu_disable_bts(); + return; + } + if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { intel_pmu_disable_fixed(hwc, idx); return; @@ -990,6 +1210,9 @@ x86_perf_counter_set_period(struct perf_counter *counter, s64 period = hwc->sample_period; int err, ret = 0; + if (idx == X86_PMC_IDX_FIXED_BTS) + return 0; + /* * If we are way outside a reasoable range then just skip forward: */ @@ -1072,6 +1295,14 @@ static void p6_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) { + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { + if (!__get_cpu_var(cpu_hw_counters).enabled) + return; + + intel_pmu_enable_bts(hwc->config); + return; + } + if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { intel_pmu_enable_fixed(hwc, idx); return; @@ -1093,11 +1324,16 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc) { unsigned int event; + event = hwc->config & ARCH_PERFMON_EVENT_MASK; + + if (unlikely((event == + x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && + (hwc->sample_period == 1))) + return X86_PMC_IDX_FIXED_BTS; + if (!x86_pmu.num_counters_fixed) return -1; - event = hwc->config & ARCH_PERFMON_EVENT_MASK; - if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) return X86_PMC_IDX_FIXED_INSTRUCTIONS; if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_CPU_CYCLES))) @@ -1118,7 +1354,25 @@ static int x86_pmu_enable(struct perf_counter *counter) int idx; idx = fixed_mode_idx(counter, hwc); - if (idx >= 0) { + if (idx == X86_PMC_IDX_FIXED_BTS) { + /* + * Try to use BTS for branch tracing. If that is not + * available, try to get a generic counter. + */ + if (unlikely(!cpuc->ds)) + goto try_generic; + + /* + * Try to get the fixed counter, if that is already taken + * then try to get a generic counter: + */ + if (test_and_set_bit(idx, cpuc->used_mask)) + goto try_generic; + + hwc->config_base = 0; + hwc->counter_base = 0; + hwc->idx = idx; + } else if (idx >= 0) { /* * Try to get the fixed counter, if that is already taken * then try to get a generic counter: @@ -1229,6 +1483,45 @@ void perf_counter_print_debug(void) local_irq_restore(flags); } +static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc, + struct perf_sample_data *data) +{ + struct debug_store *ds = cpuc->ds; + struct bts_record { + u64 from; + u64 to; + u64 flags; + }; + struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS]; + unsigned long orig_ip = data->regs->ip; + u64 at; + + if (!counter) + return; + + if (!ds) + return; + + for (at = ds->bts_buffer_base; + at < ds->bts_index; + at += sizeof(struct bts_record)) { + struct bts_record *rec = (struct bts_record *)(long)at; + + data->regs->ip = rec->from; + data->addr = rec->to; + + perf_counter_output(counter, 1, data); + } + + ds->bts_index = ds->bts_buffer_base; + + data->regs->ip = orig_ip; + data->addr = 0; + + /* There's new data available. */ + counter->pending_kill = POLL_IN; +} + static void x86_pmu_disable(struct perf_counter *counter) { struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); @@ -1253,6 +1546,15 @@ static void x86_pmu_disable(struct perf_counter *counter) * that we are disabling: */ x86_perf_counter_update(counter, hwc, idx); + + /* Drain the remaining BTS records. */ + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { + struct perf_sample_data data; + struct pt_regs regs; + + data.regs = ®s; + intel_pmu_drain_bts_buffer(cpuc, &data); + } cpuc->counters[idx] = NULL; clear_bit(idx, cpuc->used_mask); @@ -1280,6 +1582,7 @@ static int intel_pmu_save_and_restart(struct perf_counter *counter) static void intel_pmu_reset(void) { + struct debug_store *ds = __get_cpu_var(cpu_hw_counters).ds; unsigned long flags; int idx; @@ -1297,6 +1600,8 @@ static void intel_pmu_reset(void) for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); } + if (ds) + ds->bts_index = ds->bts_buffer_base; local_irq_restore(flags); } @@ -1362,6 +1667,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) cpuc = &__get_cpu_var(cpu_hw_counters); perf_disable(); + intel_pmu_drain_bts_buffer(cpuc, &data); status = intel_pmu_get_status(); if (!status) { perf_enable(); @@ -1571,6 +1877,8 @@ static struct x86_pmu intel_pmu = { * the generic counter period: */ .max_period = (1ULL << 31) - 1, + .enable_bts = intel_pmu_enable_bts, + .disable_bts = intel_pmu_disable_bts, }; static struct x86_pmu amd_pmu = { @@ -1962,3 +2270,8 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) return entry; } + +void hw_perf_counter_setup_online(int cpu) +{ + init_debug_store_on_cpu(cpu); +} diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index e72e9311078..370344afb5b 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -27,6 +27,9 @@ OPTIONS -n --show-nr-samples Show the number of samples for each symbol +-T +--threads + Show per-thread event counters -C:: --comms=:: Only consider symbols in these comms. CSV that understands diff --git a/tools/perf/Makefile b/tools/perf/Makefile index c045b4271e5..5d54ddb83ab 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -166,7 +166,12 @@ endif # 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 -Wformat -Wformat-security -Wformat-y2k -Wshadow -Winit-self -Wpacked -Wredundant-decls -Wstack-protector -Wstrict-aliasing=3 -Wswitch-default -Wswitch-enum -Wno-system-headers -Wundef -Wvolatile-register-var -Wwrite-strings -Wbad-function-cast -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wstrict-prototypes -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 ALL_CFLAGS = $(CFLAGS) ALL_LDFLAGS = $(LDFLAGS) @@ -310,6 +315,7 @@ LIB_H += util/sigchain.h LIB_H += util/symbol.h LIB_H += util/module.h LIB_H += util/color.h +LIB_H += util/values.h LIB_OBJS += util/abspath.o LIB_OBJS += util/alias.o @@ -337,6 +343,10 @@ LIB_OBJS += util/color.o LIB_OBJS += util/pager.o LIB_OBJS += util/header.o LIB_OBJS += util/callchain.o +LIB_OBJS += util/values.o +LIB_OBJS += util/debug.o +LIB_OBJS += util/map.o +LIB_OBJS += util/thread.o BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-help.o diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5e17de984dc..4ac618b3425 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -17,16 +17,13 @@ #include "util/string.h" #include "perf.h" +#include "util/debug.h" #include "util/parse-options.h" #include "util/parse-events.h" - -#define SHOW_KERNEL 1 -#define SHOW_USER 2 -#define SHOW_HV 4 +#include "util/thread.h" static char const *input_name = "perf.data"; -static char *vmlinux = "vmlinux"; static char default_sort_order[] = "comm,symbol"; static char *sort_order = default_sort_order; @@ -35,13 +32,6 @@ static int force; static int input; static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; -static int dump_trace = 0; -#define dprintf(x...) do { if (dump_trace) printf(x); } while (0) - -static int verbose; - -static int modules; - static int full_paths; static int print_line; @@ -49,39 +39,8 @@ static int print_line; static unsigned long page_size; static unsigned long mmap_window = 32; -struct ip_event { - struct perf_event_header header; - u64 ip; - u32 pid, tid; -}; - -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; -}; - -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; -} event_t; +static struct rb_root threads; +static struct thread *last_match; struct sym_ext { @@ -90,323 +49,6 @@ struct sym_ext { char *path; }; -static LIST_HEAD(dsos); -static struct dso *kernel_dso; -static struct dso *vdso; - - -static void dsos__add(struct dso *dso) -{ - list_add_tail(&dso->node, &dsos); -} - -static struct dso *dsos__find(const char *name) -{ - struct dso *pos; - - list_for_each_entry(pos, &dsos, node) - if (strcmp(pos->name, name) == 0) - return pos; - return NULL; -} - -static struct dso *dsos__findnew(const char *name) -{ - struct dso *dso = dsos__find(name); - int nr; - - if (dso) - return dso; - - dso = dso__new(name, 0); - if (!dso) - goto out_delete_dso; - - nr = dso__load(dso, NULL, verbose); - if (nr < 0) { - if (verbose) - fprintf(stderr, "Failed to open: %s\n", name); - goto out_delete_dso; - } - if (!nr && verbose) { - fprintf(stderr, - "No symbols found in: %s, maybe install a debug package?\n", - name); - } - - dsos__add(dso); - - return dso; - -out_delete_dso: - dso__delete(dso); - return NULL; -} - -static void dsos__fprintf(FILE *fp) -{ - struct dso *pos; - - list_for_each_entry(pos, &dsos, node) - dso__fprintf(pos, fp); -} - -static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) -{ - return dso__find_symbol(dso, ip); -} - -static int load_kernel(void) -{ - int err; - - kernel_dso = dso__new("[kernel]", 0); - if (!kernel_dso) - return -1; - - err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); - if (err <= 0) { - dso__delete(kernel_dso); - kernel_dso = NULL; - } else - dsos__add(kernel_dso); - - vdso = dso__new("[vdso]", 0); - if (!vdso) - return -1; - - vdso->find_symbol = vdso__find_symbol; - - dsos__add(vdso); - - return err; -} - -struct map { - struct list_head node; - u64 start; - u64 end; - u64 pgoff; - u64 (*map_ip)(struct map *, u64); - struct dso *dso; -}; - -static u64 map__map_ip(struct map *map, u64 ip) -{ - return ip - map->start + map->pgoff; -} - -static u64 vdso__map_ip(struct map *map __used, u64 ip) -{ - return ip; -} - -static struct map *map__new(struct mmap_event *event) -{ - struct map *self = malloc(sizeof(*self)); - - if (self != NULL) { - const char *filename = event->filename; - - 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) - self->map_ip = vdso__map_ip; - else - self->map_ip = map__map_ip; - } - return self; -out_delete: - free(self); - return NULL; -} - -static struct map *map__clone(struct map *self) -{ - struct map *map = malloc(sizeof(*self)); - - if (!map) - return NULL; - - memcpy(map, self, sizeof(*self)); - - return map; -} - -static 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; -} - -static 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); -} - - -struct thread { - struct rb_node rb_node; - struct list_head maps; - pid_t pid; - char *comm; -}; - -static struct thread *thread__new(pid_t pid) -{ - struct thread *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->pid = pid; - self->comm = malloc(32); - if (self->comm) - snprintf(self->comm, 32, ":%d", self->pid); - INIT_LIST_HEAD(&self->maps); - } - - return self; -} - -static int thread__set_comm(struct thread *self, const char *comm) -{ - if (self->comm) - free(self->comm); - self->comm = strdup(comm); - return self->comm ? 0 : -ENOMEM; -} - -static size_t thread__fprintf(struct thread *self, FILE *fp) -{ - struct map *pos; - size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); - - list_for_each_entry(pos, &self->maps, node) - ret += map__fprintf(pos, fp); - - return ret; -} - - -static struct rb_root threads; -static struct thread *last_match; - -static struct thread *threads__findnew(pid_t pid) -{ - struct rb_node **p = &threads.rb_node; - struct rb_node *parent = NULL; - struct thread *th; - - /* - * Font-end cache - PID lookups come in blocks, - * so most of the time we dont have to look up - * the full rbtree: - */ - if (last_match && last_match->pid == pid) - return last_match; - - while (*p != NULL) { - parent = *p; - th = rb_entry(parent, struct thread, rb_node); - - if (th->pid == pid) { - last_match = th; - return th; - } - - if (pid < th->pid) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - th = thread__new(pid); - if (th != NULL) { - rb_link_node(&th->rb_node, parent, p); - rb_insert_color(&th->rb_node, &threads); - last_match = th; - } - - return th; -} - -static void thread__insert_map(struct thread *self, struct map *map) -{ - struct map *pos, *tmp; - - list_for_each_entry_safe(pos, tmp, &self->maps, node) { - if (map__overlap(pos, map)) { - list_del_init(&pos->node); - /* XXX leaks dsos */ - free(pos); - } - } - - list_add_tail(&map->node, &self->maps); -} - -static int thread__fork(struct thread *self, struct thread *parent) -{ - struct map *map; - - if (self->comm) - free(self->comm); - self->comm = strdup(parent->comm); - if (!self->comm) - return -ENOMEM; - - list_for_each_entry(map, &parent->maps, node) { - struct map *new = map__clone(map); - if (!new) - return -ENOMEM; - thread__insert_map(self, new); - } - - return 0; -} - -static struct map *thread__find_map(struct thread *self, u64 ip) -{ - struct map *pos; - - if (self == NULL) - return NULL; - - list_for_each_entry(pos, &self->maps, node) - if (ip >= pos->start && ip <= pos->end) - return pos; - - return NULL; -} - -static size_t threads__fprintf(FILE *fp) -{ - size_t ret = 0; - struct rb_node *nd; - - for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { - struct thread *pos = rb_entry(nd, struct thread, rb_node); - - ret += thread__fprintf(pos, fp); - } - - return ret; -} - /* * histogram, sorted on item, collects counts */ @@ -433,7 +75,7 @@ struct hist_entry { struct sort_entry { struct list_head list; - char *header; + const char *header; int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*collapse)(struct hist_entry *, struct hist_entry *); @@ -577,7 +219,7 @@ static struct sort_entry sort_sym = { static int sort__need_collapse = 0; struct sort_dimension { - char *name; + const char *name; struct sort_entry *entry; int taken; }; @@ -832,7 +474,7 @@ static void output__resort(void) static void register_idle_thread(void) { - struct thread *thread = threads__findnew(0); + struct thread *thread = threads__findnew(0, &threads, &last_match); if (thread == NULL || thread__set_comm(thread, "[idle]")) { @@ -853,18 +495,20 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) char level; int show = 0; struct dso *dso = NULL; - struct thread *thread = threads__findnew(event->ip.pid); + struct thread *thread; u64 ip = event->ip.ip; struct map *map = NULL; - dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", + thread = threads__findnew(event->ip.pid, &threads, &last_match); + + dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.misc, event->ip.pid, (void *)(long)ip); - dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); if (thread == NULL) { fprintf(stderr, "problem processing %d event, skipping it.\n", @@ -878,7 +522,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dso = kernel_dso; - dprintf(" ...... dso: %s\n", dso->name); + dump_printf(" ...... dso: %s\n", dso->name); } else if (event->header.misc & PERF_EVENT_MISC_USER) { @@ -899,12 +543,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) if ((long long)ip < 0) dso = kernel_dso; } - dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); + dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); } else { show = SHOW_HV; level = 'H'; - dprintf(" ...... dso: [hypervisor]\n"); + dump_printf(" ...... dso: [hypervisor]\n"); } if (show & show_mask) { @@ -927,10 +571,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) static int process_mmap_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->mmap.pid); - struct map *map = map__new(&event->mmap); + struct thread *thread; + struct map *map = map__new(&event->mmap, NULL, 0); + + thread = threads__findnew(event->mmap.pid, &threads, &last_match); - dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", + dump_printf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", (void *)(offset + head), (void *)(long)(event->header.size), event->mmap.pid, @@ -940,7 +586,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) event->mmap.filename); if (thread == NULL || map == NULL) { - dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n"); + dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); return 0; } @@ -953,16 +599,17 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) static int process_comm_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->comm.pid); + struct thread *thread; - dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", + 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)) { - dprintf("problem processing PERF_EVENT_COMM, skipping event.\n"); + dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); return -1; } total_comm++; @@ -973,10 +620,12 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) static int process_fork_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->fork.pid); - struct thread *parent = threads__findnew(event->fork.ppid); + struct thread *thread; + struct thread *parent; - dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", + thread = threads__findnew(event->fork.pid, &threads, &last_match); + parent = threads__findnew(event->fork.ppid, &threads, &last_match); + dump_printf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->fork.pid, event->fork.ppid); @@ -989,7 +638,7 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) return 0; if (!thread || !parent || thread__fork(thread, parent)) { - dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); + dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); return -1; } total_fork++; @@ -1075,7 +724,7 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) const char *path = NULL; unsigned int hits = 0; double percent = 0.0; - char *color; + const char *color; struct sym_ext *sym_ext = sym->priv; offset = line_ip - start; @@ -1157,7 +806,7 @@ static void free_source_line(struct symbol *sym, int len) /* Get the filename:line for the colored entries */ static void -get_source_line(struct symbol *sym, u64 start, int len, char *filename) +get_source_line(struct symbol *sym, u64 start, int len, const char *filename) { int i; char cmd[PATH_MAX * 2]; @@ -1203,7 +852,7 @@ get_source_line(struct symbol *sym, u64 start, int len, char *filename) } } -static void print_summary(char *filename) +static void print_summary(const char *filename) { struct sym_ext *sym_ext; struct rb_node *node; @@ -1219,7 +868,7 @@ static void print_summary(char *filename) node = rb_first(&root_sym_ext); while (node) { double percent; - char *color; + const char *color; char *path; sym_ext = rb_entry(node, struct sym_ext, node); @@ -1234,7 +883,7 @@ static void print_summary(char *filename) static void annotate_sym(struct dso *dso, struct symbol *sym) { - char *filename = dso->name, *d_filename; + const char *filename = dso->name, *d_filename; u64 start, end, len; char command[PATH_MAX*2]; FILE *file; @@ -1244,7 +893,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) if (sym->module) filename = sym->module->path; else if (dso == kernel_dso) - filename = vmlinux; + filename = vmlinux_name; start = sym->obj_start; if (!start) @@ -1316,7 +965,7 @@ static int __cmd_annotate(void) int ret, rc = EXIT_FAILURE; unsigned long offset = 0; unsigned long head = 0; - struct stat stat; + struct stat input_stat; event_t *event; uint32_t size; char *buf; @@ -1329,18 +978,18 @@ static int __cmd_annotate(void) exit(-1); } - ret = fstat(input, &stat); + ret = fstat(input, &input_stat); if (ret < 0) { perror("failed to stat file"); exit(-1); } - if (!force && (stat.st_uid != geteuid())) { + if (!force && (input_stat.st_uid != geteuid())) { fprintf(stderr, "file: %s not owned by current user\n", input_name); exit(-1); } - if (!stat.st_size) { + if (!input_stat.st_size) { fprintf(stderr, "zero-sized file, nothing to do!\n"); exit(0); } @@ -1367,10 +1016,10 @@ more: if (head + event->header.size >= page_size * mmap_window) { unsigned long shift = page_size * (head / page_size); - int ret; + int munmap_ret; - ret = munmap(buf, page_size * mmap_window); - assert(ret == 0); + munmap_ret = munmap(buf, page_size * mmap_window); + assert(munmap_ret == 0); offset += shift; head -= shift; @@ -1379,14 +1028,14 @@ more: size = event->header.size; - dprintf("%p [%p]: event: %d\n", + dump_printf("%p [%p]: event: %d\n", (void *)(offset + head), (void *)(long)event->header.size, event->header.type); if (!size || process_event(event, offset, head) < 0) { - dprintf("%p [%p]: skipping unknown header type: %d\n", + dump_printf("%p [%p]: skipping unknown header type: %d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.type); @@ -1406,23 +1055,23 @@ more: head += size; - if (offset + head < (unsigned long)stat.st_size) + if (offset + head < (unsigned long)input_stat.st_size) goto more; rc = EXIT_SUCCESS; close(input); - dprintf(" IP events: %10ld\n", total); - dprintf(" mmap events: %10ld\n", total_mmap); - dprintf(" comm events: %10ld\n", total_comm); - dprintf(" fork events: %10ld\n", total_fork); - dprintf(" unknown events: %10ld\n", total_unknown); + dump_printf(" IP events: %10ld\n", total); + dump_printf(" mmap events: %10ld\n", total_mmap); + dump_printf(" comm events: %10ld\n", total_comm); + dump_printf(" fork events: %10ld\n", total_fork); + dump_printf(" unknown events: %10ld\n", total_unknown); if (dump_trace) return 0; if (verbose >= 3) - threads__fprintf(stdout); + threads__fprintf(stdout, &threads); if (verbose >= 2) dsos__fprintf(stdout); @@ -1450,7 +1099,7 @@ static const struct option options[] = { "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), - OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), + OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), OPT_BOOLEAN('m', "modules", &modules, "load module symbols - WARNING: use only with -k and LIVE kernel"), OPT_BOOLEAN('l', "print-line", &print_line, diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 2599d86a733..4fb8734a796 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -456,6 +456,7 @@ int cmd_help(int argc, const char **argv, const char *prefix __used) break; case HELP_FORMAT_WEB: show_html_page(argv[0]); + default: break; } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 89a5ddcd1de..6a5db675ee4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -15,6 +15,8 @@ #include "util/string.h" #include "util/header.h" +#include "util/event.h" +#include "util/debug.h" #include <unistd.h> #include <sched.h> @@ -42,7 +44,6 @@ static int inherit = 1; static int force = 0; static int append_file = 0; static int call_graph = 0; -static int verbose = 0; static int inherit_stat = 0; static int no_samples = 0; static int sample_address = 0; @@ -62,24 +63,6 @@ static int file_new = 1; 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 { int counter; void *base; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8b2ec882e6e..d2e28820ee6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -17,19 +17,18 @@ #include "util/string.h" #include "util/callchain.h" #include "util/strlist.h" +#include "util/values.h" #include "perf.h" +#include "util/debug.h" #include "util/header.h" #include "util/parse-options.h" #include "util/parse-events.h" -#define SHOW_KERNEL 1 -#define SHOW_USER 2 -#define SHOW_HV 4 +#include "util/thread.h" static char const *input_name = "perf.data"; -static char *vmlinux = NULL; static char default_sort_order[] = "comm,dso,symbol"; static char *sort_order = default_sort_order; @@ -42,18 +41,15 @@ static int force; static int input; static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; -static int dump_trace = 0; -#define dprintf(x...) do { if (dump_trace) printf(x); } while (0) -#define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) - -static int verbose; -#define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) - -static int modules; - static int full_paths; static int show_nr_samples; +static int show_threads; +static struct perf_read_values show_threads_values; + +static char default_pretty_printing_style[] = "normal"; +static char *pretty_printing_style = default_pretty_printing_style; + static unsigned long page_size; static unsigned long mmap_window = 32; @@ -67,6 +63,15 @@ static char callchain_default_opt[] = "fractal,0.5"; static int callchain; +static char __cwd[PATH_MAX]; +static char *cwd = __cwd; +static int cwdlen; + +static struct rb_root threads; +static struct thread *last_match; + +static struct perf_header *header; + static struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_REL, @@ -75,59 +80,6 @@ struct callchain_param callchain_param = { static u64 sample_type; -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; -}; - -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; - static int repsep_fprintf(FILE *fp, const char *fmt, ...) { int n; @@ -141,6 +93,7 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...) n = vasprintf(&bf, fmt, ap); if (n > 0) { char *sep = bf; + while (1) { sep = strchr(sep, *field_sep); if (sep == NULL) @@ -155,396 +108,10 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...) return n; } -static LIST_HEAD(dsos); -static struct dso *kernel_dso; -static struct dso *vdso; -static struct dso *hypervisor_dso; - -static void dsos__add(struct dso *dso) -{ - list_add_tail(&dso->node, &dsos); -} - -static struct dso *dsos__find(const char *name) -{ - struct dso *pos; - - list_for_each_entry(pos, &dsos, node) - if (strcmp(pos->name, name) == 0) - return pos; - return NULL; -} - -static struct dso *dsos__findnew(const char *name) -{ - struct dso *dso = dsos__find(name); - int nr; - - if (dso) - return dso; - - dso = dso__new(name, 0); - if (!dso) - goto out_delete_dso; - - nr = dso__load(dso, NULL, verbose); - if (nr < 0) { - eprintf("Failed to open: %s\n", name); - goto out_delete_dso; - } - if (!nr) - eprintf("No symbols found in: %s, maybe install a debug package?\n", name); - - dsos__add(dso); - - return dso; - -out_delete_dso: - dso__delete(dso); - return NULL; -} - -static void dsos__fprintf(FILE *fp) -{ - struct dso *pos; - - list_for_each_entry(pos, &dsos, node) - dso__fprintf(pos, fp); -} - -static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) -{ - return dso__find_symbol(dso, ip); -} - -static int load_kernel(void) -{ - int err; - - kernel_dso = dso__new("[kernel]", 0); - if (!kernel_dso) - return -1; - - err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); - if (err <= 0) { - dso__delete(kernel_dso); - kernel_dso = NULL; - } else - dsos__add(kernel_dso); - - vdso = dso__new("[vdso]", 0); - if (!vdso) - return -1; - - vdso->find_symbol = vdso__find_symbol; - - dsos__add(vdso); - - hypervisor_dso = dso__new("[hypervisor]", 0); - if (!hypervisor_dso) - return -1; - dsos__add(hypervisor_dso); - - return err; -} - -static char __cwd[PATH_MAX]; -static char *cwd = __cwd; -static int cwdlen; - -static int strcommon(const char *pathname) -{ - int n = 0; - - while (n < cwdlen && pathname[n] == cwd[n]) - ++n; - - return n; -} - -struct map { - struct list_head node; - u64 start; - u64 end; - u64 pgoff; - u64 (*map_ip)(struct map *, u64); - struct dso *dso; -}; - -static u64 map__map_ip(struct map *map, u64 ip) -{ - return ip - map->start + map->pgoff; -} - -static u64 vdso__map_ip(struct map *map __used, u64 ip) -{ - return ip; -} - -static inline int is_anon_memory(const char *filename) -{ - return strcmp(filename, "//anon") == 0; -} - -static struct map *map__new(struct mmap_event *event) -{ - 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); - - 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; -} - -static struct map *map__clone(struct map *self) -{ - struct map *map = malloc(sizeof(*self)); - - if (!map) - return NULL; - - memcpy(map, self, sizeof(*self)); - - return map; -} - -static 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; -} - -static 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); -} - - -struct thread { - struct rb_node rb_node; - struct list_head maps; - pid_t pid; - char *comm; -}; - -static struct thread *thread__new(pid_t pid) -{ - struct thread *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->pid = pid; - self->comm = malloc(32); - if (self->comm) - snprintf(self->comm, 32, ":%d", self->pid); - INIT_LIST_HEAD(&self->maps); - } - - return self; -} - static unsigned int dsos__col_width, comms__col_width, threads__col_width; -static int thread__set_comm(struct thread *self, const char *comm) -{ - if (self->comm) - free(self->comm); - self->comm = strdup(comm); - if (!self->comm) - return -ENOMEM; - - if (!col_width_list_str && !field_sep && - (!comm_list || strlist__has_entry(comm_list, comm))) { - unsigned int slen = strlen(comm); - if (slen > comms__col_width) { - comms__col_width = slen; - threads__col_width = slen + 6; - } - } - - return 0; -} - -static size_t thread__fprintf(struct thread *self, FILE *fp) -{ - struct map *pos; - size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); - - list_for_each_entry(pos, &self->maps, node) - ret += map__fprintf(pos, fp); - - return ret; -} - - -static struct rb_root threads; -static struct thread *last_match; - -static struct thread *threads__findnew(pid_t pid) -{ - struct rb_node **p = &threads.rb_node; - struct rb_node *parent = NULL; - struct thread *th; - - /* - * Font-end cache - PID lookups come in blocks, - * so most of the time we dont have to look up - * the full rbtree: - */ - if (last_match && last_match->pid == pid) - return last_match; - - while (*p != NULL) { - parent = *p; - th = rb_entry(parent, struct thread, rb_node); - - if (th->pid == pid) { - last_match = th; - return th; - } - - if (pid < th->pid) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - th = thread__new(pid); - if (th != NULL) { - rb_link_node(&th->rb_node, parent, p); - rb_insert_color(&th->rb_node, &threads); - last_match = th; - } - - return th; -} - -static void thread__insert_map(struct thread *self, struct map *map) -{ - struct map *pos, *tmp; - - list_for_each_entry_safe(pos, tmp, &self->maps, node) { - if (map__overlap(pos, map)) { - if (verbose >= 2) { - printf("overlapping maps:\n"); - 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); - } - } - } - - list_add_tail(&map->node, &self->maps); -} - -static int thread__fork(struct thread *self, struct thread *parent) -{ - struct map *map; - - if (self->comm) - free(self->comm); - self->comm = strdup(parent->comm); - if (!self->comm) - return -ENOMEM; - - list_for_each_entry(map, &parent->maps, node) { - struct map *new = map__clone(map); - if (!new) - return -ENOMEM; - thread__insert_map(self, new); - } - - return 0; -} - -static struct map *thread__find_map(struct thread *self, u64 ip) -{ - struct map *pos; - - if (self == NULL) - return NULL; - - list_for_each_entry(pos, &self->maps, node) - if (ip >= pos->start && ip <= pos->end) - return pos; - - return NULL; -} - -static size_t threads__fprintf(FILE *fp) -{ - size_t ret = 0; - struct rb_node *nd; - - for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { - struct thread *pos = rb_entry(nd, struct thread, rb_node); - - ret += thread__fprintf(pos, fp); - } - - return ret; -} - /* * histogram, sorted on item, collects counts */ @@ -574,7 +141,7 @@ struct hist_entry { struct sort_entry { struct list_head list; - char *header; + const char *header; int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*collapse)(struct hist_entry *, struct hist_entry *); @@ -758,7 +325,7 @@ static int sort__need_collapse = 0; static int sort__has_parent = 0; struct sort_dimension { - char *name; + const char *name; struct sort_entry *entry; int taken; }; @@ -773,7 +340,7 @@ static struct sort_dimension sort_dimensions[] = { static LIST_HEAD(hist_entry__sort_list); -static int sort_dimension__add(char *tok) +static int sort_dimension__add(const char *tok) { unsigned int i; @@ -1032,6 +599,7 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, case CHAIN_GRAPH_REL: ret += callchain__fprintf_graph(fp, chain, total_samples, 1, 1); + case CHAIN_NONE: default: break; } @@ -1098,6 +666,27 @@ static void dso__calc_col_width(struct dso *self) self->slen_calculated = 1; } +static int thread__set_comm_adjust(struct thread *self, const char *comm) +{ + int ret = thread__set_comm(self, comm); + + if (ret) + return ret; + + if (!col_width_list_str && !field_sep && + (!comm_list || strlist__has_entry(comm_list, comm))) { + unsigned int slen = strlen(comm); + + if (slen > comms__col_width) { + comms__col_width = slen; + threads__col_width = slen + 6; + } + } + + return 0; +} + + static struct symbol * resolve_symbol(struct thread *thread, struct map **mapp, struct dso **dsop, u64 *ipp) @@ -1141,8 +730,8 @@ got_map: if ((long long)ip < 0) dso = kernel_dso; } - dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); - dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip); + dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); + dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); *ipp = ip; if (dsop) @@ -1398,6 +987,9 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) size_t ret = 0; unsigned int width; char *col_width = col_width_list_str; + int raw_printing_style; + + raw_printing_style = !strcmp(pretty_printing_style, "raw"); init_rem_hits(); @@ -1474,15 +1066,19 @@ print_entries: free(rem_sq_bracket); + if (show_threads) + perf_read_values_display(fp, &show_threads_values, + raw_printing_style); + return ret; } static void register_idle_thread(void) { - struct thread *thread = threads__findnew(0); + struct thread *thread = threads__findnew(0, &threads, &last_match); if (thread == NULL || - thread__set_comm(thread, "[idle]")) { + thread__set_comm_adjust(thread, "[idle]")) { fprintf(stderr, "problem inserting idle task.\n"); exit(-1); } @@ -1514,7 +1110,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) char level; int show = 0; struct dso *dso = NULL; - struct thread *thread = threads__findnew(event->ip.pid); + struct thread *thread; u64 ip = event->ip.ip; u64 period = 1; struct map *map = NULL; @@ -1522,12 +1118,14 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) struct ip_callchain *chain = NULL; int cpumode; + thread = threads__findnew(event->ip.pid, &threads, &last_match); + if (sample_type & PERF_SAMPLE_PERIOD) { period = *(u64 *)more_data; more_data += sizeof(u64); } - dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", + 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, @@ -1540,7 +1138,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) chain = (void *)more_data; - dprintf("... chain: nr:%Lu\n", chain->nr); + dump_printf("... chain: nr:%Lu\n", chain->nr); if (validate_chain(chain, event) < 0) { eprintf("call-chain problem with event, skipping it.\n"); @@ -1549,11 +1147,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) if (dump_trace) { for (i = 0; i < chain->nr; i++) - dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); + dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); } } - dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); if (thread == NULL) { eprintf("problem processing %d event, skipping it.\n", @@ -1572,7 +1170,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dso = kernel_dso; - dprintf(" ...... dso: %s\n", dso->name); + dump_printf(" ...... dso: %s\n", dso->name); } else if (cpumode == PERF_EVENT_MISC_USER) { @@ -1585,7 +1183,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dso = hypervisor_dso; - dprintf(" ...... dso: [hypervisor]\n"); + dump_printf(" ...... dso: [hypervisor]\n"); } if (show & show_mask) { @@ -1611,10 +1209,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) static int process_mmap_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->mmap.pid); - struct map *map = map__new(&event->mmap); + struct thread *thread; + struct map *map = map__new(&event->mmap, cwd, cwdlen); - dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", + thread = threads__findnew(event->mmap.pid, &threads, &last_match); + + dump_printf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", (void *)(offset + head), (void *)(long)(event->header.size), event->mmap.pid, @@ -1625,7 +1225,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) event->mmap.filename); if (thread == NULL || map == NULL) { - dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n"); + dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); return 0; } @@ -1638,16 +1238,18 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) static int process_comm_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->comm.pid); + struct thread *thread; + + thread = threads__findnew(event->comm.pid, &threads, &last_match); - dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", + 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)) { - dprintf("problem processing PERF_EVENT_COMM, skipping event.\n"); + thread__set_comm_adjust(thread, event->comm.comm)) { + dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); return -1; } total_comm++; @@ -1658,10 +1260,13 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) static int process_task_event(event_t *event, unsigned long offset, unsigned long head) { - struct thread *thread = threads__findnew(event->fork.pid); - struct thread *parent = threads__findnew(event->fork.ppid); + struct thread *thread; + struct thread *parent; - dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", + thread = threads__findnew(event->fork.pid, &threads, &last_match); + parent = threads__findnew(event->fork.ppid, &threads, &last_match); + + dump_printf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", @@ -1679,7 +1284,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) return 0; if (!thread || !parent || thread__fork(thread, parent)) { - dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); + dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); return -1; } total_fork++; @@ -1690,7 +1295,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) static int process_lost_event(event_t *event, unsigned long offset, unsigned long head) { - dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", + dump_printf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->lost.id, @@ -1701,67 +1306,24 @@ process_lost_event(event_t *event, unsigned long offset, unsigned long head) return 0; } -static void trace_event(event_t *event) -{ - unsigned char *raw_event = (void *)event; - char *color = PERF_COLOR_BLUE; - int i, j; - - if (!dump_trace) - return; - - dprintf("."); - cdprintf("\n. ... raw event: size %d bytes\n", event->header.size); - - for (i = 0; i < event->header.size; i++) { - if ((i & 15) == 0) { - dprintf("."); - cdprintf(" %04x: ", i); - } - - cdprintf(" %02x", raw_event[i]); - - if (((i & 15) == 15) || i == event->header.size-1) { - cdprintf(" "); - for (j = 0; j < 15-(i & 15); j++) - cdprintf(" "); - for (j = 0; j < (i & 15); j++) { - if (isprint(raw_event[i-15+j])) - cdprintf("%c", raw_event[i-15+j]); - else - cdprintf("."); - } - cdprintf("\n"); - } - } - dprintf(".\n"); -} - -static struct perf_header *header; - -static struct perf_counter_attr *perf_header__find_attr(u64 id) +static int +process_read_event(event_t *event, unsigned long offset, unsigned long head) { - int i; + struct perf_counter_attr *attr; - for (i = 0; i < header->attrs; i++) { - struct perf_header_attr *attr = header->attr[i]; - int j; + attr = perf_header__find_attr(event->read.id, header); - for (j = 0; j < attr->ids; j++) { - if (attr->id[j] == id) - return &attr->attr; - } + if (show_threads) { + const char *name = attr ? __event_name(attr->type, attr->config) + : "unknown"; + perf_read_values_add_value(&show_threads_values, + event->read.pid, event->read.tid, + event->read.id, + name, + event->read.value); } - return NULL; -} - -static int -process_read_event(event_t *event, unsigned long offset, unsigned long head) -{ - struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); - - dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", + dump_printf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", (void *)(offset + head), (void *)(long)(event->header.size), event->read.pid, @@ -1813,35 +1375,21 @@ process_event(event_t *event, unsigned long offset, unsigned long head) return 0; } -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) { int ret, rc = EXIT_FAILURE; unsigned long offset = 0; unsigned long head, shift; - struct stat stat; + struct stat input_stat; event_t *event; uint32_t size; char *buf; register_idle_thread(); + if (show_threads) + perf_read_values_init(&show_threads_values); + input = open(input_name, O_RDONLY); if (input < 0) { fprintf(stderr, " failed to open file: %s", input_name); @@ -1851,18 +1399,18 @@ static int __cmd_report(void) exit(-1); } - ret = fstat(input, &stat); + ret = fstat(input, &input_stat); if (ret < 0) { perror("failed to stat file"); exit(-1); } - if (!force && (stat.st_uid != geteuid())) { + if (!force && (input_stat.st_uid != geteuid())) { fprintf(stderr, "file: %s not owned by current user\n", input_name); exit(-1); } - if (!stat.st_size) { + if (!input_stat.st_size) { fprintf(stderr, "zero-sized file, nothing to do!\n"); exit(0); } @@ -1870,7 +1418,7 @@ static int __cmd_report(void) header = perf_header__read(input); head = header->data_offset; - sample_type = perf_header__sample_type(); + sample_type = perf_header__sample_type(header); if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { if (sort__has_parent) { @@ -1930,12 +1478,12 @@ more: size = 8; if (head + event->header.size >= page_size * mmap_window) { - int ret; + int munmap_ret; shift = page_size * (head / page_size); - ret = munmap(buf, page_size * mmap_window); - assert(ret == 0); + munmap_ret = munmap(buf, page_size * mmap_window); + assert(munmap_ret == 0); offset += shift; head -= shift; @@ -1944,14 +1492,14 @@ more: size = event->header.size; - dprintf("\n%p [%p]: event: %d\n", + dump_printf("\n%p [%p]: event: %d\n", (void *)(offset + head), (void *)(long)event->header.size, event->header.type); if (!size || process_event(event, offset, head) < 0) { - dprintf("%p [%p]: skipping unknown header type: %d\n", + dump_printf("%p [%p]: skipping unknown header type: %d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.type); @@ -1974,25 +1522,25 @@ more: if (offset + head >= header->data_offset + header->data_size) goto done; - if (offset + head < (unsigned long)stat.st_size) + if (offset + head < (unsigned long)input_stat.st_size) goto more; done: rc = EXIT_SUCCESS; close(input); - dprintf(" IP events: %10ld\n", total); - dprintf(" mmap events: %10ld\n", total_mmap); - dprintf(" comm events: %10ld\n", total_comm); - dprintf(" fork events: %10ld\n", total_fork); - dprintf(" lost events: %10ld\n", total_lost); - dprintf(" unknown events: %10ld\n", total_unknown); + dump_printf(" IP events: %10ld\n", total); + dump_printf(" mmap events: %10ld\n", total_mmap); + dump_printf(" comm events: %10ld\n", total_comm); + dump_printf(" fork events: %10ld\n", total_fork); + dump_printf(" lost events: %10ld\n", total_lost); + dump_printf(" unknown events: %10ld\n", total_unknown); if (dump_trace) return 0; if (verbose >= 3) - threads__fprintf(stdout); + threads__fprintf(stdout, &threads); if (verbose >= 2) dsos__fprintf(stdout); @@ -2001,6 +1549,9 @@ done: output__resort(total); output__fprintf(stdout, total); + if (show_threads) + perf_read_values_destroy(&show_threads_values); + return rc; } @@ -2069,12 +1620,16 @@ static const struct option options[] = { "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), - OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), + OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_BOOLEAN('m', "modules", &modules, "load module symbols - WARNING: use only with -k and LIVE kernel"), OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, "Show a column with the number of samples"), + OPT_BOOLEAN('T', "threads", &show_threads, + "Show per-thread event counters"), + OPT_STRING(0, "pretty", &pretty_printing_style, "key", + "pretty printing style key: normal raw"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent"), OPT_BOOLEAN('P', "full-paths", &full_paths, diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index b4b06c7903e..1a262623066 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -42,6 +42,8 @@ #include "util/util.h" #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/event.h" +#include "util/debug.h" #include <sys/prctl.h> #include <math.h> @@ -63,7 +65,6 @@ static struct perf_counter_attr default_attrs[] = { #define MAX_RUN 100 static int system_wide = 0; -static int verbose = 0; static unsigned int nr_cpus = 0; static int run_idx = 0; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7de28ce9ca2..62b55ecab2c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -27,6 +27,8 @@ #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/debug.h" + #include <assert.h> #include <fcntl.h> @@ -68,8 +70,6 @@ static int group = 0; static unsigned int page_size; static unsigned int mmap_pages = 16; static int freq = 0; -static int verbose = 0; -static char *vmlinux = NULL; static int delay_secs = 2; static int zero; @@ -122,7 +122,8 @@ static void parse_source(struct sym_entry *syme) struct module *module; struct section *section = NULL; FILE *file; - char command[PATH_MAX*2], *path = vmlinux; + char command[PATH_MAX*2]; + const char *path = vmlinux_name; u64 start, end, len; if (!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); } -struct dso *kernel_dso; - /* * Symbols will be added here in record_ip and will get out * after decayed. @@ -491,10 +490,12 @@ static void print_sym_table(void) ); 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 *)(syme + 1); + struct symbol *sym; 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) continue; @@ -613,7 +614,7 @@ static void print_mapped_keys(void) 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[s] annotate symbol. \t(%s)\n", name?: "NULL"); fprintf(stdout, "\t[S] stop annotation.\n"); @@ -642,7 +643,9 @@ static int key_mapped(int c) case 'F': case 's': case 'S': - return vmlinux ? 1 : 0; + return vmlinux_name ? 1 : 0; + default: + break; } return 0; @@ -728,6 +731,8 @@ static void handle_keypress(int c) case 'z': zero = ~zero; break; + default: + break; } } @@ -816,13 +821,13 @@ static int parse_symbols(void) { struct rb_node *node; struct symbol *sym; - int modules = vmlinux ? 1 : 0; + int use_modules = vmlinux_name ? 1 : 0; kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); if (kernel_dso == NULL) 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; node = rb_first(&kernel_dso->syms); @@ -937,26 +942,6 @@ static void mmap_read_counter(struct mmap_data *md) last_read = this_read; 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_copy; @@ -1138,7 +1123,7 @@ static const struct option options[] = { "system-wide collection from all CPUs"), OPT_INTEGER('C', "CPU", &profile_cpu, "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, "number of mmap data pages"), OPT_INTEGER('r', "realtime", &realtime_prio, diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 51d168230ee..3a63e41fb44 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -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_version(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 diff --git a/tools/perf/util/abspath.c b/tools/perf/util/abspath.c index 61d33b81fc9..a791dd46726 100644 --- a/tools/perf/util/abspath.c +++ b/tools/perf/util/abspath.c @@ -50,7 +50,8 @@ const char *make_absolute_path(const char *path) die ("Could not get current working directory"); if (last_elem) { - int len = strlen(buf); + len = strlen(buf); + if (len + strlen(last_elem) + 2 > PATH_MAX) die ("Too long path name: '%s/%s'", buf, last_elem); diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 4b50c412b9c..6f8ea9d210b 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -52,7 +52,6 @@ extern const char *perf_mailmap_file; extern void maybe_flush_or_die(FILE *, const char *); extern int copy_fd(int ifd, int ofd); 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 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); diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 01147341164..3b8380f1b47 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -50,6 +50,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, else p = &(*p)->rb_right; break; + case CHAIN_NONE: default: break; } @@ -143,6 +144,7 @@ int register_callchain_param(struct callchain_param *param) case CHAIN_FLAT: param->sort = sort_chain_flat; break; + case CHAIN_NONE: default: return -1; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index a926ae4f5a1..43cf3ea9e08 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -4,6 +4,7 @@ #include "../perf.h" #include <linux/list.h> #include <linux/rbtree.h> +#include "util.h" #include "symbol.h" enum chain_mode { diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 90a044d1fe7..e88bca55a59 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -166,7 +166,7 @@ int perf_color_default_config(const char *var, const char *value, void *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) { int r = 0; @@ -191,6 +191,10 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt, 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, ...) @@ -199,7 +203,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) int r; va_start(args, fmt); - r = color_vfprintf(fp, color, fmt, args, NULL); + r = color_vfprintf(fp, color, fmt, args); va_end(args); return r; } @@ -209,7 +213,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) va_list args; int r; va_start(args, fmt); - r = color_vfprintf(fp, color, fmt, args, "\n"); + r = __color_vfprintf(fp, color, fmt, args, "\n"); va_end(args); return r; } @@ -242,9 +246,9 @@ int color_fwrite_lines(FILE *fp, const char *color, 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 @@ -263,7 +267,7 @@ char *get_percent_color(double percent) int percent_color_fprintf(FILE *fp, const char *fmt, double percent) { int r; - char *color; + const char *color; color = get_percent_color(percent); r = color_fprintf(fp, color, fmt, percent); diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 706cec50bd2..58d597564b9 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -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); void color_parse(const char *value, const char *var, char *dst); void color_parse_mem(const char *value, int len, const char *var, char *dst); +int color_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_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); -char *get_percent_color(double percent); +const char *get_percent_color(double percent); #endif /* COLOR_H */ diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 780df541006..8784649109c 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -160,17 +160,18 @@ static int get_extended_base_var(char *name, int baselen, int c) name[baselen++] = '.'; for (;;) { - int c = get_next_char(); - if (c == '\n') + int ch = get_next_char(); + + if (ch == '\n') return -1; - if (c == '"') + if (ch == '"') break; - if (c == '\\') { - c = get_next_char(); - if (c == '\n') + if (ch == '\\') { + ch = get_next_char(); + if (ch == '\n') return -1; } - name[baselen++] = c; + name[baselen++] = ch; if (baselen > MAXNAME / 2) return -1; } @@ -530,6 +531,8 @@ static int store_aux(const char* key, const char* value, void *cb __used) store.offset[store.seen] = ftell(config_file); } } + default: + break; } return 0; } @@ -619,6 +622,7 @@ contline: switch (contents[offset]) { case '=': equal_offset = offset; break; case ']': bracket_offset = offset; break; + default: break; } if (offset > 0 && contents[offset-1] == '\\') { offset_ = offset; @@ -742,9 +746,9 @@ int perf_config_set_multivar(const char* key, const char* value, goto write_err_out; } else { struct stat st; - char* contents; + char *contents; ssize_t contents_sz, copy_begin, copy_end; - int i, new_line = 0; + int new_line = 0; if (value_regex == NULL) store.value_regex = NULL; diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c new file mode 100644 index 00000000000..e8ca98fe0bd --- /dev/null +++ b/tools/perf/util/debug.c @@ -0,0 +1,95 @@ +/* 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"); +} diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h new file mode 100644 index 00000000000..437eea58ce4 --- /dev/null +++ b/tools/perf/util/debug.h @@ -0,0 +1,8 @@ +/* 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); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h new file mode 100644 index 00000000000..fa2d4e91d32 --- /dev/null +++ b/tools/perf/util/event.h @@ -0,0 +1,96 @@ +#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 diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 34a35286738..2745605dba1 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -6,7 +6,6 @@ #define MAX_ARGS 32 -extern char **environ; static const char *argv_exec_path; static const char *argv0_path; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b92a457ca32..a37a2221a0c 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -243,3 +243,38 @@ struct perf_header *perf_header__read(int fd) 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; +} diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index bf280449fcf..5d0a72ecc91 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -31,6 +31,10 @@ 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); +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); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c new file mode 100644 index 00000000000..804e0238273 --- /dev/null +++ b/tools/perf/util/map.c @@ -0,0 +1,97 @@ +#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); +} diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c index ddabe925d65..3d567fe59c7 100644 --- a/tools/perf/util/module.c +++ b/tools/perf/util/module.c @@ -436,9 +436,9 @@ static int mod_dso__load_module_paths(struct mod_dso *self) goto out_failure; while (!feof(file)) { - char *path, *name, *tmp; + char *name, *tmp; struct module *module; - int line_len, len; + int line_len; line_len = getline(&line, &n, file); if (line_len < 0) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 04417840878..1cda97b3911 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -14,10 +14,10 @@ int nr_counters; struct perf_counter_attr attrs[MAX_COUNTERS]; struct event_symbol { - u8 type; - u64 config; - char *symbol; - char *alias; + u8 type; + u64 config; + const char *symbol; + const char *alias; }; char debugfs_path[MAXPATHLEN]; @@ -51,7 +51,7 @@ static struct event_symbol event_symbols[] = { #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) -static char *hw_event_names[] = { +static const char *hw_event_names[] = { "cycles", "instructions", "cache-references", @@ -61,7 +61,7 @@ static char *hw_event_names[] = { "bus-cycles", }; -static char *sw_event_names[] = { +static const char *sw_event_names[] = { "cpu-clock-msecs", "task-clock-msecs", "page-faults", @@ -73,7 +73,7 @@ static char *sw_event_names[] = { #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-icache", "l1-i", "l1i", "L1-instruction", }, { "LLC", "L2" }, @@ -82,13 +82,13 @@ static char *hw_cache[][MAX_ALIASES] = { { "branch", "branches", "bpu", "btb", "bpc", }, }; -static char *hw_cache_op[][MAX_ALIASES] = { +static const char *hw_cache_op[][MAX_ALIASES] = { { "load", "loads", "read", }, { "store", "stores", "write", }, { "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", }, { "misses", "miss", }, }; @@ -158,7 +158,7 @@ int valid_debugfs_mount(const char *debugfs) return 0; } -static char *tracepoint_id_to_name(u64 config) +static const char *tracepoint_id_to_name(u64 config) { static char tracepoint_name[2 * MAX_EVENT_LENGTH]; DIR *sys_dir, *evt_dir; @@ -235,7 +235,7 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) return name; } -char *event_name(int counter) +const char *event_name(int counter) { u64 config = attrs[counter].config; int type = attrs[counter].type; @@ -243,7 +243,7 @@ char *event_name(int counter) return __event_name(type, config); } -char *__event_name(int type, u64 config) +const char *__event_name(int type, u64 config) { static char buf[32]; @@ -294,7 +294,7 @@ char *__event_name(int type, u64 config) 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 n, longest = -1; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 192a962e3a0..9b1aeea0163 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -9,8 +9,8 @@ extern int nr_counters; extern struct perf_counter_attr attrs[MAX_COUNTERS]; -extern char *event_name(int ctr); -extern char *__event_name(int type, u64 config); +extern const char *event_name(int ctr); +extern const char *__event_name(int type, u64 config); extern int parse_events(const struct option *opt, const char *str, int unset); diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 1bf67190c82..6d8af48c925 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -53,6 +53,12 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_SET_INT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: + case OPTION_STRING: + case OPTION_INTEGER: + case OPTION_LONG: default: break; } @@ -130,6 +136,9 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: default: die("should not happen, someone must be hit on the forehead"); } @@ -296,6 +305,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, return parse_options_usage(usagestr, options); case -2: goto unknown; + default: + break; } if (ctx->opt) check_typos(arg + 1, options); @@ -314,6 +325,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, ctx->argv[0] = strdup(ctx->opt - 1); *(char *)ctx->argv[0] = '-'; goto unknown; + default: + break; } } continue; @@ -336,6 +349,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, return parse_options_usage(usagestr, options); case -2: goto unknown; + default: + break; } continue; unknown: @@ -456,6 +471,13 @@ int usage_with_options_internal(const char * const *usagestr, } break; default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ + case OPTION_END: + case OPTION_GROUP: + case OPTION_BIT: + case OPTION_BOOLEAN: + case OPTION_SET_INT: + case OPTION_SET_PTR: + case OPTION_LONG: break; } diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index a501a40dd2c..fd1f2faaade 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -17,7 +17,7 @@ static char bad_path[] = "/bad-path/"; * Two hacks: */ -static char *get_perf_dir(void) +static const char *get_perf_dir(void) { return "."; } @@ -38,8 +38,9 @@ size_t strlcpy(char *dest, const char *src, size_t size) static char *get_pathname(void) { static char pathname_array[4][PATH_MAX]; - static int index; - return pathname_array[3 & ++index]; + static int idx; + + return pathname_array[3 & ++idx]; } static char *cleanup_path(char *path) @@ -161,20 +162,24 @@ int perf_mkstemp(char *path, size_t len, const char *template) } -const char *make_relative_path(const char *abs, const char *base) +const char *make_relative_path(const char *abs_path, const char *base) { static char buf[PATH_MAX + 1]; int baselen; + if (!base) - return abs; + return abs_path; + baselen = strlen(base); - if (prefixcmp(abs, base)) - return abs; - if (abs[baselen] == '/') + if (prefixcmp(abs_path, base)) + return abs_path; + if (abs_path[baselen] == '/') baselen++; else if (base[baselen - 1] != '/') - return abs; - strcpy(buf, abs + baselen); + return abs_path; + + strcpy(buf, abs_path + baselen); + return buf; } diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index a3935343091..2b615acf94d 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c @@ -262,7 +262,7 @@ int run_hook(const char *index_file, const char *name, ...) { struct child_process hook; const char **argv = NULL, *env[2]; - char index[PATH_MAX]; + char idx[PATH_MAX]; va_list args; int ret; size_t i = 0, alloc = 0; @@ -284,8 +284,8 @@ int run_hook(const char *index_file, const char *name, ...) hook.no_stdin = 1; hook.stdout_to_stderr = 1; if (index_file) { - snprintf(index, sizeof(index), "PERF_INDEX_FILE=%s", index_file); - env[0] = index; + snprintf(idx, sizeof(idx), "PERF_INDEX_FILE=%s", index_file); + env[0] = idx; env[1] = NULL; hook.env = env; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5c0f42e6b33..fd3d9c8e90f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -3,6 +3,8 @@ #include "string.h" #include "symbol.h" +#include "debug.h" + #include <libelf.h> #include <gelf.h> #include <elf.h> @@ -21,7 +23,7 @@ enum dso_origin { static struct symbol *symbol__new(u64 start, u64 len, const char *name, unsigned int priv_size, - u64 obj_start, int verbose) + u64 obj_start, int v) { size_t namelen = strlen(name) + 1; struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); @@ -29,7 +31,7 @@ static struct symbol *symbol__new(u64 start, u64 len, if (!self) return NULL; - if (verbose >= 2) + if (v >= 2) printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); @@ -156,7 +158,7 @@ size_t dso__fprintf(struct dso *self, FILE *fp) return ret; } -static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verbose) +static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) { struct rb_node *nd, *prevnd; char *line = NULL; @@ -198,7 +200,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verb * Well fix up the end later, when we have all sorted. */ sym = symbol__new(start, 0xdead, line + len + 2, - self->sym_priv_size, 0, verbose); + self->sym_priv_size, 0, v); if (sym == NULL) goto out_delete_line; @@ -239,7 +241,7 @@ out_failure: return -1; } -static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verbose) +static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) { char *line = NULL; size_t n; @@ -277,7 +279,7 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verb continue; sym = symbol__new(start, size, line + len, - self->sym_priv_size, start, verbose); + self->sym_priv_size, start, v); if (sym == NULL) goto out_delete_line; @@ -305,13 +307,13 @@ out_failure: * elf_symtab__for_each_symbol - iterate thru all the symbols * * @self: struct elf_symtab instance to iterate - * @index: uint32_t index + * @idx: uint32_t idx * @sym: GElf_Sym iterator */ -#define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ - for (index = 0, gelf_getsym(syms, index, &sym);\ - index < nr_syms; \ - index++, gelf_getsym(syms, index, &sym)) +#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ + for (idx = 0, gelf_getsym(syms, idx, &sym);\ + idx < nr_syms; \ + idx++, gelf_getsym(syms, idx, &sym)) static inline uint8_t elf_sym__type(const GElf_Sym *sym) { @@ -354,7 +356,7 @@ static inline const char *elf_sym__name(const GElf_Sym *sym, static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, GElf_Shdr *shp, const char *name, - size_t *index) + size_t *idx) { Elf_Scn *sec = NULL; size_t cnt = 1; @@ -365,8 +367,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, gelf_getshdr(sec, shp); str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); if (!strcmp(name, str)) { - if (index) - *index = cnt; + if (idx) + *idx = cnt; break; } ++cnt; @@ -392,7 +394,7 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, * And always look at the original dso, not at debuginfo packages, that * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). */ -static int dso__synthesize_plt_symbols(struct dso *self, int verbose) +static int dso__synthesize_plt_symbols(struct dso *self, int v) { uint32_t nr_rel_entries, idx; GElf_Sym sym; @@ -442,7 +444,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int verbose) goto out_elf_end; /* - * Fetch the relocation section to find the indexes to the GOT + * Fetch the relocation section to find the idxes to the GOT * and the symbols in the .dynsym they refer to. */ reldata = elf_getdata(scn_plt_rel, NULL); @@ -476,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int verbose) "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname, self->sym_priv_size, 0, verbose); + sympltname, self->sym_priv_size, 0, v); if (!f) goto out_elf_end; @@ -494,7 +496,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int verbose) "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname, self->sym_priv_size, 0, verbose); + sympltname, self->sym_priv_size, 0, v); if (!f) goto out_elf_end; @@ -518,12 +520,12 @@ out: } static int dso__load_sym(struct dso *self, int fd, const char *name, - symbol_filter_t filter, int verbose, struct module *mod) + symbol_filter_t filter, int v, struct module *mod) { Elf_Data *symstrs, *secstrs; uint32_t nr_syms; int err = -1; - uint32_t index; + uint32_t idx; GElf_Ehdr ehdr; GElf_Shdr shdr; Elf_Data *syms; @@ -534,14 +536,14 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (elf == NULL) { - if (verbose) + if (v) fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, name); goto out_close; } if (gelf_getehdr(elf, &ehdr) == NULL) { - if (verbose) + if (v) fprintf(stderr, "%s: cannot get elf header.\n", __func__); goto out_elf_end; } @@ -583,9 +585,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, NULL) != NULL); } else self->adjust_symbols = 0; - elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { + elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { struct symbol *f; - const char *name; + const char *elf_name; char *demangled; u64 obj_start; struct section *section = NULL; @@ -608,7 +610,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, obj_start = sym.st_value; if (self->adjust_symbols) { - if (verbose >= 2) + if (v >= 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); @@ -630,13 +632,13 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, * DWARF DW_compile_unit has this, but we don't always have access * to it... */ - name = elf_sym__name(&sym, symstrs); - demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); + elf_name = elf_sym__name(&sym, symstrs); + demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); if (demangled != NULL) - name = demangled; + elf_name = demangled; - f = symbol__new(sym.st_value, sym.st_size, name, - self->sym_priv_size, obj_start, verbose); + f = symbol__new(sym.st_value, sym.st_size, elf_name, + self->sym_priv_size, obj_start, v); free(demangled); if (!f) goto out_elf_end; @@ -659,7 +661,7 @@ out_close: #define BUILD_ID_SIZE 128 -static char *dso__read_build_id(struct dso *self, int verbose) +static char *dso__read_build_id(struct dso *self, int v) { int i; GElf_Ehdr ehdr; @@ -676,14 +678,14 @@ static char *dso__read_build_id(struct dso *self, int verbose) elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (elf == NULL) { - if (verbose) + if (v) fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, self->name); goto out_close; } if (gelf_getehdr(elf, &ehdr) == NULL) { - if (verbose) + if (v) fprintf(stderr, "%s: cannot get elf header.\n", __func__); goto out_elf_end; } @@ -706,7 +708,7 @@ static char *dso__read_build_id(struct dso *self, int verbose) ++raw; bid += 2; } - if (verbose >= 2) + if (v >= 2) printf("%s(%s): %s\n", __func__, self->name, build_id); out_elf_end: elf_end(elf); @@ -732,7 +734,7 @@ char dso__symtab_origin(const struct dso *self) return origin[self->origin]; } -int dso__load(struct dso *self, symbol_filter_t filter, int verbose) +int dso__load(struct dso *self, symbol_filter_t filter, int v) { int size = PATH_MAX; char *name = malloc(size), *build_id = NULL; @@ -745,7 +747,7 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose) self->adjust_symbols = 0; if (strncmp(self->name, "/tmp/perf-", 10) == 0) { - ret = dso__load_perf_map(self, filter, verbose); + ret = dso__load_perf_map(self, filter, v); self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : DSO__ORIG_NOT_FOUND; return ret; @@ -764,7 +766,7 @@ more: snprintf(name, size, "/usr/lib/debug%s", self->name); break; case DSO__ORIG_BUILDID: - build_id = dso__read_build_id(self, verbose); + build_id = dso__read_build_id(self, v); if (build_id != NULL) { snprintf(name, size, "/usr/lib/debug/.build-id/%.2s/%s.debug", @@ -785,7 +787,7 @@ more: fd = open(name, O_RDONLY); } while (fd < 0); - ret = dso__load_sym(self, fd, name, filter, verbose, NULL); + ret = dso__load_sym(self, fd, name, filter, v, NULL); close(fd); /* @@ -795,7 +797,7 @@ more: goto more; if (ret > 0) { - int nr_plt = dso__synthesize_plt_symbols(self, verbose); + int nr_plt = dso__synthesize_plt_symbols(self, v); if (nr_plt > 0) ret += nr_plt; } @@ -807,7 +809,7 @@ out: } static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, - symbol_filter_t filter, int verbose) + symbol_filter_t filter, int v) { struct module *mod = mod_dso__find_module(mods, name); int err = 0, fd; @@ -820,13 +822,13 @@ static int dso__load_module(struct dso *self, struct mod_dso *mods, const char * if (fd < 0) return err; - err = dso__load_sym(self, fd, name, filter, verbose, mod); + err = dso__load_sym(self, fd, name, filter, v, mod); close(fd); return err; } -int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) +int dso__load_modules(struct dso *self, symbol_filter_t filter, int v) { struct mod_dso *mods = mod_dso__new_dso("modules"); struct module *pos; @@ -844,7 +846,7 @@ int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) next = rb_first(&mods->mods); while (next) { pos = rb_entry(next, struct module, rb_node); - err = dso__load_module(self, mods, pos->name, filter, verbose); + err = dso__load_module(self, mods, pos->name, filter, v); if (err < 0) break; @@ -887,14 +889,14 @@ static inline void dso__fill_symbol_holes(struct dso *self) } static int dso__load_vmlinux(struct dso *self, const char *vmlinux, - symbol_filter_t filter, int verbose) + symbol_filter_t filter, int v) { int err, fd = open(vmlinux, O_RDONLY); if (fd < 0) return -1; - err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL); + err = dso__load_sym(self, fd, vmlinux, filter, v, NULL); if (err > 0) dso__fill_symbol_holes(self); @@ -905,18 +907,18 @@ static int dso__load_vmlinux(struct dso *self, const char *vmlinux, } int dso__load_kernel(struct dso *self, const char *vmlinux, - symbol_filter_t filter, int verbose, int modules) + symbol_filter_t filter, int v, int use_modules) { int err = -1; if (vmlinux) { - err = dso__load_vmlinux(self, vmlinux, filter, verbose); - if (err > 0 && modules) - err = dso__load_modules(self, filter, verbose); + err = dso__load_vmlinux(self, vmlinux, filter, v); + if (err > 0 && use_modules) + err = dso__load_modules(self, filter, v); } if (err <= 0) - err = dso__load_kallsyms(self, filter, verbose); + err = dso__load_kallsyms(self, filter, v); if (err > 0) self->origin = DSO__ORIG_KERNEL; @@ -924,6 +926,103 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, return err; } +LIST_HEAD(dsos); +struct dso *kernel_dso; +struct dso *vdso; +struct dso *hypervisor_dso; + +const char *vmlinux_name = "vmlinux"; +int modules; + +static void dsos__add(struct dso *dso) +{ + list_add_tail(&dso->node, &dsos); +} + +static struct dso *dsos__find(const char *name) +{ + struct dso *pos; + + list_for_each_entry(pos, &dsos, node) + if (strcmp(pos->name, name) == 0) + return pos; + return NULL; +} + +struct dso *dsos__findnew(const char *name) +{ + struct dso *dso = dsos__find(name); + int nr; + + if (dso) + return dso; + + dso = dso__new(name, 0); + if (!dso) + goto out_delete_dso; + + nr = dso__load(dso, NULL, verbose); + if (nr < 0) { + eprintf("Failed to open: %s\n", name); + goto out_delete_dso; + } + if (!nr) + eprintf("No symbols found in: %s, maybe install a debug package?\n", name); + + dsos__add(dso); + + return dso; + +out_delete_dso: + dso__delete(dso); + return NULL; +} + +void dsos__fprintf(FILE *fp) +{ + struct dso *pos; + + list_for_each_entry(pos, &dsos, node) + dso__fprintf(pos, fp); +} + +static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) +{ + return dso__find_symbol(dso, ip); +} + +int load_kernel(void) +{ + int err; + + kernel_dso = dso__new("[kernel]", 0); + if (!kernel_dso) + return -1; + + err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules); + if (err <= 0) { + dso__delete(kernel_dso); + kernel_dso = NULL; + } else + dsos__add(kernel_dso); + + vdso = dso__new("[vdso]", 0); + if (!vdso) + return -1; + + vdso->find_symbol = vdso__find_symbol; + + dsos__add(vdso); + + hypervisor_dso = dso__new("[hypervisor]", 0); + if (!hypervisor_dso) + return -1; + dsos__add(hypervisor_dso); + + return err; +} + + void symbol__init(void) { elf_version(EV_CURRENT); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b53bf0125c1..6e849071640 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -6,6 +6,7 @@ #include <linux/list.h> #include <linux/rbtree.h> #include "module.h" +#include "event.h" #ifdef HAVE_CPLUS_DEMANGLE extern char *cplus_demangle(const char *, int); @@ -54,7 +55,7 @@ struct dso { char name[0]; }; -const char *sym_hist_filter; +extern const char *sym_hist_filter; typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); @@ -72,9 +73,20 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, symbol_filter_t filter, int verbose, int modules); int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); int dso__load(struct dso *self, symbol_filter_t filter, int verbose); +struct dso *dsos__findnew(const char *name); +void dsos__fprintf(FILE *fp); size_t dso__fprintf(struct dso *self, FILE *fp); char dso__symtab_origin(const struct dso *self); +int load_kernel(void); + void symbol__init(void); + +extern struct list_head dsos; +extern struct dso *kernel_dso; +extern struct dso *vdso; +extern struct dso *hypervisor_dso; +extern const char *vmlinux_name; +extern int modules; #endif /* _PERF_SYMBOL_ */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c new file mode 100644 index 00000000000..f98032c135c --- /dev/null +++ b/tools/perf/util/thread.c @@ -0,0 +1,162 @@ +#include "../perf.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "thread.h" +#include "util.h" +#include "debug.h" + +static struct thread *thread__new(pid_t pid) +{ + struct thread *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->pid = pid; + self->comm = malloc(32); + if (self->comm) + snprintf(self->comm, 32, ":%d", self->pid); + INIT_LIST_HEAD(&self->maps); + } + + return self; +} + +int thread__set_comm(struct thread *self, const char *comm) +{ + if (self->comm) + free(self->comm); + self->comm = strdup(comm); + return self->comm ? 0 : -ENOMEM; +} + +static size_t thread__fprintf(struct thread *self, FILE *fp) +{ + struct map *pos; + size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); + + list_for_each_entry(pos, &self->maps, node) + ret += map__fprintf(pos, fp); + + return ret; +} + +struct thread * +threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) +{ + struct rb_node **p = &threads->rb_node; + struct rb_node *parent = NULL; + struct thread *th; + + /* + * Font-end cache - PID lookups come in blocks, + * so most of the time we dont have to look up + * the full rbtree: + */ + if (*last_match && (*last_match)->pid == pid) + return *last_match; + + while (*p != NULL) { + parent = *p; + th = rb_entry(parent, struct thread, rb_node); + + if (th->pid == pid) { + *last_match = th; + return th; + } + + if (pid < th->pid) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + th = thread__new(pid); + if (th != NULL) { + rb_link_node(&th->rb_node, parent, p); + rb_insert_color(&th->rb_node, threads); + *last_match = th; + } + + return th; +} + +void thread__insert_map(struct thread *self, struct map *map) +{ + struct map *pos, *tmp; + + list_for_each_entry_safe(pos, tmp, &self->maps, node) { + if (map__overlap(pos, map)) { + if (verbose >= 2) { + printf("overlapping maps:\n"); + 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); + } + } + } + + list_add_tail(&map->node, &self->maps); +} + +int thread__fork(struct thread *self, struct thread *parent) +{ + struct map *map; + + if (self->comm) + free(self->comm); + self->comm = strdup(parent->comm); + if (!self->comm) + return -ENOMEM; + + list_for_each_entry(map, &parent->maps, node) { + struct map *new = map__clone(map); + if (!new) + return -ENOMEM; + thread__insert_map(self, new); + } + + return 0; +} + +struct map *thread__find_map(struct thread *self, u64 ip) +{ + struct map *pos; + + if (self == NULL) + return NULL; + + list_for_each_entry(pos, &self->maps, node) + if (ip >= pos->start && ip <= pos->end) + return pos; + + return NULL; +} + +size_t threads__fprintf(FILE *fp, struct rb_root *threads) +{ + size_t ret = 0; + struct rb_node *nd; + + for (nd = rb_first(threads); nd; nd = rb_next(nd)) { + struct thread *pos = rb_entry(nd, struct thread, rb_node); + + ret += thread__fprintf(pos, fp); + } + + return ret; +} diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h new file mode 100644 index 00000000000..b1c66719379 --- /dev/null +++ b/tools/perf/util/thread.h @@ -0,0 +1,19 @@ +#include <linux/rbtree.h> +#include <linux/list.h> +#include <unistd.h> +#include "symbol.h" + +struct thread { + struct rb_node rb_node; + struct list_head maps; + pid_t pid; + char *comm; +}; + +int thread__set_comm(struct thread *self, const char *comm); +struct thread * +threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match); +void thread__insert_map(struct thread *self, struct map *map); +int thread__fork(struct thread *self, struct thread *parent); +struct map *thread__find_map(struct thread *self, u64 ip); +size_t threads__fprintf(FILE *fp, struct rb_root *threads); diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 68fe157d72f..15004d21166 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -39,10 +39,6 @@ /* Approximation of the length of the decimal representation of this type. */ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) -#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) -#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ -#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ -#endif #define _ALL_SOURCE 1 #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 @@ -83,6 +79,7 @@ #include <inttypes.h> #include "../../../include/linux/magic.h" + #ifndef NO_ICONV #include <iconv.h> #endif diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c new file mode 100644 index 00000000000..1c15e39f99e --- /dev/null +++ b/tools/perf/util/values.c @@ -0,0 +1,230 @@ +#include <stdlib.h> + +#include "util.h" +#include "values.h" + +void perf_read_values_init(struct perf_read_values *values) +{ + values->threads_max = 16; + values->pid = malloc(values->threads_max * sizeof(*values->pid)); + values->tid = malloc(values->threads_max * sizeof(*values->tid)); + values->value = malloc(values->threads_max * sizeof(*values->value)); + if (!values->pid || !values->tid || !values->value) + die("failed to allocate read_values threads arrays"); + values->threads = 0; + + values->counters_max = 16; + values->counterrawid = malloc(values->counters_max + * sizeof(*values->counterrawid)); + values->countername = malloc(values->counters_max + * sizeof(*values->countername)); + if (!values->counterrawid || !values->countername) + die("failed to allocate read_values counters arrays"); + values->counters = 0; +} + +void perf_read_values_destroy(struct perf_read_values *values) +{ + int i; + + if (!values->threads_max || !values->counters_max) + return; + + for (i = 0; i < values->threads; i++) + free(values->value[i]); + free(values->pid); + free(values->tid); + free(values->counterrawid); + for (i = 0; i < values->counters; i++) + free(values->countername[i]); + free(values->countername); +} + +static void perf_read_values__enlarge_threads(struct perf_read_values *values) +{ + values->threads_max *= 2; + values->pid = realloc(values->pid, + values->threads_max * sizeof(*values->pid)); + values->tid = realloc(values->tid, + values->threads_max * sizeof(*values->tid)); + values->value = realloc(values->value, + values->threads_max * sizeof(*values->value)); + if (!values->pid || !values->tid || !values->value) + die("failed to enlarge read_values threads arrays"); +} + +static int perf_read_values__findnew_thread(struct perf_read_values *values, + u32 pid, u32 tid) +{ + int i; + + for (i = 0; i < values->threads; i++) + if (values->pid[i] == pid && values->tid[i] == tid) + return i; + + if (values->threads == values->threads_max) + perf_read_values__enlarge_threads(values); + + i = values->threads++; + values->pid[i] = pid; + values->tid[i] = tid; + values->value[i] = malloc(values->counters_max * sizeof(**values->value)); + if (!values->value[i]) + die("failed to allocate read_values counters array"); + + return i; +} + +static void perf_read_values__enlarge_counters(struct perf_read_values *values) +{ + int i; + + values->counters_max *= 2; + values->counterrawid = realloc(values->counterrawid, + values->counters_max * sizeof(*values->counterrawid)); + values->countername = realloc(values->countername, + values->counters_max * sizeof(*values->countername)); + if (!values->counterrawid || !values->countername) + die("failed to enlarge read_values counters arrays"); + + for (i = 0; i < values->threads; i++) { + values->value[i] = realloc(values->value[i], + values->counters_max * sizeof(**values->value)); + if (!values->value[i]) + die("failed to enlarge read_values counters arrays"); + } +} + +static int perf_read_values__findnew_counter(struct perf_read_values *values, + u64 rawid, const char *name) +{ + int i; + + for (i = 0; i < values->counters; i++) + if (values->counterrawid[i] == rawid) + return i; + + if (values->counters == values->counters_max) + perf_read_values__enlarge_counters(values); + + i = values->counters++; + values->counterrawid[i] = rawid; + values->countername[i] = strdup(name); + + return i; +} + +void perf_read_values_add_value(struct perf_read_values *values, + u32 pid, u32 tid, + u64 rawid, const char *name, u64 value) +{ + int tindex, cindex; + + tindex = perf_read_values__findnew_thread(values, pid, tid); + cindex = perf_read_values__findnew_counter(values, rawid, name); + + values->value[tindex][cindex] = value; +} + +static void perf_read_values__display_pretty(FILE *fp, + struct perf_read_values *values) +{ + int i, j; + int pidwidth, tidwidth; + int *counterwidth; + + counterwidth = malloc(values->counters * sizeof(*counterwidth)); + if (!counterwidth) + die("failed to allocate counterwidth array"); + tidwidth = 3; + pidwidth = 3; + for (j = 0; j < values->counters; j++) + counterwidth[j] = strlen(values->countername[j]); + for (i = 0; i < values->threads; i++) { + int width; + + width = snprintf(NULL, 0, "%d", values->pid[i]); + if (width > pidwidth) + pidwidth = width; + width = snprintf(NULL, 0, "%d", values->tid[i]); + if (width > tidwidth) + tidwidth = width; + for (j = 0; j < values->counters; j++) { + width = snprintf(NULL, 0, "%Lu", values->value[i][j]); + if (width > counterwidth[j]) + counterwidth[j] = width; + } + } + + fprintf(fp, "# %*s %*s", pidwidth, "PID", tidwidth, "TID"); + for (j = 0; j < values->counters; j++) + fprintf(fp, " %*s", counterwidth[j], values->countername[j]); + fprintf(fp, "\n"); + + for (i = 0; i < values->threads; i++) { + fprintf(fp, " %*d %*d", pidwidth, values->pid[i], + tidwidth, values->tid[i]); + for (j = 0; j < values->counters; j++) + fprintf(fp, " %*Lu", + counterwidth[j], values->value[i][j]); + fprintf(fp, "\n"); + } +} + +static void perf_read_values__display_raw(FILE *fp, + struct perf_read_values *values) +{ + int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth; + int i, j; + + tidwidth = 3; /* TID */ + pidwidth = 3; /* PID */ + namewidth = 4; /* "Name" */ + rawwidth = 3; /* "Raw" */ + countwidth = 5; /* "Count" */ + + for (i = 0; i < values->threads; i++) { + width = snprintf(NULL, 0, "%d", values->pid[i]); + if (width > pidwidth) + pidwidth = width; + width = snprintf(NULL, 0, "%d", values->tid[i]); + if (width > tidwidth) + tidwidth = width; + } + for (j = 0; j < values->counters; j++) { + width = strlen(values->countername[j]); + if (width > namewidth) + namewidth = width; + width = snprintf(NULL, 0, "%llx", values->counterrawid[j]); + if (width > rawwidth) + rawwidth = width; + } + for (i = 0; i < values->threads; i++) { + for (j = 0; j < values->counters; j++) { + width = snprintf(NULL, 0, "%Lu", values->value[i][j]); + if (width > countwidth) + countwidth = width; + } + } + + fprintf(fp, "# %*s %*s %*s %*s %*s\n", + pidwidth, "PID", tidwidth, "TID", + namewidth, "Name", rawwidth, "Raw", + countwidth, "Count"); + for (i = 0; i < values->threads; i++) + for (j = 0; j < values->counters; j++) + fprintf(fp, " %*d %*d %*s %*llx %*Lu\n", + pidwidth, values->pid[i], + tidwidth, values->tid[i], + namewidth, values->countername[j], + rawwidth, values->counterrawid[j], + countwidth, values->value[i][j]); +} + +void perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw) +{ + if (raw) + perf_read_values__display_raw(fp, values); + else + perf_read_values__display_pretty(fp, values); +} diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h new file mode 100644 index 00000000000..cadf8cf2a59 --- /dev/null +++ b/tools/perf/util/values.h @@ -0,0 +1,27 @@ +#ifndef _PERF_VALUES_H +#define _PERF_VALUES_H + +#include "types.h" + +struct perf_read_values { + int threads; + int threads_max; + u32 *pid, *tid; + int counters; + int counters_max; + u64 *counterrawid; + char **countername; + u64 **value; +}; + +void perf_read_values_init(struct perf_read_values *values); +void perf_read_values_destroy(struct perf_read_values *values); + +void perf_read_values_add_value(struct perf_read_values *values, + u32 pid, u32 tid, + u64 rawid, const char *name, u64 value); + +void perf_read_values_display(FILE *fp, struct perf_read_values *values, + int raw); + +#endif /* _PERF_VALUES_H */ |