blob: a2488b6e27d6c20aefcfff006ae63da03eec9b22 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**
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>
Robert Richtera0e3e702011-06-03 16:37:47 +020014#include <linux/compat.h>
Robert Richter1ac2e6c2011-06-07 11:49:55 +020015#include <linux/uaccess.h>
Robert Richtera0e3e702011-06-03 16:37:47 +020016
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <asm/ptrace.h>
Jan Blunck574a6042007-10-19 20:35:03 +020018#include <asm/stacktrace.h>
Josh Poimboeufec2ad9c2016-09-16 14:18:15 -050019#include <asm/unwind.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
Jiri Olsaf6dedec2010-09-29 10:46:47 -040021#ifdef CONFIG_COMPAT
22static struct stack_frame_ia32 *
23dump_user_backtrace_32(struct stack_frame_ia32 *head)
24{
Robert Richtera0e3e702011-06-03 16:37:47 +020025 /* Also check accessibility of one struct frame_head beyond: */
Jiri Olsaf6dedec2010-09-29 10:46:47 -040026 struct stack_frame_ia32 bufhead[2];
27 struct stack_frame_ia32 *fp;
Robert Richtera0e3e702011-06-03 16:37:47 +020028 unsigned long bytes;
Jiri Olsaf6dedec2010-09-29 10:46:47 -040029
Robert Richtera0e3e702011-06-03 16:37:47 +020030 bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
Peter Zijlstra0a196842013-10-30 21:16:22 +010031 if (bytes != 0)
Jiri Olsaf6dedec2010-09-29 10:46:47 -040032 return NULL;
33
34 fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame);
35
36 oprofile_add_trace(bufhead[0].return_address);
37
38 /* frame pointers should strictly progress back up the stack
39 * (towards higher addresses) */
40 if (head >= fp)
41 return NULL;
42
43 return fp;
44}
45
46static inline int
47x86_backtrace_32(struct pt_regs * const regs, unsigned int depth)
48{
49 struct stack_frame_ia32 *head;
50
H. Peter Anvin6bd33002012-02-06 13:03:09 -080051 /* User process is IA32 */
Jiri Olsaf6dedec2010-09-29 10:46:47 -040052 if (!current || !test_thread_flag(TIF_IA32))
53 return 0;
54
55 head = (struct stack_frame_ia32 *) regs->bp;
56 while (depth-- && head)
57 head = dump_user_backtrace_32(head);
58
59 return 1;
60}
61
62#else
63static inline int
64x86_backtrace_32(struct pt_regs * const regs, unsigned int depth)
65{
66 return 0;
67}
68#endif /* CONFIG_COMPAT */
69
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040070static struct stack_frame *dump_user_backtrace(struct stack_frame *head)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
Robert Richtera0e3e702011-06-03 16:37:47 +020072 /* Also check accessibility of one struct frame_head beyond: */
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040073 struct stack_frame bufhead[2];
Robert Richtera0e3e702011-06-03 16:37:47 +020074 unsigned long bytes;
Hugh Dickinsc34d1b42005-10-29 18:16:32 -070075
Robert Richtera0e3e702011-06-03 16:37:47 +020076 bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
Peter Zijlstra0a196842013-10-30 21:16:22 +010077 if (bytes != 0)
Hugh Dickinsc34d1b42005-10-29 18:16:32 -070078 return NULL;
79
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040080 oprofile_add_trace(bufhead[0].return_address);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 /* frame pointers should strictly progress back up the stack
83 * (towards higher addresses) */
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040084 if (head >= bufhead[0].next_frame)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 return NULL;
86
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040087 return bufhead[0].next_frame;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
Linus Torvalds1da177e2005-04-16 15:20:36 -070090void
91x86_backtrace(struct pt_regs * const regs, unsigned int depth)
92{
Jiri Olsa40c6b3c2010-09-29 10:46:46 -040093 struct stack_frame *head = (struct stack_frame *)frame_pointer(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
Andy Lutomirskif39b6f02015-03-18 18:33:33 -070095 if (!user_mode(regs)) {
Josh Poimboeufec2ad9c2016-09-16 14:18:15 -050096 struct unwind_state state;
97 unsigned long addr;
98
Josh Poimboeuf3e344a02016-08-24 11:50:15 -050099 if (!depth)
100 return;
101
102 oprofile_add_trace(regs->ip);
Josh Poimboeufec2ad9c2016-09-16 14:18:15 -0500103
Josh Poimboeuf3e344a02016-08-24 11:50:15 -0500104 if (!--depth)
105 return;
106
Josh Poimboeufec2ad9c2016-09-16 14:18:15 -0500107 for (unwind_start(&state, current, regs, NULL);
108 !unwind_done(&state); unwind_next_frame(&state)) {
109 addr = unwind_get_return_address(&state);
110 if (!addr)
111 break;
112
113 oprofile_add_trace(addr);
114
115 if (!--depth)
116 break;
117 }
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 return;
120 }
121
Jiri Olsaf6dedec2010-09-29 10:46:47 -0400122 if (x86_backtrace_32(regs, depth))
123 return;
124
Hugh Dickinsc34d1b42005-10-29 18:16:32 -0700125 while (depth-- && head)
Gerald Britton30379442006-02-14 10:19:04 -0500126 head = dump_user_backtrace(head);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}