1
2
3#include <linux/seq_file.h>
4#include <linux/kallsyms.h>
5#include <linux/module.h>
6#include <linux/ftrace.h>
7#include <linux/fs.h>
8
9#include "trace_output.h"
10
11struct recursed_functions {
12 unsigned long ip;
13 unsigned long parent_ip;
14};
15
16static struct recursed_functions recursed_functions[CONFIG_FTRACE_RECORD_RECURSION_SIZE];
17static atomic_t nr_records;
18
19
20
21
22
23static unsigned long cached_function;
24
25void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip)
26{
27 int index = 0;
28 int i;
29 unsigned long old;
30
31 again:
32
33 if (ip == cached_function)
34 return;
35
36 i = atomic_read(&nr_records);
37
38 smp_mb__after_atomic();
39 if (i < 0)
40 return;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 if (index < i)
59 index = i;
60 if (index >= CONFIG_FTRACE_RECORD_RECURSION_SIZE)
61 return;
62
63 for (i = index - 1; i >= 0; i--) {
64 if (recursed_functions[i].ip == ip) {
65 cached_function = ip;
66 return;
67 }
68 }
69
70 cached_function = ip;
71
72
73
74
75
76
77
78 old = cmpxchg(&recursed_functions[index].ip, 0, ip);
79 if (old != 0) {
80
81 if (old == ip)
82 return;
83
84 index++;
85 goto again;
86 }
87
88 recursed_functions[index].parent_ip = parent_ip;
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 i = atomic_read(&nr_records);
110 smp_mb__after_atomic();
111 if (i < 0)
112 cmpxchg(&recursed_functions[index].ip, ip, 0);
113 else if (i <= index)
114 atomic_cmpxchg(&nr_records, i, index + 1);
115}
116EXPORT_SYMBOL_GPL(ftrace_record_recursion);
117
118static DEFINE_MUTEX(recursed_function_lock);
119static struct trace_seq *tseq;
120
121static void *recursed_function_seq_start(struct seq_file *m, loff_t *pos)
122{
123 void *ret = NULL;
124 int index;
125
126 mutex_lock(&recursed_function_lock);
127 index = atomic_read(&nr_records);
128 if (*pos < index) {
129 ret = &recursed_functions[*pos];
130 }
131
132 tseq = kzalloc(sizeof(*tseq), GFP_KERNEL);
133 if (!tseq)
134 return ERR_PTR(-ENOMEM);
135
136 trace_seq_init(tseq);
137
138 return ret;
139}
140
141static void *recursed_function_seq_next(struct seq_file *m, void *v, loff_t *pos)
142{
143 int index;
144 int p;
145
146 index = atomic_read(&nr_records);
147 p = ++(*pos);
148
149 return p < index ? &recursed_functions[p] : NULL;
150}
151
152static void recursed_function_seq_stop(struct seq_file *m, void *v)
153{
154 kfree(tseq);
155 mutex_unlock(&recursed_function_lock);
156}
157
158static int recursed_function_seq_show(struct seq_file *m, void *v)
159{
160 struct recursed_functions *record = v;
161 int ret = 0;
162
163 if (record) {
164 trace_seq_print_sym(tseq, record->parent_ip, true);
165 trace_seq_puts(tseq, ":\t");
166 trace_seq_print_sym(tseq, record->ip, true);
167 trace_seq_putc(tseq, '\n');
168 ret = trace_print_seq(m, tseq);
169 }
170
171 return ret;
172}
173
174static const struct seq_operations recursed_function_seq_ops = {
175 .start = recursed_function_seq_start,
176 .next = recursed_function_seq_next,
177 .stop = recursed_function_seq_stop,
178 .show = recursed_function_seq_show
179};
180
181static int recursed_function_open(struct inode *inode, struct file *file)
182{
183 int ret = 0;
184
185 mutex_lock(&recursed_function_lock);
186
187 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
188
189 atomic_set(&nr_records, -1);
190 smp_mb__after_atomic();
191 memset(recursed_functions, 0, sizeof(recursed_functions));
192 smp_wmb();
193
194 atomic_set(&nr_records, 0);
195 }
196 if (file->f_mode & FMODE_READ)
197 ret = seq_open(file, &recursed_function_seq_ops);
198 mutex_unlock(&recursed_function_lock);
199
200 return ret;
201}
202
203static ssize_t recursed_function_write(struct file *file,
204 const char __user *buffer,
205 size_t count, loff_t *ppos)
206{
207 return count;
208}
209
210static int recursed_function_release(struct inode *inode, struct file *file)
211{
212 if (file->f_mode & FMODE_READ)
213 seq_release(inode, file);
214 return 0;
215}
216
217static const struct file_operations recursed_functions_fops = {
218 .open = recursed_function_open,
219 .write = recursed_function_write,
220 .read = seq_read,
221 .llseek = seq_lseek,
222 .release = recursed_function_release,
223};
224
225__init static int create_recursed_functions(void)
226{
227 struct dentry *dentry;
228
229 dentry = trace_create_file("recursed_functions", 0644, NULL, NULL,
230 &recursed_functions_fops);
231 if (!dentry)
232 pr_warn("WARNING: Failed to create recursed_functions\n");
233 return 0;
234}
235
236fs_initcall(create_recursed_functions);
237