diff options
author | David S. Miller <davem@davemloft.net> | 2010-01-19 03:26:13 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-20 19:23:02 -0500 |
commit | 4f6dbe4ac01d2664231d3f3eceadd33a44cde993 (patch) | |
tree | 029201f8597df383c5fac18b2693227570b1693e /arch/sparc/kernel | |
parent | 0299b1371d8f1b074c8284a19beb9094ada9405f (diff) |
sparc64: Add perf callchain support.
Pretty straightforward, and it should be easy to add accurate
walk through of signal stack frames in userspace.
Signed-off-by: David S. Miller <davem@davemloft.net>
Tested-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r-- | arch/sparc/kernel/perf_event.c | 120 |
1 files changed, 119 insertions, 1 deletions
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 198fb4e79ba2..2386ac6ec956 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* Performance event support for sparc64. | 1 | /* Performance event support for sparc64. |
2 | * | 2 | * |
3 | * Copyright (C) 2009 David S. Miller <davem@davemloft.net> | 3 | * Copyright (C) 2009, 2010 David S. Miller <davem@davemloft.net> |
4 | * | 4 | * |
5 | * This code is based almost entirely upon the x86 perf event | 5 | * This code is based almost entirely upon the x86 perf event |
6 | * code, which is: | 6 | * code, which is: |
@@ -18,11 +18,15 @@ | |||
18 | #include <linux/kdebug.h> | 18 | #include <linux/kdebug.h> |
19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
20 | 20 | ||
21 | #include <asm/stacktrace.h> | ||
21 | #include <asm/cpudata.h> | 22 | #include <asm/cpudata.h> |
23 | #include <asm/uaccess.h> | ||
22 | #include <asm/atomic.h> | 24 | #include <asm/atomic.h> |
23 | #include <asm/nmi.h> | 25 | #include <asm/nmi.h> |
24 | #include <asm/pcr.h> | 26 | #include <asm/pcr.h> |
25 | 27 | ||
28 | #include "kstack.h" | ||
29 | |||
26 | /* Sparc64 chips have two performance counters, 32-bits each, with | 30 | /* Sparc64 chips have two performance counters, 32-bits each, with |
27 | * overflow interrupts generated on transition from 0xffffffff to 0. | 31 | * overflow interrupts generated on transition from 0xffffffff to 0. |
28 | * The counters are accessed in one go using a 64-bit register. | 32 | * The counters are accessed in one go using a 64-bit register. |
@@ -1062,3 +1066,117 @@ void __init init_hw_perf_events(void) | |||
1062 | 1066 | ||
1063 | register_die_notifier(&perf_event_nmi_notifier); | 1067 | register_die_notifier(&perf_event_nmi_notifier); |
1064 | } | 1068 | } |
1069 | |||
1070 | static inline void callchain_store(struct perf_callchain_entry *entry, u64 ip) | ||
1071 | { | ||
1072 | if (entry->nr < PERF_MAX_STACK_DEPTH) | ||
1073 | entry->ip[entry->nr++] = ip; | ||
1074 | } | ||
1075 | |||
1076 | static void perf_callchain_kernel(struct pt_regs *regs, | ||
1077 | struct perf_callchain_entry *entry) | ||
1078 | { | ||
1079 | unsigned long ksp, fp; | ||
1080 | |||
1081 | callchain_store(entry, PERF_CONTEXT_KERNEL); | ||
1082 | callchain_store(entry, regs->tpc); | ||
1083 | |||
1084 | ksp = regs->u_regs[UREG_I6]; | ||
1085 | fp = ksp + STACK_BIAS; | ||
1086 | do { | ||
1087 | struct sparc_stackf *sf; | ||
1088 | struct pt_regs *regs; | ||
1089 | unsigned long pc; | ||
1090 | |||
1091 | if (!kstack_valid(current_thread_info(), fp)) | ||
1092 | break; | ||
1093 | |||
1094 | sf = (struct sparc_stackf *) fp; | ||
1095 | regs = (struct pt_regs *) (sf + 1); | ||
1096 | |||
1097 | if (kstack_is_trap_frame(current_thread_info(), regs)) { | ||
1098 | if (user_mode(regs)) | ||
1099 | break; | ||
1100 | pc = regs->tpc; | ||
1101 | fp = regs->u_regs[UREG_I6] + STACK_BIAS; | ||
1102 | } else { | ||
1103 | pc = sf->callers_pc; | ||
1104 | fp = (unsigned long)sf->fp + STACK_BIAS; | ||
1105 | } | ||
1106 | callchain_store(entry, pc); | ||
1107 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
1108 | } | ||
1109 | |||
1110 | static void perf_callchain_user_64(struct pt_regs *regs, | ||
1111 | struct perf_callchain_entry *entry) | ||
1112 | { | ||
1113 | unsigned long ufp; | ||
1114 | |||
1115 | callchain_store(entry, PERF_CONTEXT_USER); | ||
1116 | callchain_store(entry, regs->tpc); | ||
1117 | |||
1118 | ufp = regs->u_regs[UREG_I6] + STACK_BIAS; | ||
1119 | do { | ||
1120 | struct sparc_stackf *usf, sf; | ||
1121 | unsigned long pc; | ||
1122 | |||
1123 | usf = (struct sparc_stackf *) ufp; | ||
1124 | if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) | ||
1125 | break; | ||
1126 | |||
1127 | pc = sf.callers_pc; | ||
1128 | ufp = (unsigned long)sf.fp + STACK_BIAS; | ||
1129 | callchain_store(entry, pc); | ||
1130 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
1131 | } | ||
1132 | |||
1133 | static void perf_callchain_user_32(struct pt_regs *regs, | ||
1134 | struct perf_callchain_entry *entry) | ||
1135 | { | ||
1136 | unsigned long ufp; | ||
1137 | |||
1138 | callchain_store(entry, PERF_CONTEXT_USER); | ||
1139 | callchain_store(entry, regs->tpc); | ||
1140 | |||
1141 | ufp = regs->u_regs[UREG_I6]; | ||
1142 | do { | ||
1143 | struct sparc_stackf32 *usf, sf; | ||
1144 | unsigned long pc; | ||
1145 | |||
1146 | usf = (struct sparc_stackf32 *) ufp; | ||
1147 | if (__copy_from_user_inatomic(&sf, usf, sizeof(sf))) | ||
1148 | break; | ||
1149 | |||
1150 | pc = sf.callers_pc; | ||
1151 | ufp = (unsigned long)sf.fp; | ||
1152 | callchain_store(entry, pc); | ||
1153 | } while (entry->nr < PERF_MAX_STACK_DEPTH); | ||
1154 | } | ||
1155 | |||
1156 | /* Like powerpc we can't get PMU interrupts within the PMU handler, | ||
1157 | * so no need for seperate NMI and IRQ chains as on x86. | ||
1158 | */ | ||
1159 | static DEFINE_PER_CPU(struct perf_callchain_entry, callchain); | ||
1160 | |||
1161 | struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | ||
1162 | { | ||
1163 | struct perf_callchain_entry *entry = &__get_cpu_var(callchain); | ||
1164 | |||
1165 | entry->nr = 0; | ||
1166 | if (!user_mode(regs)) { | ||
1167 | stack_trace_flush(); | ||
1168 | perf_callchain_kernel(regs, entry); | ||
1169 | if (current->mm) | ||
1170 | regs = task_pt_regs(current); | ||
1171 | else | ||
1172 | regs = NULL; | ||
1173 | } | ||
1174 | if (regs) { | ||
1175 | flushw_user(); | ||
1176 | if (test_thread_flag(TIF_32BIT)) | ||
1177 | perf_callchain_user_32(regs, entry); | ||
1178 | else | ||
1179 | perf_callchain_user_64(regs, entry); | ||
1180 | } | ||
1181 | return entry; | ||
1182 | } | ||