1/* 2 * Linux performance counter support for MIPS. 3 * 4 * Copyright (C) 2010 MIPS Technologies, Inc. 5 * Author: Deng-Cheng Zhu 6 * 7 * This code is based on the implementation for ARM, which is in turn 8 * based on the sparc64 perf event code and the x86 code. Performance 9 * counter access is based on the MIPS Oprofile code. And the callchain 10 * support references the code of MIPS stacktrace.c. 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 */ 16 17#include <linux/perf_event.h> 18 19#include <asm/stacktrace.h> 20 21/* Callchain handling code. */ 22 23/* 24 * Leave userspace callchain empty for now. When we find a way to trace 25 * the user stack callchains, we will add it here. 26 */ 27 28static void save_raw_perf_callchain(struct perf_callchain_entry *entry, 29 unsigned long reg29) 30{ 31 unsigned long *sp = (unsigned long *)reg29; 32 unsigned long addr; 33 34 while (!kstack_end(sp)) { 35 addr = *sp++; 36 if (__kernel_text_address(addr)) { 37 perf_callchain_store(entry, addr); 38 if (entry->nr >= PERF_MAX_STACK_DEPTH) 39 break; 40 } 41 } 42} 43 44void perf_callchain_kernel(struct perf_callchain_entry *entry, 45 struct pt_regs *regs) 46{ 47 unsigned long sp = regs->regs[29]; 48#ifdef CONFIG_KALLSYMS 49 unsigned long ra = regs->regs[31]; 50 unsigned long pc = regs->cp0_epc; 51 52 if (raw_show_trace || !__kernel_text_address(pc)) { 53 unsigned long stack_page = 54 (unsigned long)task_stack_page(current); 55 if (stack_page && sp >= stack_page && 56 sp <= stack_page + THREAD_SIZE - 32) 57 save_raw_perf_callchain(entry, sp); 58 return; 59 } 60 do { 61 perf_callchain_store(entry, pc); 62 if (entry->nr >= PERF_MAX_STACK_DEPTH) 63 break; 64 pc = unwind_stack(current, &sp, pc, &ra); 65 } while (pc); 66#else 67 save_raw_perf_callchain(entry, sp); 68#endif 69} 70