Commit | Line | Data |
---|---|---|
40a8b421 DP |
1 | /* |
2 | * SH specific backtracing code for oprofile | |
3 | * | |
4 | * Copyright 2007 STMicroelectronics Ltd. | |
5 | * | |
6 | * Author: Dave Peverley <dpeverley@mpc-data.co.uk> | |
7 | * | |
8 | * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386 | |
9 | * oprofile backtrace code by John Levon, David Smith | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | * | |
15 | */ | |
16 | #include <linux/oprofile.h> | |
17 | #include <linux/sched.h> | |
18 | #include <linux/kallsyms.h> | |
19 | #include <linux/mm.h> | |
0eff9f66 | 20 | #include <asm/unwinder.h> |
40a8b421 DP |
21 | #include <asm/ptrace.h> |
22 | #include <asm/uaccess.h> | |
23 | #include <asm/sections.h> | |
4e14dfc7 MF |
24 | #include <asm/stacktrace.h> |
25 | ||
4e14dfc7 MF |
26 | static int backtrace_stack(void *data, char *name) |
27 | { | |
28 | /* Yes, we want all stacks */ | |
29 | return 0; | |
30 | } | |
31 | ||
32 | static void backtrace_address(void *data, unsigned long addr, int reliable) | |
33 | { | |
34 | unsigned int *depth = data; | |
35 | ||
36 | if ((*depth)--) | |
37 | oprofile_add_trace(addr); | |
38 | } | |
39 | ||
40 | static struct stacktrace_ops backtrace_ops = { | |
4e14dfc7 MF |
41 | .stack = backtrace_stack, |
42 | .address = backtrace_address, | |
43 | }; | |
40a8b421 DP |
44 | |
45 | /* Limit to stop backtracing too far. */ | |
46 | static int backtrace_limit = 20; | |
47 | ||
48 | static unsigned long * | |
49 | user_backtrace(unsigned long *stackaddr, struct pt_regs *regs) | |
50 | { | |
51 | unsigned long buf_stack; | |
52 | ||
53 | /* Also check accessibility of address */ | |
54 | if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long))) | |
55 | return NULL; | |
56 | ||
57 | if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long))) | |
58 | return NULL; | |
59 | ||
60 | /* Quick paranoia check */ | |
61 | if (buf_stack & 3) | |
62 | return NULL; | |
63 | ||
64 | oprofile_add_trace(buf_stack); | |
65 | ||
66 | stackaddr++; | |
67 | ||
68 | return stackaddr; | |
69 | } | |
70 | ||
40a8b421 DP |
71 | void sh_backtrace(struct pt_regs * const regs, unsigned int depth) |
72 | { | |
73 | unsigned long *stackaddr; | |
74 | ||
75 | /* | |
76 | * Paranoia - clip max depth as we could get lost in the weeds. | |
77 | */ | |
78 | if (depth > backtrace_limit) | |
79 | depth = backtrace_limit; | |
80 | ||
d1ba71f7 | 81 | stackaddr = (unsigned long *)kernel_stack_pointer(regs); |
40a8b421 | 82 | if (!user_mode(regs)) { |
4e14dfc7 | 83 | if (depth) |
0eff9f66 MF |
84 | unwind_stack(NULL, regs, stackaddr, |
85 | &backtrace_ops, &depth); | |
40a8b421 DP |
86 | return; |
87 | } | |
88 | ||
89 | while (depth-- && (stackaddr != NULL)) | |
90 | stackaddr = user_backtrace(stackaddr, regs); | |
91 | } |