xref: /openbmc/linux/arch/mips/kernel/perf_event.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
214f70012SDeng-Cheng Zhu /*
314f70012SDeng-Cheng Zhu  * Linux performance counter support for MIPS.
414f70012SDeng-Cheng Zhu  *
514f70012SDeng-Cheng Zhu  * Copyright (C) 2010 MIPS Technologies, Inc.
614f70012SDeng-Cheng Zhu  * Author: Deng-Cheng Zhu
714f70012SDeng-Cheng Zhu  *
814f70012SDeng-Cheng Zhu  * This code is based on the implementation for ARM, which is in turn
914f70012SDeng-Cheng Zhu  * based on the sparc64 perf event code and the x86 code. Performance
107e788d96SDeng-Cheng Zhu  * counter access is based on the MIPS Oprofile code. And the callchain
117e788d96SDeng-Cheng Zhu  * support references the code of MIPS stacktrace.c.
1214f70012SDeng-Cheng Zhu  */
1314f70012SDeng-Cheng Zhu 
1414f70012SDeng-Cheng Zhu #include <linux/perf_event.h>
1568db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
1614f70012SDeng-Cheng Zhu 
1714f70012SDeng-Cheng Zhu #include <asm/stacktrace.h>
183a9ab99eSDeng-Cheng Zhu 
197e788d96SDeng-Cheng Zhu /* Callchain handling code. */
207e788d96SDeng-Cheng Zhu 
217e788d96SDeng-Cheng Zhu /*
227e788d96SDeng-Cheng Zhu  * Leave userspace callchain empty for now. When we find a way to trace
23e5dcb58aSDavid Daney  * the user stack callchains, we will add it here.
247e788d96SDeng-Cheng Zhu  */
257e788d96SDeng-Cheng Zhu 
save_raw_perf_callchain(struct perf_callchain_entry_ctx * entry,unsigned long reg29)26cfbcf468SArnaldo Carvalho de Melo static void save_raw_perf_callchain(struct perf_callchain_entry_ctx *entry,
277e788d96SDeng-Cheng Zhu 				    unsigned long reg29)
287e788d96SDeng-Cheng Zhu {
297e788d96SDeng-Cheng Zhu 	unsigned long *sp = (unsigned long *)reg29;
307e788d96SDeng-Cheng Zhu 	unsigned long addr;
317e788d96SDeng-Cheng Zhu 
327e788d96SDeng-Cheng Zhu 	while (!kstack_end(sp)) {
337e788d96SDeng-Cheng Zhu 		addr = *sp++;
347e788d96SDeng-Cheng Zhu 		if (__kernel_text_address(addr)) {
3598f92f2fSDeng-Cheng Zhu 			perf_callchain_store(entry, addr);
363b1fff08SArnaldo Carvalho de Melo 			if (entry->nr >= entry->max_stack)
377e788d96SDeng-Cheng Zhu 				break;
387e788d96SDeng-Cheng Zhu 		}
397e788d96SDeng-Cheng Zhu 	}
407e788d96SDeng-Cheng Zhu }
417e788d96SDeng-Cheng Zhu 
perf_callchain_kernel(struct perf_callchain_entry_ctx * entry,struct pt_regs * regs)42cfbcf468SArnaldo Carvalho de Melo void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
4398f92f2fSDeng-Cheng Zhu 			   struct pt_regs *regs)
447e788d96SDeng-Cheng Zhu {
457e788d96SDeng-Cheng Zhu 	unsigned long sp = regs->regs[29];
467e788d96SDeng-Cheng Zhu #ifdef CONFIG_KALLSYMS
477e788d96SDeng-Cheng Zhu 	unsigned long ra = regs->regs[31];
487e788d96SDeng-Cheng Zhu 	unsigned long pc = regs->cp0_epc;
497e788d96SDeng-Cheng Zhu 
507e788d96SDeng-Cheng Zhu 	if (raw_show_trace || !__kernel_text_address(pc)) {
517e788d96SDeng-Cheng Zhu 		unsigned long stack_page =
527e788d96SDeng-Cheng Zhu 			(unsigned long)task_stack_page(current);
537e788d96SDeng-Cheng Zhu 		if (stack_page && sp >= stack_page &&
547e788d96SDeng-Cheng Zhu 		    sp <= stack_page + THREAD_SIZE - 32)
557e788d96SDeng-Cheng Zhu 			save_raw_perf_callchain(entry, sp);
567e788d96SDeng-Cheng Zhu 		return;
577e788d96SDeng-Cheng Zhu 	}
587e788d96SDeng-Cheng Zhu 	do {
5998f92f2fSDeng-Cheng Zhu 		perf_callchain_store(entry, pc);
603b1fff08SArnaldo Carvalho de Melo 		if (entry->nr >= entry->max_stack)
617e788d96SDeng-Cheng Zhu 			break;
627e788d96SDeng-Cheng Zhu 		pc = unwind_stack(current, &sp, pc, &ra);
637e788d96SDeng-Cheng Zhu 	} while (pc);
647e788d96SDeng-Cheng Zhu #else
657e788d96SDeng-Cheng Zhu 	save_raw_perf_callchain(entry, sp);
667e788d96SDeng-Cheng Zhu #endif
677e788d96SDeng-Cheng Zhu }
68