diff options
author | Wu Zhangjin <wuzhangjin@gmail.com> | 2009-11-20 07:34:34 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-12-16 20:57:25 -0500 |
commit | 29c5d3462f7c8f17bb9e0a29f0a299036468074d (patch) | |
tree | ea18e1a1011e27095b1f2b7b68f26e28573d47eb /arch/mips/kernel/ftrace.c | |
parent | 8f99a162653531ef25a3dd0f92bfb6332cd2b295 (diff) |
MIPS: Tracing: Add function graph tracer support for MIPS
The implementation of function graph tracer for MIPS is a little
different from X86.
in MIPS, gcc(with -pg) only transfer the caller's return address(at) and
the _mcount's return address(ra) to us.
For the kernel part without -mlong-calls:
move at, ra
jal _mcount
For the module part with -mlong-calls:
lui v1, hi16bit_of_mcount
addiu v1, v1, low16bit_of_mcount
move at, ra
jal _mcount
Without -mlong-calls,
if the function is a leaf, it will not save the return address(ra):
ffffffff80101298 <au1k_wait>:
ffffffff80101298: 67bdfff0 daddiu sp,sp,-16
ffffffff8010129c: ffbe0008 sd s8,8(sp)
ffffffff801012a0: 03a0f02d move s8,sp
ffffffff801012a4: 03e0082d move at,ra
ffffffff801012a8: 0c042930 jal ffffffff8010a4c0 <_mcount>
ffffffff801012ac: 00020021 nop
so, we can hijack it directly in _mcount, but if the function is non-leaf, the
return address is saved in the stack.
ffffffff80133030 <copy_process>:
ffffffff80133030: 67bdff50 daddiu sp,sp,-176
ffffffff80133034: ffbe00a0 sd s8,160(sp)
ffffffff80133038: 03a0f02d move s8,sp
ffffffff8013303c: ffbf00a8 sd ra,168(sp)
ffffffff80133040: ffb70098 sd s7,152(sp)
ffffffff80133044: ffb60090 sd s6,144(sp)
ffffffff80133048: ffb50088 sd s5,136(sp)
ffffffff8013304c: ffb40080 sd s4,128(sp)
ffffffff80133050: ffb30078 sd s3,120(sp)
ffffffff80133054: ffb20070 sd s2,112(sp)
ffffffff80133058: ffb10068 sd s1,104(sp)
ffffffff8013305c: ffb00060 sd s0,96(sp)
ffffffff80133060: 03e0082d move at,ra
ffffffff80133064: 0c042930 jal ffffffff8010a4c0 <_mcount>
ffffffff80133068: 00020021 nop
but we can not get the exact stack address(which saved ra) directly in
_mcount, we need to search the content of at register in the stack space
or search the "s{d,w} ra, offset(sp)" instruction in the text. 'Cause we
can not prove there is only a match in the stack space, so, we search
the text instead.
as we can see, if the first instruction above "move at, ra" is not a
store instruction, there should be a leaf function, so we hijack the at
register directly via putting &return_to_handler into it, otherwise, we
search the "s{d,w} ra, offset(sp)" instruction to get the stack offset,
and then the stack address. we use the above copy_process() as an
example, we at last find "ffbf00a8", 0xa8 is the stack offset, we plus
it with s8(fp), that is the stack address, we hijack the content via
writing the &return_to_handler in.
If with -mlong-calls, since there are two more instructions above "move
at, ra", so, we can move the pointer to the position above "lui v1,
hi16bit_of_mcount".
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Nicholas Mc Guire <der.herr@hofr.at>
Cc: zhangfx@lemote.com
Cc: Wu Zhangjin <wuzhangjin@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Patchwork: http://patchwork.linux-mips.org/patch/677/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/ftrace.c')
-rw-r--r-- | arch/mips/kernel/ftrace.c | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c index 5459a7838370..65a3f8a89927 100644 --- a/arch/mips/kernel/ftrace.c +++ b/arch/mips/kernel/ftrace.c | |||
@@ -13,6 +13,8 @@ | |||
13 | #include <linux/ftrace.h> | 13 | #include <linux/ftrace.h> |
14 | 14 | ||
15 | #include <asm/cacheflush.h> | 15 | #include <asm/cacheflush.h> |
16 | #include <asm/asm.h> | ||
17 | #include <asm/asm-offsets.h> | ||
16 | 18 | ||
17 | #ifdef CONFIG_DYNAMIC_FTRACE | 19 | #ifdef CONFIG_DYNAMIC_FTRACE |
18 | 20 | ||
@@ -110,3 +112,107 @@ int __init ftrace_dyn_arch_init(void *data) | |||
110 | return 0; | 112 | return 0; |
111 | } | 113 | } |
112 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 114 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
115 | |||
116 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
117 | |||
118 | #define S_RA_SP (0xafbf << 16) /* s{d,w} ra, offset(sp) */ | ||
119 | #define S_R_SP (0xafb0 << 16) /* s{d,w} R, offset(sp) */ | ||
120 | #define OFFSET_MASK 0xffff /* stack offset range: 0 ~ PT_SIZE */ | ||
121 | |||
122 | unsigned long ftrace_get_parent_addr(unsigned long self_addr, | ||
123 | unsigned long parent, | ||
124 | unsigned long parent_addr, | ||
125 | unsigned long fp) | ||
126 | { | ||
127 | unsigned long sp, ip, ra; | ||
128 | unsigned int code; | ||
129 | |||
130 | /* in module or kernel? */ | ||
131 | if (self_addr & 0x40000000) { | ||
132 | /* module: move to the instruction "lui v1, HI_16BIT_OF_MCOUNT" */ | ||
133 | ip = self_addr - 20; | ||
134 | } else { | ||
135 | /* kernel: move to the instruction "move ra, at" */ | ||
136 | ip = self_addr - 12; | ||
137 | } | ||
138 | |||
139 | /* search the text until finding the non-store instruction or "s{d,w} | ||
140 | * ra, offset(sp)" instruction */ | ||
141 | do { | ||
142 | ip -= 4; | ||
143 | |||
144 | /* get the code at "ip" */ | ||
145 | code = *(unsigned int *)ip; | ||
146 | |||
147 | /* If we hit the non-store instruction before finding where the | ||
148 | * ra is stored, then this is a leaf function and it does not | ||
149 | * store the ra on the stack. */ | ||
150 | if ((code & S_R_SP) != S_R_SP) | ||
151 | return parent_addr; | ||
152 | |||
153 | } while (((code & S_RA_SP) != S_RA_SP)); | ||
154 | |||
155 | sp = fp + (code & OFFSET_MASK); | ||
156 | ra = *(unsigned long *)sp; | ||
157 | |||
158 | if (ra == parent) | ||
159 | return sp; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Hook the return address and push it in the stack of return addrs | ||
166 | * in current thread info. | ||
167 | */ | ||
168 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | ||
169 | unsigned long fp) | ||
170 | { | ||
171 | unsigned long old; | ||
172 | struct ftrace_graph_ent trace; | ||
173 | unsigned long return_hooker = (unsigned long) | ||
174 | &return_to_handler; | ||
175 | |||
176 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | ||
177 | return; | ||
178 | |||
179 | /* "parent" is the stack address saved the return address of the caller | ||
180 | * of _mcount, for a leaf function not save the return address in the | ||
181 | * stack address, so, we "emulate" one in _mcount's stack space, and | ||
182 | * hijack it directly, but for a non-leaf function, it will save the | ||
183 | * return address to the its stack space, so, we can not hijack the | ||
184 | * "parent" directly, but need to find the real stack address, | ||
185 | * ftrace_get_parent_addr() does it! | ||
186 | */ | ||
187 | |||
188 | old = *parent; | ||
189 | |||
190 | parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old, | ||
191 | (unsigned long)parent, | ||
192 | fp); | ||
193 | |||
194 | /* If fails when getting the stack address of the non-leaf function's | ||
195 | * ra, stop function graph tracer and return */ | ||
196 | if (parent == 0) { | ||
197 | ftrace_graph_stop(); | ||
198 | WARN_ON(1); | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | *parent = return_hooker; | ||
203 | |||
204 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, fp) == | ||
205 | -EBUSY) { | ||
206 | *parent = old; | ||
207 | return; | ||
208 | } | ||
209 | |||
210 | trace.func = self_addr; | ||
211 | |||
212 | /* Only trace if the calling function expects to */ | ||
213 | if (!ftrace_graph_entry(&trace)) { | ||
214 | current->curr_ret_stack--; | ||
215 | *parent = old; | ||
216 | } | ||
217 | } | ||
218 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||