aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/oprofile
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/oprofile')
-rw-r--r--arch/ia64/oprofile/Kconfig26
-rw-r--r--arch/ia64/oprofile/Makefile10
-rw-r--r--arch/ia64/oprofile/backtrace.c150
-rw-r--r--arch/ia64/oprofile/init.c38
-rw-r--r--arch/ia64/oprofile/perfmon.c100
5 files changed, 324 insertions, 0 deletions
diff --git a/arch/ia64/oprofile/Kconfig b/arch/ia64/oprofile/Kconfig
new file mode 100644
index 000000000000..56e6f614b04a
--- /dev/null
+++ b/arch/ia64/oprofile/Kconfig
@@ -0,0 +1,26 @@
1
2menu "Profiling support"
3 depends on EXPERIMENTAL
4
5config PROFILING
6 bool "Profiling support (EXPERIMENTAL)"
7 help
8 Say Y here to enable the extended profiling support mechanisms used
9 by profilers such as OProfile.
10
11config OPROFILE
12 tristate "OProfile system profiling (EXPERIMENTAL)"
13 depends on PROFILING
14 help
15 OProfile is a profiling system capable of profiling the
16 whole system, include the kernel, kernel modules, libraries,
17 and applications.
18
19 Due to firmware bugs, you may need to use the "nohalt" boot
20 option if you're using OProfile with the hardware performance
21 counters.
22
23 If unsure, say N.
24
25endmenu
26
diff --git a/arch/ia64/oprofile/Makefile b/arch/ia64/oprofile/Makefile
new file mode 100644
index 000000000000..aad27a718ee0
--- /dev/null
+++ b/arch/ia64/oprofile/Makefile
@@ -0,0 +1,10 @@
1obj-$(CONFIG_OPROFILE) += oprofile.o
2
3DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \
7 timer_int.o )
8
9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
10oprofile-$(CONFIG_PERFMON) += perfmon.o
diff --git a/arch/ia64/oprofile/backtrace.c b/arch/ia64/oprofile/backtrace.c
new file mode 100644
index 000000000000..b7dabbfb0d61
--- /dev/null
+++ b/arch/ia64/oprofile/backtrace.c
@@ -0,0 +1,150 @@
1/**
2 * @file backtrace.c
3 *
4 * @remark Copyright 2004 Silicon Graphics Inc. All Rights Reserved.
5 * @remark Read the file COPYING
6 *
7 * @author Greg Banks <gnb@melbourne.sgi.com>
8 * @author Keith Owens <kaos@melbourne.sgi.com>
9 * Based on work done for the ia64 port of the SGI kernprof patch, which is
10 * Copyright (c) 2003-2004 Silicon Graphics Inc. All Rights Reserved.
11 */
12
13#include <linux/oprofile.h>
14#include <linux/sched.h>
15#include <linux/mm.h>
16#include <asm/ptrace.h>
17#include <asm/system.h>
18
19/*
20 * For IA64 we need to perform a complex little dance to get both
21 * the struct pt_regs and a synthetic struct switch_stack in place
22 * to allow the unwind code to work. This dance requires our unwind
23 * using code to be called from a function called from unw_init_running().
24 * There we only get a single void* data pointer, so use this struct
25 * to hold all the data we need during the unwind.
26 */
27typedef struct
28{
29 unsigned int depth;
30 struct pt_regs *regs;
31 struct unw_frame_info frame;
32 u64 *prev_pfs_loc; /* state for WAR for old spinlock ool code */
33} ia64_backtrace_t;
34
35#if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
36/*
37 * Returns non-zero if the PC is in the spinlock contention out-of-line code
38 * with non-standard calling sequence (on older compilers).
39 */
40static __inline__ int in_old_ool_spinlock_code(unsigned long pc)
41{
42 extern const char ia64_spinlock_contention_pre3_4[] __attribute__ ((weak));
43 extern const char ia64_spinlock_contention_pre3_4_end[] __attribute__ ((weak));
44 unsigned long sc_start = (unsigned long)ia64_spinlock_contention_pre3_4;
45 unsigned long sc_end = (unsigned long)ia64_spinlock_contention_pre3_4_end;
46 return (sc_start && sc_end && pc >= sc_start && pc < sc_end);
47}
48#else
49/* Newer spinlock code does a proper br.call and works fine with the unwinder */
50#define in_old_ool_spinlock_code(pc) 0
51#endif
52
53/* Returns non-zero if the PC is in the Interrupt Vector Table */
54static __inline__ int in_ivt_code(unsigned long pc)
55{
56 extern char ia64_ivt[];
57 return (pc >= (u_long)ia64_ivt && pc < (u_long)ia64_ivt+32768);
58}
59
60/*
61 * Unwind to next stack frame.
62 */
63static __inline__ int next_frame(ia64_backtrace_t *bt)
64{
65 /*
66 * Avoid unsightly console message from unw_unwind() when attempting
67 * to unwind through the Interrupt Vector Table which has no unwind
68 * information.
69 */
70 if (in_ivt_code(bt->frame.ip))
71 return 0;
72
73 /*
74 * WAR for spinlock contention from leaf functions. ia64_spinlock_contention_pre3_4
75 * has ar.pfs == r0. Leaf functions do not modify ar.pfs so ar.pfs remains
76 * as 0, stopping the backtrace. Record the previous ar.pfs when the current
77 * IP is in ia64_spinlock_contention_pre3_4 then unwind, if pfs_loc has not changed
78 * after unwind then use pt_regs.ar_pfs which is where the real ar.pfs is for
79 * leaf functions.
80 */
81 if (bt->prev_pfs_loc && bt->regs && bt->frame.pfs_loc == bt->prev_pfs_loc)
82 bt->frame.pfs_loc = &bt->regs->ar_pfs;
83 bt->prev_pfs_loc = (in_old_ool_spinlock_code(bt->frame.ip) ? bt->frame.pfs_loc : NULL);
84
85 return unw_unwind(&bt->frame) == 0;
86}
87
88
89static void do_ia64_backtrace(struct unw_frame_info *info, void *vdata)
90{
91 ia64_backtrace_t *bt = vdata;
92 struct switch_stack *sw;
93 int count = 0;
94 u_long pc, sp;
95
96 sw = (struct switch_stack *)(info+1);
97 /* padding from unw_init_running */
98 sw = (struct switch_stack *)(((unsigned long)sw + 15) & ~15);
99
100 unw_init_frame_info(&bt->frame, current, sw);
101
102 /* skip over interrupt frame and oprofile calls */
103 do {
104 unw_get_sp(&bt->frame, &sp);
105 if (sp >= (u_long)bt->regs)
106 break;
107 if (!next_frame(bt))
108 return;
109 } while (count++ < 200);
110
111 /* finally, grab the actual sample */
112 while (bt->depth-- && next_frame(bt)) {
113 unw_get_ip(&bt->frame, &pc);
114 oprofile_add_trace(pc);
115 if (unw_is_intr_frame(&bt->frame)) {
116 /*
117 * Interrupt received on kernel stack; this can
118 * happen when timer interrupt fires while processing
119 * a softirq from the tail end of a hardware interrupt
120 * which interrupted a system call. Don't laugh, it
121 * happens! Splice the backtrace into two parts to
122 * avoid spurious cycles in the gprof output.
123 */
124 /* TODO: split rather than drop the 2nd half */
125 break;
126 }
127 }
128}
129
130void
131ia64_backtrace(struct pt_regs * const regs, unsigned int depth)
132{
133 ia64_backtrace_t bt;
134 unsigned long flags;
135
136 /*
137 * On IA64 there is little hope of getting backtraces from
138 * user space programs -- the problems of getting the unwind
139 * information from arbitrary user programs are extreme.
140 */
141 if (user_mode(regs))
142 return;
143
144 bt.depth = depth;
145 bt.regs = regs;
146 bt.prev_pfs_loc = NULL;
147 local_irq_save(flags);
148 unw_init_running(do_ia64_backtrace, &bt);
149 local_irq_restore(flags);
150}
diff --git a/arch/ia64/oprofile/init.c b/arch/ia64/oprofile/init.c
new file mode 100644
index 000000000000..125a602a660d
--- /dev/null
+++ b/arch/ia64/oprofile/init.c
@@ -0,0 +1,38 @@
1/**
2 * @file init.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon <levon@movementarian.org>
8 */
9
10#include <linux/kernel.h>
11#include <linux/oprofile.h>
12#include <linux/init.h>
13#include <linux/errno.h>
14
15extern int perfmon_init(struct oprofile_operations * ops);
16extern void perfmon_exit(void);
17extern void ia64_backtrace(struct pt_regs * const regs, unsigned int depth);
18
19int __init oprofile_arch_init(struct oprofile_operations * ops)
20{
21 int ret = -ENODEV;
22
23#ifdef CONFIG_PERFMON
24 /* perfmon_init() can fail, but we have no way to report it */
25 ret = perfmon_init(ops);
26#endif
27 ops->backtrace = ia64_backtrace;
28
29 return ret;
30}
31
32
33void oprofile_arch_exit(void)
34{
35#ifdef CONFIG_PERFMON
36 perfmon_exit();
37#endif
38}
diff --git a/arch/ia64/oprofile/perfmon.c b/arch/ia64/oprofile/perfmon.c
new file mode 100644
index 000000000000..b7975a469fb8
--- /dev/null
+++ b/arch/ia64/oprofile/perfmon.c
@@ -0,0 +1,100 @@
1/**
2 * @file perfmon.c
3 *
4 * @remark Copyright 2003 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon <levon@movementarian.org>
8 */
9
10#include <linux/kernel.h>
11#include <linux/config.h>
12#include <linux/oprofile.h>
13#include <linux/sched.h>
14#include <asm/perfmon.h>
15#include <asm/ptrace.h>
16#include <asm/errno.h>
17
18static int allow_ints;
19
20static int
21perfmon_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg,
22 struct pt_regs *regs, unsigned long stamp)
23{
24 int event = arg->pmd_eventid;
25
26 arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1;
27
28 /* the owner of the oprofile event buffer may have exited
29 * without perfmon being shutdown (e.g. SIGSEGV)
30 */
31 if (allow_ints)
32 oprofile_add_sample(regs, event);
33 return 0;
34}
35
36
37static int perfmon_start(void)
38{
39 allow_ints = 1;
40 return 0;
41}
42
43
44static void perfmon_stop(void)
45{
46 allow_ints = 0;
47}
48
49
50#define OPROFILE_FMT_UUID { \
51 0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69, 0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c }
52
53static pfm_buffer_fmt_t oprofile_fmt = {
54 .fmt_name = "oprofile_format",
55 .fmt_uuid = OPROFILE_FMT_UUID,
56 .fmt_handler = perfmon_handler,
57};
58
59
60static char * get_cpu_type(void)
61{
62 __u8 family = local_cpu_data->family;
63
64 switch (family) {
65 case 0x07:
66 return "ia64/itanium";
67 case 0x1f:
68 return "ia64/itanium2";
69 default:
70 return "ia64/ia64";
71 }
72}
73
74
75/* all the ops are handled via userspace for IA64 perfmon */
76
77static int using_perfmon;
78
79int perfmon_init(struct oprofile_operations * ops)
80{
81 int ret = pfm_register_buffer_fmt(&oprofile_fmt);
82 if (ret)
83 return -ENODEV;
84
85 ops->cpu_type = get_cpu_type();
86 ops->start = perfmon_start;
87 ops->stop = perfmon_stop;
88 using_perfmon = 1;
89 printk(KERN_INFO "oprofile: using perfmon.\n");
90 return 0;
91}
92
93
94void perfmon_exit(void)
95{
96 if (!using_perfmon)
97 return;
98
99 pfm_unregister_buffer_fmt(oprofile_fmt.fmt_uuid);
100}