diff options
Diffstat (limited to 'arch/sh/oprofile/backtrace.c')
-rw-r--r-- | arch/sh/oprofile/backtrace.c | 84 |
1 files changed, 37 insertions, 47 deletions
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c index 9499a2914f89..2bc74de23f08 100644 --- a/arch/sh/oprofile/backtrace.c +++ b/arch/sh/oprofile/backtrace.c | |||
@@ -17,9 +17,43 @@ | |||
17 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
18 | #include <linux/kallsyms.h> | 18 | #include <linux/kallsyms.h> |
19 | #include <linux/mm.h> | 19 | #include <linux/mm.h> |
20 | #include <asm/unwinder.h> | ||
20 | #include <asm/ptrace.h> | 21 | #include <asm/ptrace.h> |
21 | #include <asm/uaccess.h> | 22 | #include <asm/uaccess.h> |
22 | #include <asm/sections.h> | 23 | #include <asm/sections.h> |
24 | #include <asm/stacktrace.h> | ||
25 | |||
26 | static void backtrace_warning_symbol(void *data, char *msg, | ||
27 | unsigned long symbol) | ||
28 | { | ||
29 | /* Ignore warnings */ | ||
30 | } | ||
31 | |||
32 | static void backtrace_warning(void *data, char *msg) | ||
33 | { | ||
34 | /* Ignore warnings */ | ||
35 | } | ||
36 | |||
37 | static int backtrace_stack(void *data, char *name) | ||
38 | { | ||
39 | /* Yes, we want all stacks */ | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static void backtrace_address(void *data, unsigned long addr, int reliable) | ||
44 | { | ||
45 | unsigned int *depth = data; | ||
46 | |||
47 | if ((*depth)--) | ||
48 | oprofile_add_trace(addr); | ||
49 | } | ||
50 | |||
51 | static struct stacktrace_ops backtrace_ops = { | ||
52 | .warning = backtrace_warning, | ||
53 | .warning_symbol = backtrace_warning_symbol, | ||
54 | .stack = backtrace_stack, | ||
55 | .address = backtrace_address, | ||
56 | }; | ||
23 | 57 | ||
24 | /* Limit to stop backtracing too far. */ | 58 | /* Limit to stop backtracing too far. */ |
25 | static int backtrace_limit = 20; | 59 | static int backtrace_limit = 20; |
@@ -47,50 +81,6 @@ user_backtrace(unsigned long *stackaddr, struct pt_regs *regs) | |||
47 | return stackaddr; | 81 | return stackaddr; |
48 | } | 82 | } |
49 | 83 | ||
50 | /* | ||
51 | * | | /\ Higher addresses | ||
52 | * | | | ||
53 | * --------------- stack base (address of current_thread_info) | ||
54 | * | thread info | | ||
55 | * . . | ||
56 | * | stack | | ||
57 | * --------------- saved regs->regs[15] value if valid | ||
58 | * . . | ||
59 | * --------------- struct pt_regs stored on stack (struct pt_regs *) | ||
60 | * | | | ||
61 | * . . | ||
62 | * | | | ||
63 | * --------------- ??? | ||
64 | * | | | ||
65 | * | | \/ Lower addresses | ||
66 | * | ||
67 | * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values | ||
68 | */ | ||
69 | static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs) | ||
70 | { | ||
71 | unsigned long stack = (unsigned long)regs; | ||
72 | unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; | ||
73 | |||
74 | return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base); | ||
75 | } | ||
76 | |||
77 | static unsigned long * | ||
78 | kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs) | ||
79 | { | ||
80 | unsigned long addr; | ||
81 | |||
82 | /* | ||
83 | * If not a valid kernel address, keep going till we find one | ||
84 | * or the SP stops being a valid address. | ||
85 | */ | ||
86 | do { | ||
87 | addr = *stackaddr++; | ||
88 | oprofile_add_trace(addr); | ||
89 | } while (valid_kernel_stack(stackaddr, regs)); | ||
90 | |||
91 | return stackaddr; | ||
92 | } | ||
93 | |||
94 | void sh_backtrace(struct pt_regs * const regs, unsigned int depth) | 84 | void sh_backtrace(struct pt_regs * const regs, unsigned int depth) |
95 | { | 85 | { |
96 | unsigned long *stackaddr; | 86 | unsigned long *stackaddr; |
@@ -103,9 +93,9 @@ void sh_backtrace(struct pt_regs * const regs, unsigned int depth) | |||
103 | 93 | ||
104 | stackaddr = (unsigned long *)regs->regs[15]; | 94 | stackaddr = (unsigned long *)regs->regs[15]; |
105 | if (!user_mode(regs)) { | 95 | if (!user_mode(regs)) { |
106 | while (depth-- && valid_kernel_stack(stackaddr, regs)) | 96 | if (depth) |
107 | stackaddr = kernel_backtrace(stackaddr, regs); | 97 | unwind_stack(NULL, regs, stackaddr, |
108 | 98 | &backtrace_ops, &depth); | |
109 | return; | 99 | return; |
110 | } | 100 | } |
111 | 101 | ||