This patch adds callchain support for MIPS Perf-events. For more info on this feature, please refer to tools/perf/Documentation/perf-report.txt and tools/perf/design.txt. Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@xxxxxxxxx> --- arch/mips/kernel/perf_event.c | 98 ++++++++++++++++++++++++++++++++++++++++- 1 files changed, 97 insertions(+), 1 deletions(-) diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c index 788815f..63ea0e9 100644 --- a/arch/mips/kernel/perf_event.c +++ b/arch/mips/kernel/perf_event.c @@ -5,7 +5,8 @@ * * This code is based on the implementation for ARM, which is in turn * based on the sparc64 perf event code and the x86 code. Performance - * counter access is based on the MIPS Oprofile code. + * counter access is based on the MIPS Oprofile code. And the callchain + * support references the code of MIPS traps.c. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -501,3 +502,98 @@ handle_associated_event(struct cpu_hw_events *cpuc, mipspmu->disable_event(idx); } +/* + * Callchain handling code. + */ +static inline void +callchain_store(struct perf_callchain_entry *entry, + u64 ip) +{ + if (entry->nr < PERF_MAX_STACK_DEPTH) + entry->ip[entry->nr++] = ip; +} + +static void +perf_callchain_user(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long *sp; + unsigned long addr; + + callchain_store(entry, PERF_CONTEXT_USER); + + if (!user_mode(regs)) + regs = task_pt_regs(current); + + sp = (unsigned long *)(regs->regs[29] & ~3); + + while (!kstack_end(sp)) { + unsigned long __user *p = + (unsigned long __user *)(unsigned long)sp++; + if (__get_user(addr, p)) { + pr_warning("Performance counter callchain " + "suppport: Bad stack address.\n"); + break; + } + callchain_store(entry, addr); + } +} + +static void +perf_callchain_kernel(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + unsigned long sp = regs->regs[29]; + unsigned long ra = regs->regs[31]; + unsigned long pc = regs->cp0_epc; + + if (unlikely(!__kernel_text_address(pc))) { + pr_warning("Performance counter callchain support " + "error.\n"); + return; + } + + callchain_store(entry, PERF_CONTEXT_KERNEL); + + do { + callchain_store(entry, pc); + pc = unwind_stack(current, &sp, pc, &ra); + } while (pc); +} + +static void +perf_do_callchain(struct pt_regs *regs, + struct perf_callchain_entry *entry) +{ + int is_user; + + if (!regs) + return; + + is_user = user_mode(regs); + + if (!current || !current->pid) + return; + + if (is_user && current->state != TASK_RUNNING) + return; + + if (!is_user) + perf_callchain_kernel(regs, entry); + + if (current->mm) + perf_callchain_user(regs, entry); +} + +static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry); + +struct perf_callchain_entry * +perf_callchain(struct pt_regs *regs) +{ + struct perf_callchain_entry *entry = &__get_cpu_var(pmc_irq_entry); + + entry->nr = 0; + perf_do_callchain(regs, entry); + return entry; +} + -- 1.6.3.3