Commit | Line | Data |
---|---|---|
14f70012 DCZ |
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 | |
7e788d96 DCZ |
9 | * counter access is based on the MIPS Oprofile code. And the callchain |
10 | * support references the code of MIPS stacktrace.c. | |
14f70012 DCZ |
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 | ||
14f70012 | 17 | #include <linux/perf_event.h> |
14f70012 | 18 | |
14f70012 | 19 | #include <asm/stacktrace.h> |
3a9ab99e | 20 | |
7e788d96 | 21 | /* Callchain handling code. */ |
7e788d96 DCZ |
22 | |
23 | /* | |
24 | * Leave userspace callchain empty for now. When we find a way to trace | |
e5dcb58a | 25 | * the user stack callchains, we will add it here. |
7e788d96 | 26 | */ |
7e788d96 DCZ |
27 | |
28 | static 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)) { | |
98f92f2f | 37 | perf_callchain_store(entry, addr); |
c5dfd78e | 38 | if (entry->nr >= sysctl_perf_event_max_stack) |
7e788d96 DCZ |
39 | break; |
40 | } | |
41 | } | |
42 | } | |
43 | ||
98f92f2f DCZ |
44 | void perf_callchain_kernel(struct perf_callchain_entry *entry, |
45 | struct pt_regs *regs) | |
7e788d96 DCZ |
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 | ||
7e788d96 DCZ |
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 { | |
98f92f2f | 61 | perf_callchain_store(entry, pc); |
c5dfd78e | 62 | if (entry->nr >= sysctl_perf_event_max_stack) |
7e788d96 DCZ |
63 | break; |
64 | pc = unwind_stack(current, &sp, pc, &ra); | |
65 | } while (pc); | |
66 | #else | |
7e788d96 DCZ |
67 | save_raw_perf_callchain(entry, sp); |
68 | #endif | |
69 | } |