blob: b2fa5df64a62b50393488152882ac9f67ab8c593 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**
2 * @file oprof.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon <levon@movementarian.org>
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/oprofile.h>
14#include <linux/moduleparam.h>
Jason Yeh1a960b42008-07-23 23:05:53 +020015#include <linux/workqueue.h>
16#include <linux/time.h>
Markus Armbruster59cc1852006-06-25 05:47:33 -070017#include <asm/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
19#include "oprof.h"
20#include "event_buffer.h"
21#include "cpu_buffer.h"
22#include "buffer_sync.h"
23#include "oprofile_stats.h"
Jason Yeh1a960b42008-07-23 23:05:53 +020024
25static unsigned long is_setup;
26static void switch_worker(struct work_struct *work);
27static DECLARE_DELAYED_WORK(switch_work, switch_worker);
28static DEFINE_MUTEX(start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30struct oprofile_operations oprofile_ops;
31
Jason Yeh1a960b42008-07-23 23:05:53 +020032unsigned long timeout_jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033unsigned long oprofile_started;
34unsigned long backtrace_depth;
Jason Yeh1a960b42008-07-23 23:05:53 +020035/* Multiplexing defaults at 1 msec*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37/* timer
38 0 - use performance monitoring hardware if available
39 1 - use the timer int mechanism regardless
40 */
41static int timer = 0;
42
43int oprofile_setup(void)
44{
45 int err;
46
Markus Armbruster59cc1852006-06-25 05:47:33 -070047 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49 if ((err = alloc_cpu_buffers()))
50 goto out;
51
52 if ((err = alloc_event_buffer()))
53 goto out1;
54
55 if (oprofile_ops.setup && (err = oprofile_ops.setup()))
56 goto out2;
57
58 /* Note even though this starts part of the
59 * profiling overhead, it's necessary to prevent
60 * us missing task deaths and eventually oopsing
61 * when trying to process the event buffer.
62 */
Bob Nelson14748552007-07-20 21:39:53 +020063 if (oprofile_ops.sync_start) {
64 int sync_ret = oprofile_ops.sync_start();
65 switch (sync_ret) {
66 case 0:
67 goto post_sync;
68 case 1:
69 goto do_generic;
70 case -1:
71 goto out3;
72 default:
73 goto out3;
74 }
75 }
76do_generic:
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 if ((err = sync_start()))
78 goto out3;
79
Bob Nelson14748552007-07-20 21:39:53 +020080post_sync:
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 is_setup = 1;
Markus Armbruster59cc1852006-06-25 05:47:33 -070082 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 return 0;
84
85out3:
86 if (oprofile_ops.shutdown)
87 oprofile_ops.shutdown();
88out2:
89 free_event_buffer();
90out1:
91 free_cpu_buffers();
92out:
Markus Armbruster59cc1852006-06-25 05:47:33 -070093 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 return err;
95}
96
Jason Yeh1a960b42008-07-23 23:05:53 +020097static void start_switch_worker(void)
98{
99 schedule_delayed_work(&switch_work, timeout_jiffies);
100}
101
102static void switch_worker(struct work_struct *work)
103{
104 if (!oprofile_ops.switch_events())
105 start_switch_worker();
106}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108/* Actually start profiling (echo 1>/dev/oprofile/enable) */
109int oprofile_start(void)
110{
111 int err = -EINVAL;
112
Markus Armbruster59cc1852006-06-25 05:47:33 -0700113 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 if (!is_setup)
115 goto out;
116
117 err = 0;
118
119 if (oprofile_started)
120 goto out;
121
122 oprofile_reset_stats();
123
124 if ((err = oprofile_ops.start()))
125 goto out;
126
Jason Yeh1a960b42008-07-23 23:05:53 +0200127 if (oprofile_ops.switch_events)
128 start_switch_worker();
129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 oprofile_started = 1;
131out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700132 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return err;
134}
135
136
137/* echo 0>/dev/oprofile/enable */
138void oprofile_stop(void)
139{
Markus Armbruster59cc1852006-06-25 05:47:33 -0700140 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 if (!oprofile_started)
142 goto out;
143 oprofile_ops.stop();
144 oprofile_started = 0;
Jason Yeh1a960b42008-07-23 23:05:53 +0200145 cancel_delayed_work_sync(&switch_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 /* wake up the daemon to read what remains */
147 wake_up_buffer_waiter();
148out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700149 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150}
151
152
153void oprofile_shutdown(void)
154{
Markus Armbruster59cc1852006-06-25 05:47:33 -0700155 mutex_lock(&start_mutex);
Bob Nelson14748552007-07-20 21:39:53 +0200156 if (oprofile_ops.sync_stop) {
157 int sync_ret = oprofile_ops.sync_stop();
158 switch (sync_ret) {
159 case 0:
160 goto post_sync;
161 case 1:
162 goto do_generic;
163 default:
164 goto post_sync;
165 }
166 }
167do_generic:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 sync_stop();
Bob Nelson14748552007-07-20 21:39:53 +0200169post_sync:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 if (oprofile_ops.shutdown)
171 oprofile_ops.shutdown();
172 is_setup = 0;
173 free_event_buffer();
174 free_cpu_buffers();
Markus Armbruster59cc1852006-06-25 05:47:33 -0700175 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
Jason Yeh1a960b42008-07-23 23:05:53 +0200178/* User inputs in ms, converts to jiffies */
179int oprofile_set_timeout(unsigned long val_msec)
180{
181 int err = 0;
182
183 mutex_lock(&start_mutex);
184
185 if (oprofile_started) {
186 err = -EBUSY;
187 goto out;
188 }
189
190 if (!oprofile_ops.switch_events) {
191 err = -EINVAL;
192 goto out;
193 }
194
195 timeout_jiffies = msecs_to_jiffies(val_msec);
196 if (timeout_jiffies == MAX_JIFFY_OFFSET)
197 timeout_jiffies = msecs_to_jiffies(1);
198
199out:
200 mutex_unlock(&start_mutex);
201 return err;
202
203}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
205int oprofile_set_backtrace(unsigned long val)
206{
207 int err = 0;
208
Markus Armbruster59cc1852006-06-25 05:47:33 -0700209 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
211 if (oprofile_started) {
212 err = -EBUSY;
213 goto out;
214 }
215
216 if (!oprofile_ops.backtrace) {
217 err = -EINVAL;
218 goto out;
219 }
220
221 backtrace_depth = val;
222
223out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700224 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 return err;
226}
227
Jason Yeh1a960b42008-07-23 23:05:53 +0200228static void __init oprofile_switch_timer_init(void)
229{
230 timeout_jiffies = msecs_to_jiffies(1);
231}
232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233static int __init oprofile_init(void)
234{
235 int err;
236
Jason Yeh1a960b42008-07-23 23:05:53 +0200237 oprofile_switch_timer_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 err = oprofile_arch_init(&oprofile_ops);
239
240 if (err < 0 || timer) {
241 printk(KERN_INFO "oprofile: using timer interrupt.\n");
242 oprofile_timer_init(&oprofile_ops);
243 }
244
245 err = oprofilefs_register();
246 if (err)
247 oprofile_arch_exit();
248
249 return err;
250}
251
252
253static void __exit oprofile_exit(void)
254{
255 oprofilefs_unregister();
256 oprofile_arch_exit();
257}
258
259
260module_init(oprofile_init);
261module_exit(oprofile_exit);
262
263module_param_named(timer, timer, int, 0644);
264MODULE_PARM_DESC(timer, "force use of timer interrupt");
265
266MODULE_LICENSE("GPL");
267MODULE_AUTHOR("John Levon <levon@movementarian.org>");
268MODULE_DESCRIPTION("OProfile system profiler");