Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /** |
2 | * @file backtrace.c | |
3 | * | |
4 | * @remark Copyright 2002 OProfile authors | |
5 | * @remark Read the file COPYING | |
6 | * | |
7 | * @author John Levon | |
8 | * @author David Smith | |
9 | */ | |
10 | ||
11 | #include <linux/oprofile.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/mm.h> | |
14 | #include <asm/ptrace.h> | |
c34d1b4d | 15 | #include <asm/uaccess.h> |
574a6042 | 16 | #include <asm/stacktrace.h> |
1da177e4 | 17 | |
574a6042 JB |
18 | static void backtrace_warning_symbol(void *data, char *msg, |
19 | unsigned long symbol) | |
20 | { | |
21 | /* Ignore warnings */ | |
22 | } | |
1da177e4 | 23 | |
574a6042 | 24 | static void backtrace_warning(void *data, char *msg) |
30379440 | 25 | { |
574a6042 JB |
26 | /* Ignore warnings */ |
27 | } | |
30379440 | 28 | |
574a6042 JB |
29 | static int backtrace_stack(void *data, char *name) |
30 | { | |
31 | /* Yes, we want all stacks */ | |
32 | return 0; | |
33 | } | |
30379440 | 34 | |
bc850d6b | 35 | static void backtrace_address(void *data, unsigned long addr, int reliable) |
574a6042 JB |
36 | { |
37 | unsigned int *depth = data; | |
38 | ||
39 | if ((*depth)--) | |
40 | oprofile_add_trace(addr); | |
30379440 GB |
41 | } |
42 | ||
574a6042 | 43 | static struct stacktrace_ops backtrace_ops = { |
61c1917f FW |
44 | .warning = backtrace_warning, |
45 | .warning_symbol = backtrace_warning_symbol, | |
46 | .stack = backtrace_stack, | |
47 | .address = backtrace_address, | |
48 | .walk_stack = print_context_stack, | |
574a6042 JB |
49 | }; |
50 | ||
51 | struct frame_head { | |
65ea5b03 | 52 | struct frame_head *bp; |
574a6042 JB |
53 | unsigned long ret; |
54 | } __attribute__((packed)); | |
55 | ||
0f019cc4 | 56 | static struct frame_head *dump_user_backtrace(struct frame_head *head) |
1da177e4 | 57 | { |
c34d1b4d | 58 | struct frame_head bufhead[2]; |
1da177e4 | 59 | |
c34d1b4d HD |
60 | /* Also check accessibility of one struct frame_head beyond */ |
61 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
62 | return NULL; | |
63 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
1da177e4 LT |
64 | return NULL; |
65 | ||
c34d1b4d | 66 | oprofile_add_trace(bufhead[0].ret); |
1da177e4 | 67 | |
c34d1b4d HD |
68 | /* frame pointers should strictly progress back up the stack |
69 | * (towards higher addresses) */ | |
65ea5b03 | 70 | if (head >= bufhead[0].bp) |
c34d1b4d | 71 | return NULL; |
1da177e4 | 72 | |
65ea5b03 | 73 | return bufhead[0].bp; |
1da177e4 LT |
74 | } |
75 | ||
1da177e4 LT |
76 | void |
77 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |
78 | { | |
f1df280f | 79 | struct frame_head *head = (struct frame_head *)frame_pointer(regs); |
1da177e4 | 80 | |
fa1e1bdf | 81 | if (!user_mode_vm(regs)) { |
7b6c6c77 | 82 | unsigned long stack = kernel_stack_pointer(regs); |
574a6042 | 83 | if (depth) |
5bc27dc2 | 84 | dump_trace(NULL, regs, (unsigned long *)stack, 0, |
574a6042 | 85 | &backtrace_ops, &depth); |
1da177e4 LT |
86 | return; |
87 | } | |
88 | ||
c34d1b4d | 89 | while (depth-- && head) |
30379440 | 90 | head = dump_user_backtrace(head); |
1da177e4 | 91 | } |