aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2010-01-19 03:26:13 -0500
committerDavid S. Miller <davem@davemloft.net>2010-01-20 19:23:02 -0500
commit4f6dbe4ac01d2664231d3f3eceadd33a44cde993 (patch)
tree029201f8597df383c5fac18b2693227570b1693e /arch/sparc/kernel
parent0299b1371d8f1b074c8284a19beb9094ada9405f (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.c120
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
1070static 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
1076static 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
1110static 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
1133static 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 */
1159static DEFINE_PER_CPU(struct perf_callchain_entry, callchain);
1160
1161struct 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}