Branch data Line data Source code
1 : : /**
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>
15 : : #include <linux/workqueue.h>
16 : : #include <linux/time.h>
17 : : #include <linux/mutex.h>
18 : :
19 : : #include "oprof.h"
20 : : #include "event_buffer.h"
21 : : #include "cpu_buffer.h"
22 : : #include "buffer_sync.h"
23 : : #include "oprofile_stats.h"
24 : :
25 : : struct oprofile_operations oprofile_ops;
26 : :
27 : : unsigned long oprofile_started;
28 : : unsigned long oprofile_backtrace_depth;
29 : : static unsigned long is_setup;
30 : : static DEFINE_MUTEX(start_mutex);
31 : :
32 : : /* timer
33 : : 0 - use performance monitoring hardware if available
34 : : 1 - use the timer int mechanism regardless
35 : : */
36 : : static int timer = 0;
37 : :
38 : 0 : int oprofile_setup(void)
39 : : {
40 : : int err;
41 : :
42 : 0 : mutex_lock(&start_mutex);
43 : :
44 [ # # ]: 0 : if ((err = alloc_cpu_buffers()))
45 : : goto out;
46 : :
47 [ # # ]: 0 : if ((err = alloc_event_buffer()))
48 : : goto out1;
49 : :
50 [ # # ][ # # ]: 0 : if (oprofile_ops.setup && (err = oprofile_ops.setup()))
51 : : goto out2;
52 : :
53 : : /* Note even though this starts part of the
54 : : * profiling overhead, it's necessary to prevent
55 : : * us missing task deaths and eventually oopsing
56 : : * when trying to process the event buffer.
57 : : */
58 [ # # ]: 0 : if (oprofile_ops.sync_start) {
59 : 0 : int sync_ret = oprofile_ops.sync_start();
60 [ # # # ]: 0 : switch (sync_ret) {
61 : : case 0:
62 : : goto post_sync;
63 : : case 1:
64 : : goto do_generic;
65 : : case -1:
66 : : goto out3;
67 : : default:
68 : : goto out3;
69 : : }
70 : : }
71 : : do_generic:
72 [ # # ]: 0 : if ((err = sync_start()))
73 : : goto out3;
74 : :
75 : : post_sync:
76 : 0 : is_setup = 1;
77 : 0 : mutex_unlock(&start_mutex);
78 : 0 : return 0;
79 : :
80 : : out3:
81 [ # # ]: 0 : if (oprofile_ops.shutdown)
82 : 0 : oprofile_ops.shutdown();
83 : : out2:
84 : 0 : free_event_buffer();
85 : : out1:
86 : 0 : free_cpu_buffers();
87 : : out:
88 : 0 : mutex_unlock(&start_mutex);
89 : 0 : return err;
90 : : }
91 : :
92 : : #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
93 : :
94 : : static void switch_worker(struct work_struct *work);
95 : : static DECLARE_DELAYED_WORK(switch_work, switch_worker);
96 : :
97 : : static void start_switch_worker(void)
98 : : {
99 : : if (oprofile_ops.switch_events)
100 : : schedule_delayed_work(&switch_work, oprofile_time_slice);
101 : : }
102 : :
103 : : static void stop_switch_worker(void)
104 : : {
105 : : cancel_delayed_work_sync(&switch_work);
106 : : }
107 : :
108 : : static void switch_worker(struct work_struct *work)
109 : : {
110 : : if (oprofile_ops.switch_events())
111 : : return;
112 : :
113 : : atomic_inc(&oprofile_stats.multiplex_counter);
114 : : start_switch_worker();
115 : : }
116 : :
117 : : /* User inputs in ms, converts to jiffies */
118 : : int oprofile_set_timeout(unsigned long val_msec)
119 : : {
120 : : int err = 0;
121 : : unsigned long time_slice;
122 : :
123 : : mutex_lock(&start_mutex);
124 : :
125 : : if (oprofile_started) {
126 : : err = -EBUSY;
127 : : goto out;
128 : : }
129 : :
130 : : if (!oprofile_ops.switch_events) {
131 : : err = -EINVAL;
132 : : goto out;
133 : : }
134 : :
135 : : time_slice = msecs_to_jiffies(val_msec);
136 : : if (time_slice == MAX_JIFFY_OFFSET) {
137 : : err = -EINVAL;
138 : : goto out;
139 : : }
140 : :
141 : : oprofile_time_slice = time_slice;
142 : :
143 : : out:
144 : : mutex_unlock(&start_mutex);
145 : : return err;
146 : :
147 : : }
148 : :
149 : : #else
150 : :
151 : : static inline void start_switch_worker(void) { }
152 : : static inline void stop_switch_worker(void) { }
153 : :
154 : : #endif
155 : :
156 : : /* Actually start profiling (echo 1>/dev/oprofile/enable) */
157 : 0 : int oprofile_start(void)
158 : : {
159 : : int err = -EINVAL;
160 : :
161 : 0 : mutex_lock(&start_mutex);
162 : :
163 [ # # ]: 0 : if (!is_setup)
164 : : goto out;
165 : :
166 : : err = 0;
167 : :
168 [ # # ]: 0 : if (oprofile_started)
169 : : goto out;
170 : :
171 : 0 : oprofile_reset_stats();
172 : :
173 [ # # ]: 0 : if ((err = oprofile_ops.start()))
174 : : goto out;
175 : :
176 : : start_switch_worker();
177 : :
178 : 0 : oprofile_started = 1;
179 : : out:
180 : 0 : mutex_unlock(&start_mutex);
181 : 0 : return err;
182 : : }
183 : :
184 : :
185 : : /* echo 0>/dev/oprofile/enable */
186 : 0 : void oprofile_stop(void)
187 : : {
188 : 0 : mutex_lock(&start_mutex);
189 [ # # ]: 0 : if (!oprofile_started)
190 : : goto out;
191 : 0 : oprofile_ops.stop();
192 : 0 : oprofile_started = 0;
193 : :
194 : : stop_switch_worker();
195 : :
196 : : /* wake up the daemon to read what remains */
197 : 0 : wake_up_buffer_waiter();
198 : : out:
199 : 0 : mutex_unlock(&start_mutex);
200 : 0 : }
201 : :
202 : :
203 : 0 : void oprofile_shutdown(void)
204 : : {
205 : 0 : mutex_lock(&start_mutex);
206 [ # # ]: 0 : if (oprofile_ops.sync_stop) {
207 : 0 : int sync_ret = oprofile_ops.sync_stop();
208 [ # # ]: 0 : switch (sync_ret) {
209 : : case 0:
210 : : goto post_sync;
211 : : case 1:
212 : : goto do_generic;
213 : : default:
214 : : goto post_sync;
215 : : }
216 : : }
217 : : do_generic:
218 : 0 : sync_stop();
219 : : post_sync:
220 [ # # ]: 0 : if (oprofile_ops.shutdown)
221 : 0 : oprofile_ops.shutdown();
222 : 0 : is_setup = 0;
223 : 0 : free_event_buffer();
224 : 0 : free_cpu_buffers();
225 : 0 : mutex_unlock(&start_mutex);
226 : 0 : }
227 : :
228 : 0 : int oprofile_set_ulong(unsigned long *addr, unsigned long val)
229 : : {
230 : : int err = -EBUSY;
231 : :
232 : 0 : mutex_lock(&start_mutex);
233 [ # # ]: 0 : if (!oprofile_started) {
234 : 0 : *addr = val;
235 : : err = 0;
236 : : }
237 : 0 : mutex_unlock(&start_mutex);
238 : :
239 : 0 : return err;
240 : : }
241 : :
242 : : static int timer_mode;
243 : :
244 : 0 : static int __init oprofile_init(void)
245 : : {
246 : : int err;
247 : :
248 : : /* always init architecture to setup backtrace support */
249 : 0 : timer_mode = 0;
250 : 0 : err = oprofile_arch_init(&oprofile_ops);
251 [ # # ]: 0 : if (!err) {
252 [ # # ][ # # ]: 0 : if (!timer && !oprofilefs_register())
253 : : return 0;
254 : 0 : oprofile_arch_exit();
255 : : }
256 : :
257 : : /* setup timer mode: */
258 : 0 : timer_mode = 1;
259 : : /* no nmi timer mode if oprofile.timer is set */
260 : : if (timer || op_nmi_timer_init(&oprofile_ops)) {
261 : 0 : err = oprofile_timer_init(&oprofile_ops);
262 [ # # ]: 0 : if (err)
263 : : return err;
264 : : }
265 : :
266 : 0 : return oprofilefs_register();
267 : : }
268 : :
269 : :
270 : 0 : static void __exit oprofile_exit(void)
271 : : {
272 : 0 : oprofilefs_unregister();
273 [ # # ]: 0 : if (!timer_mode)
274 : 0 : oprofile_arch_exit();
275 : 0 : }
276 : :
277 : :
278 : : module_init(oprofile_init);
279 : : module_exit(oprofile_exit);
280 : :
281 : : module_param_named(timer, timer, int, 0644);
282 : : MODULE_PARM_DESC(timer, "force use of timer interrupt");
283 : :
284 : : MODULE_LICENSE("GPL");
285 : : MODULE_AUTHOR("John Levon <levon@movementarian.org>");
286 : : MODULE_DESCRIPTION("OProfile system profiler");
|