aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/oprofile
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2005-06-28 16:01:03 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2005-06-28 16:01:03 -0400
commitfa0ebff62a8a25952a082641db0ed6b1cfc6c38c (patch)
treee7600dd94511b958a9fb315b6c0e924e8744f5ae /arch/arm/oprofile
parent99f95e5286df2f69edab8a04c7080d986ee4233b (diff)
[PATCH] ARM: 2761/1: OProfile: Add call graphing support for arm
Patch from Richard Purdie Add functions to generate backtraces of both kernel and user processes which allows oprofile's call graphing functionality to be used on arm. This requires unstripped binaries/libs which use a frame pointer. Signed-off-by: Richard Purdie Signed-off-by: Zwane Mwaikambo <zwane@arm.linux.org.uk> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/oprofile')
-rw-r--r--arch/arm/oprofile/Makefile2
-rw-r--r--arch/arm/oprofile/backtrace.c144
-rw-r--r--arch/arm/oprofile/init.c2
-rw-r--r--arch/arm/oprofile/op_arm_model.h2
4 files changed, 149 insertions, 1 deletions
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile
index ba1a6e9f2b28..8ffb523e6c77 100644
--- a/arch/arm/oprofile/Makefile
+++ b/arch/arm/oprofile/Makefile
@@ -6,6 +6,6 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
6 oprofilefs.o oprofile_stats.o \ 6 oprofilefs.o oprofile_stats.o \
7 timer_int.o ) 7 timer_int.o )
8 8
9oprofile-y := $(DRIVER_OBJS) init.o 9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
10oprofile-$(CONFIG_CPU_XSCALE) += common.o op_model_xscale.o 10oprofile-$(CONFIG_CPU_XSCALE) += common.o op_model_xscale.o
11 11
diff --git a/arch/arm/oprofile/backtrace.c b/arch/arm/oprofile/backtrace.c
new file mode 100644
index 000000000000..ec58d3e2eb8b
--- /dev/null
+++ b/arch/arm/oprofile/backtrace.c
@@ -0,0 +1,144 @@
1/*
2 * Arm specific backtracing code for oprofile
3 *
4 * Copyright 2005 Openedhand Ltd.
5 *
6 * Author: Richard Purdie <rpurdie@openedhand.com>
7 *
8 * Based on i386 oprofile backtrace code by John Levon, David Smith
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15
16#include <linux/oprofile.h>
17#include <linux/sched.h>
18#include <linux/mm.h>
19#include <asm/ptrace.h>
20#include <asm/uaccess.h>
21
22
23/*
24 * The registers we're interested in are at the end of the variable
25 * length saved register structure. The fp points at the end of this
26 * structure so the address of this struct is:
27 * (struct frame_tail *)(xxx->fp)-1
28 */
29struct frame_tail {
30 struct frame_tail *fp;
31 unsigned long sp;
32 unsigned long lr;
33} __attribute__((packed));
34
35
36#ifdef CONFIG_FRAME_POINTER
37static struct frame_tail* kernel_backtrace(struct frame_tail *tail)
38{
39 oprofile_add_trace(tail->lr);
40
41 /* frame pointers should strictly progress back up the stack
42 * (towards higher addresses) */
43 if (tail >= tail->fp)
44 return NULL;
45
46 return tail->fp-1;
47}
48#endif
49
50static struct frame_tail* user_backtrace(struct frame_tail *tail)
51{
52 struct frame_tail buftail;
53
54 /* hardware pte might not be valid due to dirty/accessed bit emulation
55 * so we use copy_from_user and benefit from exception fixups */
56 if (copy_from_user(&buftail, tail, sizeof(struct frame_tail)))
57 return NULL;
58
59 oprofile_add_trace(buftail.lr);
60
61 /* frame pointers should strictly progress back up the stack
62 * (towards higher addresses) */
63 if (tail >= buftail.fp)
64 return NULL;
65
66 return buftail.fp-1;
67}
68
69/* Compare two addresses and see if they're on the same page */
70#define CMP_ADDR_EQUAL(x,y,offset) ((((unsigned long) x) >> PAGE_SHIFT) \
71 == ((((unsigned long) y) + offset) >> PAGE_SHIFT))
72
73/* check that the page(s) containing the frame tail are present */
74static int pages_present(struct frame_tail *tail)
75{
76 struct mm_struct * mm = current->mm;
77
78 if (!check_user_page_readable(mm, (unsigned long)tail))
79 return 0;
80
81 if (CMP_ADDR_EQUAL(tail, tail, 8))
82 return 1;
83
84 if (!check_user_page_readable(mm, ((unsigned long)tail) + 8))
85 return 0;
86
87 return 1;
88}
89
90/*
91 * | | /\ Higher addresses
92 * | |
93 * --------------- stack base (address of current_thread_info)
94 * | thread info |
95 * . .
96 * | stack |
97 * --------------- saved regs->ARM_fp value if valid (frame_tail address)
98 * . .
99 * --------------- struct pt_regs stored on stack (struct pt_regs *)
100 * | |
101 * . .
102 * | |
103 * --------------- %esp
104 * | |
105 * | | \/ Lower addresses
106 *
107 * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
108 */
109static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
110{
111 unsigned long tailaddr = (unsigned long)tail;
112 unsigned long stack = (unsigned long)regs;
113 unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
114
115 return (tailaddr > stack) && (tailaddr < stack_base);
116}
117
118void arm_backtrace(struct pt_regs const *regs, unsigned int depth)
119{
120 struct frame_tail *tail;
121 unsigned long last_address = 0;
122
123 tail = ((struct frame_tail *) regs->ARM_fp) - 1;
124
125 if (!user_mode(regs)) {
126
127#ifdef CONFIG_FRAME_POINTER
128 while (depth-- && tail && valid_kernel_stack(tail, regs)) {
129 tail = kernel_backtrace(tail);
130 }
131#endif
132 return;
133 }
134
135 while (depth-- && tail && !((unsigned long) tail & 3)) {
136 if ((!CMP_ADDR_EQUAL(last_address, tail, 0)
137 || !CMP_ADDR_EQUAL(last_address, tail, 8))
138 && !pages_present(tail))
139 return;
140 last_address = (unsigned long) tail;
141 tail = user_backtrace(tail);
142 }
143}
144
diff --git a/arch/arm/oprofile/init.c b/arch/arm/oprofile/init.c
index cce3d3015eb7..d315a3a86c86 100644
--- a/arch/arm/oprofile/init.c
+++ b/arch/arm/oprofile/init.c
@@ -20,6 +20,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
20 ret = pmu_init(ops, &op_xscale_spec); 20 ret = pmu_init(ops, &op_xscale_spec);
21#endif 21#endif
22 22
23 ops->backtrace = arm_backtrace;
24
23 return ret; 25 return ret;
24} 26}
25 27
diff --git a/arch/arm/oprofile/op_arm_model.h b/arch/arm/oprofile/op_arm_model.h
index 2d4caf4781ad..2148d07484b7 100644
--- a/arch/arm/oprofile/op_arm_model.h
+++ b/arch/arm/oprofile/op_arm_model.h
@@ -24,6 +24,8 @@ struct op_arm_model_spec {
24extern struct op_arm_model_spec op_xscale_spec; 24extern struct op_arm_model_spec op_xscale_spec;
25#endif 25#endif
26 26
27extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth);
28
27extern int __init pmu_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec); 29extern int __init pmu_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec);
28extern void pmu_exit(void); 30extern void pmu_exit(void);
29#endif /* OP_ARM_MODEL_H */ 31#endif /* OP_ARM_MODEL_H */