1
2
3
4
5#include <linux/config.h>
6#include <linux/module.h>
7#include <linux/profile.h>
8#include <linux/bootmem.h>
9#include <linux/notifier.h>
10#include <linux/mm.h>
11#include <linux/cpumask.h>
12#include <linux/profile.h>
13#include <asm/sections.h>
14
15static atomic_t *prof_buffer;
16static unsigned long prof_len, prof_shift;
17static int prof_on;
18static cpumask_t prof_cpu_mask = CPU_MASK_ALL;
19
20static int __init profile_setup(char * str)
21{
22 int par;
23
24 if (!strncmp(str, "schedule", 8)) {
25 prof_on = 2;
26 printk(KERN_INFO "kernel schedule profiling enabled\n");
27 if (str[7] == ',')
28 str += 8;
29 }
30 if (get_option(&str,&par)) {
31 prof_shift = par;
32 prof_on = 1;
33 printk(KERN_INFO "kernel profiling enabled (shift: %ld)\n",
34 prof_shift);
35 }
36 return 1;
37}
38__setup("profile=", profile_setup);
39
40
41void __init profile_init(void)
42{
43 if (!prof_on)
44 return;
45
46
47 prof_len = (_etext - _stext) >> prof_shift;
48 prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));
49}
50
51
52
53#ifdef CONFIG_PROFILING
54
55static DECLARE_RWSEM(profile_rwsem);
56static rwlock_t handoff_lock = RW_LOCK_UNLOCKED;
57static struct notifier_block * task_exit_notifier;
58static struct notifier_block * task_free_notifier;
59static struct notifier_block * munmap_notifier;
60
61void profile_task_exit(struct task_struct * task)
62{
63 down_read(&profile_rwsem);
64 notifier_call_chain(&task_exit_notifier, 0, task);
65 up_read(&profile_rwsem);
66}
67
68int profile_handoff_task(struct task_struct * task)
69{
70 int ret;
71 read_lock(&handoff_lock);
72 ret = notifier_call_chain(&task_free_notifier, 0, task);
73 read_unlock(&handoff_lock);
74 return (ret == NOTIFY_OK) ? 1 : 0;
75}
76
77void profile_munmap(unsigned long addr)
78{
79 down_read(&profile_rwsem);
80 notifier_call_chain(&munmap_notifier, 0, (void *)addr);
81 up_read(&profile_rwsem);
82}
83
84int task_handoff_register(struct notifier_block * n)
85{
86 int err = -EINVAL;
87
88 write_lock(&handoff_lock);
89 err = notifier_chain_register(&task_free_notifier, n);
90 write_unlock(&handoff_lock);
91 return err;
92}
93
94int task_handoff_unregister(struct notifier_block * n)
95{
96 int err = -EINVAL;
97
98 write_lock(&handoff_lock);
99 err = notifier_chain_unregister(&task_free_notifier, n);
100 write_unlock(&handoff_lock);
101 return err;
102}
103
104int profile_event_register(enum profile_type type, struct notifier_block * n)
105{
106 int err = -EINVAL;
107
108 down_write(&profile_rwsem);
109
110 switch (type) {
111 case PROFILE_TASK_EXIT:
112 err = notifier_chain_register(&task_exit_notifier, n);
113 break;
114 case PROFILE_MUNMAP:
115 err = notifier_chain_register(&munmap_notifier, n);
116 break;
117 }
118
119 up_write(&profile_rwsem);
120
121 return err;
122}
123
124
125int profile_event_unregister(enum profile_type type, struct notifier_block * n)
126{
127 int err = -EINVAL;
128
129 down_write(&profile_rwsem);
130
131 switch (type) {
132 case PROFILE_TASK_EXIT:
133 err = notifier_chain_unregister(&task_exit_notifier, n);
134 break;
135 case PROFILE_MUNMAP:
136 err = notifier_chain_unregister(&munmap_notifier, n);
137 break;
138 }
139
140 up_write(&profile_rwsem);
141 return err;
142}
143
144static struct notifier_block * profile_listeners;
145static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
146
147int register_profile_notifier(struct notifier_block * nb)
148{
149 int err;
150 write_lock_irq(&profile_lock);
151 err = notifier_chain_register(&profile_listeners, nb);
152 write_unlock_irq(&profile_lock);
153 return err;
154}
155
156
157int unregister_profile_notifier(struct notifier_block * nb)
158{
159 int err;
160 write_lock_irq(&profile_lock);
161 err = notifier_chain_unregister(&profile_listeners, nb);
162 write_unlock_irq(&profile_lock);
163 return err;
164}
165
166
167void profile_hook(struct pt_regs * regs)
168{
169 read_lock(&profile_lock);
170 notifier_call_chain(&profile_listeners, 0, regs);
171 read_unlock(&profile_lock);
172}
173
174EXPORT_SYMBOL_GPL(register_profile_notifier);
175EXPORT_SYMBOL_GPL(unregister_profile_notifier);
176EXPORT_SYMBOL_GPL(task_handoff_register);
177EXPORT_SYMBOL_GPL(task_handoff_unregister);
178
179#endif
180
181EXPORT_SYMBOL_GPL(profile_event_register);
182EXPORT_SYMBOL_GPL(profile_event_unregister);
183
184void profile_hit(int type, void *__pc)
185{
186 unsigned long pc;
187
188 if (prof_on != type || !prof_buffer)
189 return;
190 pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
191 atomic_inc(&prof_buffer[min(pc, prof_len - 1)]);
192}
193
194void profile_tick(int type, struct pt_regs *regs)
195{
196 if (type == CPU_PROFILING)
197 profile_hook(regs);
198 if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask))
199 profile_hit(type, (void *)profile_pc(regs));
200}
201
202#ifdef CONFIG_PROC_FS
203#include <linux/proc_fs.h>
204#include <asm/uaccess.h>
205#include <asm/ptrace.h>
206
207static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
208 int count, int *eof, void *data)
209{
210 int len = cpumask_scnprintf(page, count, *(cpumask_t *)data);
211 if (count - len < 2)
212 return -EINVAL;
213 len += sprintf(page + len, "\n");
214 return len;
215}
216
217static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer,
218 unsigned long count, void *data)
219{
220 cpumask_t *mask = (cpumask_t *)data;
221 unsigned long full_count = count, err;
222 cpumask_t new_value;
223
224 err = cpumask_parse(buffer, count, new_value);
225 if (err)
226 return err;
227
228 *mask = new_value;
229 return full_count;
230}
231
232void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
233{
234 struct proc_dir_entry *entry;
235
236
237 if (!(entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir)))
238 return;
239 entry->nlink = 1;
240 entry->data = (void *)&prof_cpu_mask;
241 entry->read_proc = prof_cpu_mask_read_proc;
242 entry->write_proc = prof_cpu_mask_write_proc;
243}
244
245
246
247
248
249
250
251static ssize_t
252read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos)
253{
254 unsigned long p = *ppos;
255 ssize_t read;
256 char * pnt;
257 unsigned int sample_step = 1 << prof_shift;
258
259 if (p >= (prof_len+1)*sizeof(unsigned int))
260 return 0;
261 if (count > (prof_len+1)*sizeof(unsigned int) - p)
262 count = (prof_len+1)*sizeof(unsigned int) - p;
263 read = 0;
264
265 while (p < sizeof(unsigned int) && count > 0) {
266 put_user(*((char *)(&sample_step)+p),buf);
267 buf++; p++; count--; read++;
268 }
269 pnt = (char *)prof_buffer + p - sizeof(atomic_t);
270 if (copy_to_user(buf,(void *)pnt,count))
271 return -EFAULT;
272 read += count;
273 *ppos += read;
274 return read;
275}
276
277
278
279
280
281
282
283static ssize_t write_profile(struct file *file, const char __user *buf,
284 size_t count, loff_t *ppos)
285{
286#ifdef CONFIG_SMP
287 extern int setup_profiling_timer (unsigned int multiplier);
288
289 if (count == sizeof(int)) {
290 unsigned int multiplier;
291
292 if (copy_from_user(&multiplier, buf, sizeof(int)))
293 return -EFAULT;
294
295 if (setup_profiling_timer(multiplier))
296 return -EINVAL;
297 }
298#endif
299
300 memset(prof_buffer, 0, prof_len * sizeof(atomic_t));
301 return count;
302}
303
304static struct file_operations proc_profile_operations = {
305 .read = read_profile,
306 .write = write_profile,
307};
308
309static int __init create_proc_profile(void)
310{
311 struct proc_dir_entry *entry;
312
313 if (!prof_on)
314 return 0;
315 if (!(entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL)))
316 return 0;
317 entry->proc_fops = &proc_profile_operations;
318 entry->size = (1+prof_len) * sizeof(atomic_t);
319 return 0;
320}
321module_init(create_proc_profile);
322#endif
323