]>
Commit | Line | Data |
---|---|---|
912e8c59 | 1 | // tapset for timers |
f66bb29a | 2 | // Copyright (C) 2005-2011 Red Hat Inc. |
912e8c59 | 3 | // Copyright (C) 2005-2007 Intel Corporation. |
912e8c59 JS |
4 | // |
5 | // This file is part of systemtap, and is free software. You can | |
6 | // redistribute it and/or modify it under the terms of the GNU General | |
7 | // Public License (GPL); either version 2, or (at your option) any | |
8 | // later version. | |
9 | ||
10 | ||
11 | #include "session.h" | |
12 | #include "tapsets.h" | |
13 | #include "translate.h" | |
14 | #include "util.h" | |
15 | ||
16 | #include <cstring> | |
17 | #include <string> | |
18 | ||
19 | ||
20 | using namespace std; | |
21 | using namespace __gnu_cxx; | |
22 | ||
23 | ||
4627ed58 | 24 | static const string TOK_TIMER("timer"); |
912e8c59 JS |
25 | |
26 | ||
27 | // ------------------------------------------------------------------------ | |
28 | // timer derived probes | |
29 | // ------------------------------------------------------------------------ | |
30 | ||
31 | ||
32 | struct timer_derived_probe: public derived_probe | |
33 | { | |
34 | int64_t interval, randomize; | |
35 | bool time_is_msecs; // NB: hrtimers get ms-based probes on modern kernels instead | |
36 | timer_derived_probe (probe* p, probe_point* l, | |
37 | int64_t i, int64_t r, bool ms=false); | |
38 | virtual void join_group (systemtap_session& s); | |
2865d17a DB |
39 | |
40 | // No assertion need be emitted, since this probe is allowed for unprivileged | |
41 | // users. | |
42e38653 | 42 | void emit_privilege_assertion (translator_output*) {} |
8f6d8c2b | 43 | void print_dupe_stamp(ostream& o) { print_dupe_stamp_unprivileged (o); } |
912e8c59 JS |
44 | }; |
45 | ||
46 | ||
47 | struct timer_derived_probe_group: public generic_dpg<timer_derived_probe> | |
48 | { | |
49 | void emit_interval (translator_output* o); | |
50 | public: | |
51 | void emit_module_decls (systemtap_session& s); | |
52 | void emit_module_init (systemtap_session& s); | |
53 | void emit_module_exit (systemtap_session& s); | |
54 | }; | |
55 | ||
56 | ||
57 | timer_derived_probe::timer_derived_probe (probe* p, probe_point* l, | |
58 | int64_t i, int64_t r, bool ms): | |
59 | derived_probe (p, l), interval (i), randomize (r), time_is_msecs(ms) | |
60 | { | |
61 | if (interval <= 0 || interval > 1000000) // make i and r fit into plain ints | |
ce0f6648 | 62 | //TRANSLATORS: 'timer' is the name of a probe point |
dc09353a | 63 | throw SEMANTIC_ERROR (_("invalid interval for jiffies timer")); |
912e8c59 JS |
64 | // randomize = 0 means no randomization |
65 | if (randomize < 0 || randomize > interval) | |
ce0f6648 | 66 | //TRANSLATORS: 'randomize' is a key word |
dc09353a | 67 | throw SEMANTIC_ERROR (_("invalid randomize for jiffies timer")); |
912e8c59 JS |
68 | |
69 | if (locations.size() != 1) | |
dc09353a | 70 | throw SEMANTIC_ERROR (_("only expect one probe point")); |
912e8c59 JS |
71 | // so we don't have to loop over them in the other functions |
72 | } | |
73 | ||
74 | ||
75 | void | |
76 | timer_derived_probe::join_group (systemtap_session& s) | |
77 | { | |
78 | if (! s.timer_derived_probes) | |
79 | s.timer_derived_probes = new timer_derived_probe_group (); | |
80 | s.timer_derived_probes->enroll (this); | |
ca6d3b0f | 81 | this->group = s.timer_derived_probes; |
912e8c59 JS |
82 | } |
83 | ||
84 | ||
85 | void | |
86 | timer_derived_probe_group::emit_interval (translator_output* o) | |
87 | { | |
88 | o->line() << "({"; | |
89 | o->newline(1) << "unsigned i = stp->intrv;"; | |
90 | o->newline() << "if (stp->rnd != 0)"; | |
91 | o->newline(1) << "i += _stp_random_pm(stp->rnd);"; | |
92 | o->newline(-1) << "stp->ms ? msecs_to_jiffies(i) : i;"; | |
93 | o->newline(-1) << "})"; | |
94 | } | |
95 | ||
96 | ||
97 | void | |
98 | timer_derived_probe_group::emit_module_decls (systemtap_session& s) | |
99 | { | |
100 | if (probes.empty()) return; | |
101 | ||
102 | s.op->newline() << "/* ---- timer probes ---- */"; | |
103 | ||
104 | s.op->newline() << "static struct stap_timer_probe {"; | |
105 | s.op->newline(1) << "struct timer_list timer_list;"; | |
7c3e97f4 | 106 | s.op->newline() << "const struct stap_probe * const probe;"; |
912e8c59 JS |
107 | s.op->newline() << "unsigned intrv, ms, rnd;"; |
108 | s.op->newline(-1) << "} stap_timer_probes [" << probes.size() << "] = {"; | |
109 | s.op->indent(1); | |
110 | for (unsigned i=0; i < probes.size(); i++) | |
111 | { | |
112 | s.op->newline () << "{"; | |
faea5e16 | 113 | s.op->line() << " .probe=" << common_probe_init (probes[i]) << ","; |
912e8c59 JS |
114 | s.op->line() << " .intrv=" << probes[i]->interval << ","; |
115 | s.op->line() << " .ms=" << probes[i]->time_is_msecs << ","; | |
116 | s.op->line() << " .rnd=" << probes[i]->randomize; | |
117 | s.op->line() << " },"; | |
118 | } | |
119 | s.op->newline(-1) << "};"; | |
120 | s.op->newline(); | |
121 | ||
122 | s.op->newline() << "static void enter_timer_probe (unsigned long val) {"; | |
123 | s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [val];"; | |
065d5567 JS |
124 | s.op->newline() << "if ((atomic_read (session_state()) == STAP_SESSION_STARTING) ||"; |
125 | s.op->newline() << " (atomic_read (session_state()) == STAP_SESSION_RUNNING))"; | |
912e8c59 JS |
126 | s.op->newline(1) << "mod_timer (& stp->timer_list, jiffies + "; |
127 | emit_interval (s.op); | |
128 | s.op->line() << ");"; | |
129 | s.op->newline(-1) << "{"; | |
130 | s.op->indent(1); | |
71db462b | 131 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe", |
cda141c2 | 132 | "stp_probe_type_timer"); |
26e63673 | 133 | s.op->newline() << "(*stp->probe->ph) (c);"; |
ef1337ee | 134 | common_probe_entryfn_epilogue (s, true, otf_safe_context(s)); |
912e8c59 JS |
135 | s.op->newline(-1) << "}"; |
136 | s.op->newline(-1) << "}"; | |
137 | } | |
138 | ||
139 | ||
140 | void | |
141 | timer_derived_probe_group::emit_module_init (systemtap_session& s) | |
142 | { | |
143 | if (probes.empty()) return; | |
144 | ||
145 | s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; | |
146 | s.op->newline(1) << "struct stap_timer_probe* stp = & stap_timer_probes [i];"; | |
26e63673 | 147 | s.op->newline() << "probe_point = stp->probe->pp;"; |
912e8c59 JS |
148 | s.op->newline() << "init_timer (& stp->timer_list);"; |
149 | s.op->newline() << "stp->timer_list.function = & enter_timer_probe;"; | |
150 | s.op->newline() << "stp->timer_list.data = i;"; // NB: important! | |
151 | // copy timer renew calculations from above :-( | |
152 | s.op->newline() << "stp->timer_list.expires = jiffies + "; | |
153 | emit_interval (s.op); | |
154 | s.op->line() << ";"; | |
155 | s.op->newline() << "add_timer (& stp->timer_list);"; | |
156 | // note: no partial failure rollback is needed: add_timer cannot fail. | |
157 | s.op->newline(-1) << "}"; // for loop | |
158 | } | |
159 | ||
160 | ||
161 | void | |
162 | timer_derived_probe_group::emit_module_exit (systemtap_session& s) | |
163 | { | |
164 | if (probes.empty()) return; | |
165 | ||
166 | s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)"; | |
167 | s.op->newline(1) << "del_timer_sync (& stap_timer_probes[i].timer_list);"; | |
168 | s.op->indent(-1); | |
169 | } | |
170 | ||
171 | ||
172 | ||
173 | // ------------------------------------------------------------------------ | |
174 | // hrtimer derived probes | |
175 | // ------------------------------------------------------------------------ | |
176 | // This is a new timer interface that provides more flexibility in specifying | |
177 | // intervals, and uses the hrtimer APIs when available for greater precision. | |
178 | // While hrtimers were added in 2.6.16, the API's weren't exported until | |
179 | // 2.6.17, so we must check this kernel version before attempting to use | |
180 | // hrtimers. | |
181 | // | |
182 | // * hrtimer_derived_probe: creates a probe point based on the hrtimer APIs. | |
183 | ||
184 | ||
185 | struct hrtimer_derived_probe: public derived_probe | |
186 | { | |
187 | // set a (generous) maximum of one day in ns | |
188 | static const int64_t max_ns_interval = 1000000000LL * 60LL * 60LL * 24LL; | |
189 | ||
190 | // 100us seems like a reasonable minimum | |
191 | static const int64_t min_ns_interval = 100000LL; | |
192 | ||
193 | int64_t interval, randomize; | |
194 | ||
195 | hrtimer_derived_probe (probe* p, probe_point* l, int64_t i, int64_t r, | |
196 | int64_t scale): | |
197 | derived_probe (p, l), interval (i), randomize (r) | |
198 | { | |
199 | if ((i < min_ns_interval) || (i > max_ns_interval)) | |
dc09353a | 200 | throw SEMANTIC_ERROR(_F("interval value out of range (%s, %s)", |
b530b5b3 LB |
201 | (lex_cast(scale < min_ns_interval ? min_ns_interval/scale : 1).c_str()), |
202 | lex_cast(max_ns_interval/scale).c_str())); | |
912e8c59 JS |
203 | |
204 | // randomize = 0 means no randomization | |
205 | if ((r < 0) || (r > i)) | |
dc09353a | 206 | throw SEMANTIC_ERROR(_("randomization value out of range")); |
912e8c59 JS |
207 | } |
208 | ||
209 | void join_group (systemtap_session& s); | |
2865d17a DB |
210 | |
211 | // No assertion need be emitted, since these probes are allowed for | |
212 | // unprivileged users. | |
42e38653 | 213 | void emit_privilege_assertion (translator_output*) {} |
8f6d8c2b | 214 | void print_dupe_stamp(ostream& o) { print_dupe_stamp_unprivileged (o); } |
912e8c59 JS |
215 | }; |
216 | ||
217 | ||
218 | struct hrtimer_derived_probe_group: public generic_dpg<hrtimer_derived_probe> | |
219 | { | |
912e8c59 JS |
220 | public: |
221 | void emit_module_decls (systemtap_session& s); | |
222 | void emit_module_init (systemtap_session& s); | |
039546fa | 223 | void emit_module_refresh (systemtap_session& s); |
912e8c59 | 224 | void emit_module_exit (systemtap_session& s); |
ca6d3b0f JL |
225 | |
226 | bool otf_supported (systemtap_session& s) | |
227 | { return !s.runtime_usermode_p(); } | |
228 | ||
229 | // workqueue manipulation is safe in hrtimers | |
230 | bool otf_safe_context (systemtap_session& s) | |
231 | { return otf_supported(s); } | |
912e8c59 JS |
232 | }; |
233 | ||
234 | ||
235 | void | |
236 | hrtimer_derived_probe::join_group (systemtap_session& s) | |
237 | { | |
238 | if (! s.hrtimer_derived_probes) | |
239 | s.hrtimer_derived_probes = new hrtimer_derived_probe_group (); | |
240 | s.hrtimer_derived_probes->enroll (this); | |
ca6d3b0f | 241 | this->group = s.hrtimer_derived_probes; |
912e8c59 JS |
242 | } |
243 | ||
244 | ||
912e8c59 JS |
245 | void |
246 | hrtimer_derived_probe_group::emit_module_decls (systemtap_session& s) | |
247 | { | |
248 | if (probes.empty()) return; | |
249 | ||
250 | s.op->newline() << "/* ---- hrtimer probes ---- */"; | |
2b5ecafd DS |
251 | s.op->newline() << "#include \"timer.c\""; |
252 | s.op->newline() << "static struct stap_hrtimer_probe stap_hrtimer_probes [" << probes.size() << "] = {"; | |
912e8c59 | 253 | |
912e8c59 JS |
254 | s.op->indent(1); |
255 | for (unsigned i=0; i < probes.size(); i++) | |
256 | { | |
257 | s.op->newline () << "{"; | |
faea5e16 | 258 | s.op->line() << " .probe=" << common_probe_init (probes[i]) << ","; |
912e8c59 JS |
259 | s.op->line() << " .intrv=" << probes[i]->interval << "LL,"; |
260 | s.op->line() << " .rnd=" << probes[i]->randomize << "LL"; | |
261 | s.op->line() << " },"; | |
262 | } | |
263 | s.op->newline(-1) << "};"; | |
264 | s.op->newline(); | |
265 | ||
2b5ecafd DS |
266 | if (!s.runtime_usermode_p()) |
267 | { | |
6b378b7c | 268 | s.op->newline() << "static hrtimer_return_t _stp_hrtimer_notify_function (struct hrtimer *timer) {"; |
2b5ecafd DS |
269 | |
270 | s.op->newline(1) << "int rc = HRTIMER_NORESTART;"; | |
271 | s.op->newline() << "struct stap_hrtimer_probe *stp = container_of(timer, struct stap_hrtimer_probe, hrtimer);"; | |
272 | ||
273 | // Update the timer with the next trigger time | |
065d5567 JS |
274 | s.op->newline() << "if ((atomic_read (session_state()) == STAP_SESSION_STARTING) ||"; |
275 | s.op->newline() << " (atomic_read (session_state()) == STAP_SESSION_RUNNING)) {"; | |
2b5ecafd DS |
276 | s.op->newline(1) << "_stp_hrtimer_update(stp);"; |
277 | s.op->newline() << "rc = HRTIMER_RESTART;"; | |
278 | s.op->newline(-1) << "}"; | |
279 | ||
280 | s.op->newline() << "{"; | |
281 | s.op->indent(1); | |
282 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe", | |
283 | "stp_probe_type_hrtimer"); | |
284 | s.op->newline() << "(*stp->probe->ph) (c);"; | |
ef1337ee | 285 | common_probe_entryfn_epilogue (s, true, otf_safe_context(s)); |
2b5ecafd DS |
286 | s.op->newline(-1) << "}"; |
287 | s.op->newline() << "return rc;"; | |
288 | s.op->newline(-1) << "}"; | |
289 | } | |
290 | else | |
291 | { | |
6b378b7c | 292 | s.op->newline() << "static void _stp_hrtimer_notify_function (sigval_t value)"; |
2b5ecafd DS |
293 | s.op->newline(1) << "{"; |
294 | s.op->newline() << "struct stap_hrtimer_probe *stp = value.sival_ptr;"; | |
295 | ||
296 | // Update the timer with the next trigger time | |
065d5567 JS |
297 | s.op->newline() << "if ((atomic_read (session_state()) == STAP_SESSION_STARTING) ||"; |
298 | s.op->newline() << " (atomic_read (session_state()) == STAP_SESSION_RUNNING)) {"; | |
2b5ecafd DS |
299 | s.op->newline(1) << "_stp_hrtimer_update(stp);"; |
300 | s.op->newline(-1) << "}"; | |
301 | ||
302 | s.op->newline() << "{"; | |
303 | s.op->indent(1); | |
304 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp->probe", | |
305 | "stp_probe_type_hrtimer"); | |
306 | s.op->newline() << "(*stp->probe->ph) (c);"; | |
ef1337ee | 307 | common_probe_entryfn_epilogue (s, true, otf_safe_context(s)); |
2b5ecafd DS |
308 | s.op->newline(-1) << "}"; |
309 | s.op->newline(-1) << "}"; | |
310 | } | |
912e8c59 JS |
311 | } |
312 | ||
313 | ||
314 | void | |
315 | hrtimer_derived_probe_group::emit_module_init (systemtap_session& s) | |
316 | { | |
317 | if (probes.empty()) return; | |
318 | ||
039546fa JL |
319 | s.op->newline( 0) << "_stp_hrtimer_init();"; |
320 | s.op->newline( 0) << "for (i=0; i<" << probes.size() << "; i++) {"; | |
321 | s.op->newline(+1) << "struct stap_hrtimer_probe* stp = & stap_hrtimer_probes [i];"; | |
322 | s.op->newline( 0) << "probe_point = stp->probe->pp;"; | |
2b5ecafd DS |
323 | |
324 | // Note: no partial failure rollback is needed for kernel hrtimer | |
325 | // probes (hrtimer_start only "fails" if the timer was already | |
326 | // active, which cannot be). But, stapdyn timer probes need a | |
327 | // rollback, and it won't hurt the kernel hrtimers. | |
039546fa JL |
328 | s.op->newline( 0) << "rc = _stp_hrtimer_create(stp, _stp_hrtimer_notify_function);"; |
329 | s.op->newline( 0) << "if (rc) {"; | |
330 | s.op->newline(+1) << "for (j=i-1; j>=0; j--) {"; // partial rollback | |
331 | s.op->newline(+1) << "_stp_hrtimer_cancel(& stap_hrtimer_probes[j]);"; | |
4bf3d59d JL |
332 | if (!s.runtime_usermode_p()) |
333 | s.op->newline( 0) << "stap_hrtimer_probes[j].enabled = 0;"; | |
039546fa JL |
334 | s.op->newline(-1) << "}"; |
335 | s.op->newline( 0) << "break;"; // don't attempt to register any more | |
336 | s.op->newline(-1) << "}"; | |
337 | ||
4bf3d59d JL |
338 | // If not in kernel mode, then we always want to start the timer because |
339 | // on-the-fly starting/stopping is not supported. | |
340 | if (!s.runtime_usermode_p()) | |
341 | { | |
342 | // If the probe condition is off, then don't bother starting the timer | |
343 | s.op->newline( 0) << "if (!stp->probe->cond_enabled) {"; | |
344 | s.op->newline(+1) << "dbug_otf(\"not starting (hrtimer) pidx %zu\\n\","; | |
345 | s.op->newline( 0) << "stp->probe->index);"; | |
346 | s.op->newline( 0) << "continue;"; | |
347 | s.op->newline(-1) << "}"; | |
348 | } | |
039546fa JL |
349 | |
350 | // Start the timer (with rollback on failure) | |
351 | s.op->newline( 0) << "rc = _stp_hrtimer_start(stp);"; | |
352 | s.op->newline( 0) << "if (rc) {"; | |
353 | s.op->newline(+1) << "for (j=i-1; j>=0; j--) {"; // partial rollback | |
354 | s.op->newline(+1) << "_stp_hrtimer_cancel(& stap_hrtimer_probes[j]);"; | |
4bf3d59d JL |
355 | if (!s.runtime_usermode_p()) |
356 | s.op->newline( 0) << "stap_hrtimer_probes[j].enabled = 0;"; | |
039546fa JL |
357 | s.op->newline(-1) << "}"; |
358 | s.op->newline( 0) << "break;"; // don't attempt to register any more | |
359 | s.op->newline(-1) << "}"; | |
360 | ||
361 | // Mark as enabled since we successfully started the timer | |
4bf3d59d JL |
362 | if (!s.runtime_usermode_p()) |
363 | s.op->newline( 0) << "stp->enabled = 1;"; | |
364 | ||
039546fa JL |
365 | s.op->newline(-1) << "}"; // for loop |
366 | } | |
367 | ||
368 | ||
369 | void | |
370 | hrtimer_derived_probe_group::emit_module_refresh (systemtap_session& s) | |
371 | { | |
4bf3d59d | 372 | if (probes.empty() || s.runtime_usermode_p()) return; |
039546fa JL |
373 | |
374 | // Check if we need to enable/disable any timers | |
039546fa JL |
375 | s.op->newline( 0) << "for (i=0; i <" << probes.size() << "; i++) {"; |
376 | s.op->newline(+1) << "struct stap_hrtimer_probe* stp = &stap_hrtimer_probes[i];"; | |
377 | // timer disabled, but condition says enabled? | |
378 | s.op->newline( 0) << "if (!stp->enabled && stp->probe->cond_enabled) {"; | |
379 | s.op->newline(+1) << "dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; | |
380 | s.op->newline( 0) << "_stp_hrtimer_start(stp);"; | |
381 | // timer enabled, but condition says disabled? | |
382 | s.op->newline(-1) << "} else if (stp->enabled && !stp->probe->cond_enabled) {"; | |
383 | s.op->newline(+1) << "dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; | |
384 | s.op->newline( 0) << "_stp_hrtimer_cancel(stp);"; | |
385 | s.op->newline(-1) << "}"; | |
386 | s.op->newline( 0) << "stp->enabled = stp->probe->cond_enabled;"; | |
2b5ecafd | 387 | s.op->newline(-1) << "}"; |
912e8c59 JS |
388 | } |
389 | ||
390 | ||
391 | void | |
392 | hrtimer_derived_probe_group::emit_module_exit (systemtap_session& s) | |
393 | { | |
394 | if (probes.empty()) return; | |
395 | ||
396 | s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)"; | |
2b5ecafd | 397 | s.op->indent(1); |
039546fa | 398 | s.op->newline() << "_stp_hrtimer_delete(& stap_hrtimer_probes[i]);"; |
912e8c59 JS |
399 | s.op->indent(-1); |
400 | } | |
401 | ||
402 | ||
403 | ||
404 | // ------------------------------------------------------------------------ | |
405 | // profile derived probes | |
406 | // ------------------------------------------------------------------------ | |
407 | // On kernels < 2.6.10, this uses the register_profile_notifier API to | |
408 | // generate the timed events for profiling; on kernels >= 2.6.10 this | |
409 | // uses the register_timer_hook API. The latter doesn't currently allow | |
410 | // simultaneous users, so insertion will fail if the profiler is busy. | |
411 | // (Conflicting users may include OProfile, other SystemTap probes, etc.) | |
412 | ||
413 | ||
414 | struct profile_derived_probe: public derived_probe | |
415 | { | |
416 | profile_derived_probe (systemtap_session &s, probe* p, probe_point* l); | |
417 | void join_group (systemtap_session& s); | |
418 | }; | |
419 | ||
420 | ||
421 | struct profile_derived_probe_group: public generic_dpg<profile_derived_probe> | |
422 | { | |
423 | public: | |
424 | void emit_module_decls (systemtap_session& s); | |
425 | void emit_module_init (systemtap_session& s); | |
426 | void emit_module_exit (systemtap_session& s); | |
427 | }; | |
428 | ||
429 | ||
430 | profile_derived_probe::profile_derived_probe (systemtap_session &, probe* p, probe_point* l): | |
431 | derived_probe(p, l) | |
432 | { | |
433 | } | |
434 | ||
435 | ||
436 | void | |
437 | profile_derived_probe::join_group (systemtap_session& s) | |
438 | { | |
439 | if (! s.profile_derived_probes) | |
440 | s.profile_derived_probes = new profile_derived_probe_group (); | |
441 | s.profile_derived_probes->enroll (this); | |
ca6d3b0f | 442 | this->group = s.profile_derived_probes; |
912e8c59 JS |
443 | } |
444 | ||
445 | ||
912e8c59 JS |
446 | // timer.profile probe handlers are hooked up in an entertaining way |
447 | // to the underlying kernel facility. The fact that 2.6.11+ era | |
448 | // "register_timer_hook" API allows only one consumer *system-wide* | |
449 | // will give a hint. We will have a single entry function (and thus | |
450 | // trivial registration / unregistration), and it will call all probe | |
451 | // handler functions in sequence. | |
452 | ||
453 | void | |
454 | profile_derived_probe_group::emit_module_decls (systemtap_session& s) | |
455 | { | |
456 | if (probes.empty()) return; | |
457 | ||
458 | // kernels < 2.6.10: use register_profile_notifier API | |
459 | // kernels >= 2.6.10: use register_timer_hook API | |
460 | s.op->newline() << "/* ---- profile probes ---- */"; | |
461 | ||
462 | // This function calls all the profiling probe handlers in sequence. | |
463 | // The only tricky thing is that the context will be reused amongst | |
464 | // them. While a simple sequence of calls to the individual probe | |
465 | // handlers is unlikely to go terribly wrong (with c->last_error | |
466 | // being set causing an early return), but for extra assurance, we | |
467 | // open-code the same logic here. | |
468 | ||
469 | s.op->newline() << "static void enter_all_profile_probes (struct pt_regs *regs) {"; | |
7c3e97f4 | 470 | s.op->newline(1) << "const struct stap_probe * probe = " |
faea5e16 | 471 | << common_probe_init (probes[0]) << ";"; |
71db462b | 472 | common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "probe", |
cda141c2 | 473 | "stp_probe_type_profile_timer"); |
97cd9334 MW |
474 | // Timer interrupts save all registers, so if the interrupt happened |
475 | // in user space we can rely on it being the full user pt_regs. | |
d9aed31e | 476 | s.op->newline() << "if (user_mode(regs)) {"; |
e04b5d74 | 477 | s.op->newline(1) << "c->user_mode_p = 1;"; |
d9aed31e MW |
478 | s.op->newline() << "c->uregs = regs;"; |
479 | s.op->newline(-1) << "} else {"; | |
480 | s.op->newline(1) << "c->kregs = regs;"; | |
481 | s.op->newline(-1) << "}"; | |
912e8c59 JS |
482 | |
483 | for (unsigned i=0; i<probes.size(); i++) | |
484 | { | |
485 | if (i > 0) | |
486 | { | |
487 | // Some lightweight inter-probe context resetting | |
488 | // XXX: not quite right: MAXERRORS not respected | |
26e63673 JS |
489 | // XXX: STP_TIMING stats are also not correct |
490 | s.op->newline() << "probe = " << common_probe_init (probes[i]) << ";"; | |
491 | s.op->newline() << "#ifdef STP_NEED_PROBE_NAME"; | |
492 | s.op->newline() << "c->probe_name = probe->pn;"; | |
493 | s.op->newline() << "#endif"; | |
152fa051 LB |
494 | if(!s.suppress_time_limits) |
495 | { | |
496 | s.op->newline() << "c->actionremaining = MAXACTION;"; | |
497 | } | |
912e8c59 | 498 | } |
26e63673 | 499 | s.op->newline() << "if (c->last_error == NULL) probe->ph (c);"; |
912e8c59 | 500 | } |
ef1337ee | 501 | common_probe_entryfn_epilogue (s, true, otf_safe_context(s)); |
912e8c59 JS |
502 | s.op->newline(-1) << "}"; |
503 | ||
504 | s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore | |
505 | ||
506 | s.op->newline() << "static int enter_profile_probes (struct notifier_block *self," | |
507 | << " unsigned long val, void *data) {"; | |
508 | s.op->newline(1) << "(void) self; (void) val;"; | |
509 | s.op->newline() << "enter_all_profile_probes ((struct pt_regs *) data);"; | |
510 | s.op->newline() << "return 0;"; | |
511 | s.op->newline(-1) << "}"; | |
512 | s.op->newline() << "struct notifier_block stap_profile_notifier = {" | |
513 | << " .notifier_call = & enter_profile_probes };"; | |
514 | ||
515 | s.op->newline() << "#else"; | |
516 | ||
517 | s.op->newline() << "static int enter_profile_probes (struct pt_regs *regs) {"; | |
518 | s.op->newline(1) << "enter_all_profile_probes (regs);"; | |
519 | s.op->newline() << "return 0;"; | |
520 | s.op->newline(-1) << "}"; | |
521 | ||
522 | s.op->newline() << "#endif"; | |
523 | } | |
524 | ||
525 | ||
526 | void | |
527 | profile_derived_probe_group::emit_module_init (systemtap_session& s) | |
528 | { | |
529 | if (probes.empty()) return; | |
530 | ||
531 | s.op->newline() << "probe_point = \"timer.profile\";"; // NB: hard-coded for convenience | |
532 | s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore | |
533 | s.op->newline() << "rc = register_profile_notifier (& stap_profile_notifier);"; | |
534 | s.op->newline() << "#else"; | |
535 | s.op->newline() << "rc = register_timer_hook (& enter_profile_probes);"; | |
536 | s.op->newline() << "#endif"; | |
537 | } | |
538 | ||
539 | ||
540 | void | |
541 | profile_derived_probe_group::emit_module_exit (systemtap_session& s) | |
542 | { | |
543 | if (probes.empty()) return; | |
544 | ||
f755cab3 | 545 | s.op->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)"; // == using_rpn of yore |
912e8c59 JS |
546 | s.op->newline() << "unregister_profile_notifier (& stap_profile_notifier);"; |
547 | s.op->newline() << "#else"; | |
548 | s.op->newline() << "unregister_timer_hook (& enter_profile_probes);"; | |
549 | s.op->newline() << "#endif"; | |
912e8c59 JS |
550 | } |
551 | ||
552 | ||
553 | ||
554 | // ------------------------------------------------------------------------ | |
555 | // unified probe builder for timer probes | |
556 | // ------------------------------------------------------------------------ | |
557 | ||
558 | ||
559 | struct timer_builder: public derived_probe_builder | |
560 | { | |
561 | virtual void build(systemtap_session & sess, | |
562 | probe * base, probe_point * location, | |
563 | literal_map_t const & parameters, | |
564 | vector<derived_probe *> & finished_results); | |
565 | ||
566 | static void register_patterns(systemtap_session& s); | |
567 | }; | |
568 | ||
569 | void | |
570 | timer_builder::build(systemtap_session & sess, | |
571 | probe * base, | |
572 | probe_point * location, | |
573 | literal_map_t const & parameters, | |
574 | vector<derived_probe *> & finished_results) | |
575 | { | |
576 | int64_t scale=1, period, rand=0; | |
577 | ||
1abafad4 JS |
578 | if (has_null_param(parameters, "profile")) |
579 | { | |
2b5ecafd | 580 | if (sess.runtime_usermode_p()) |
dc09353a | 581 | throw SEMANTIC_ERROR (_("profile timer probes not available with the dyninst runtime")); |
2b5ecafd | 582 | |
08357f6b SM |
583 | /* As the latest mechanism for timer hook support has been |
584 | removed, we need to bail explicitly if the corresponding | |
585 | symbols are missing: */ | |
586 | if ((sess.kernel_exports.find("register_timer_hook") == sess.kernel_exports.end() | |
587 | || sess.kernel_exports.find("unregister_timer_hook") == sess.kernel_exports.end()) | |
588 | && (sess.kernel_exports.find("register_profile_notifier") == sess.kernel_exports.end() | |
589 | || sess.kernel_exports.find("unregister_profile_notifier") == sess.kernel_exports.end())) | |
dc09353a | 590 | throw SEMANTIC_ERROR (_("profiling timer support (register_timer_hook) not found in kernel!")); |
08357f6b | 591 | |
1abafad4 JS |
592 | sess.unwindsym_modules.insert ("kernel"); |
593 | finished_results.push_back | |
594 | (new profile_derived_probe(sess, base, location)); | |
595 | return; | |
596 | } | |
597 | ||
912e8c59 JS |
598 | if (!get_param(parameters, "randomize", rand)) |
599 | rand = 0; | |
600 | ||
601 | if (get_param(parameters, "jiffies", period)) | |
602 | { | |
2b5ecafd | 603 | if (sess.runtime_usermode_p()) |
dc09353a | 604 | throw SEMANTIC_ERROR (_("jiffies timer probes not available with the dyninst runtime")); |
2b5ecafd | 605 | |
912e8c59 JS |
606 | // always use basic timers for jiffies |
607 | finished_results.push_back | |
608 | (new timer_derived_probe(base, location, period, rand, false)); | |
609 | return; | |
610 | } | |
611 | else if (get_param(parameters, "hz", period)) | |
612 | { | |
613 | if (period <= 0) | |
dc09353a | 614 | throw SEMANTIC_ERROR (_("frequency must be greater than 0")); |
912e8c59 JS |
615 | period = (1000000000 + period - 1)/period; |
616 | } | |
617 | else if (get_param(parameters, "s", period) || | |
618 | get_param(parameters, "sec", period)) | |
619 | { | |
620 | scale = 1000000000; | |
621 | period *= scale; | |
622 | rand *= scale; | |
623 | } | |
624 | else if (get_param(parameters, "ms", period) || | |
625 | get_param(parameters, "msec", period)) | |
626 | { | |
627 | scale = 1000000; | |
628 | period *= scale; | |
629 | rand *= scale; | |
630 | } | |
631 | else if (get_param(parameters, "us", period) || | |
632 | get_param(parameters, "usec", period)) | |
633 | { | |
634 | scale = 1000; | |
635 | period *= scale; | |
636 | rand *= scale; | |
637 | } | |
638 | else if (get_param(parameters, "ns", period) || | |
639 | get_param(parameters, "nsec", period)) | |
640 | { | |
641 | // ok | |
642 | } | |
643 | else | |
dc09353a | 644 | throw SEMANTIC_ERROR (_("unrecognized timer variant")); |
912e8c59 JS |
645 | |
646 | // Redirect wallclock-time based probes to hrtimer code on recent | |
647 | // enough kernels. | |
648 | if (strverscmp(sess.kernel_base_release.c_str(), "2.6.17") < 0) | |
649 | { | |
650 | // hrtimers didn't exist, so use the old-school timers | |
651 | period = (period + 1000000 - 1)/1000000; | |
652 | rand = (rand + 1000000 - 1)/1000000; | |
653 | ||
654 | finished_results.push_back | |
655 | (new timer_derived_probe(base, location, period, rand, true)); | |
656 | } | |
657 | else | |
658 | finished_results.push_back | |
659 | (new hrtimer_derived_probe(base, location, period, rand, scale)); | |
660 | } | |
661 | ||
662 | void | |
663 | register_tapset_timers(systemtap_session& s) | |
664 | { | |
665 | match_node* root = s.pattern_root; | |
666 | derived_probe_builder *builder = new timer_builder(); | |
667 | ||
668 | root = root->bind(TOK_TIMER); | |
669 | ||
b12c8986 | 670 | root->bind_num("s") |
f66bb29a | 671 | ->bind_privilege(pr_all) |
b12c8986 DB |
672 | ->bind(builder); |
673 | root->bind_num("s")->bind_num("randomize") | |
f66bb29a | 674 | ->bind_privilege(pr_all) |
b12c8986 DB |
675 | ->bind(builder); |
676 | root->bind_num("sec") | |
f66bb29a | 677 | ->bind_privilege(pr_all) |
b12c8986 DB |
678 | ->bind(builder); |
679 | root->bind_num("sec")->bind_num("randomize") | |
f66bb29a | 680 | ->bind_privilege(pr_all) |
b12c8986 DB |
681 | ->bind(builder); |
682 | ||
683 | root->bind_num("ms") | |
f66bb29a | 684 | ->bind_privilege(pr_all) |
b12c8986 DB |
685 | ->bind(builder); |
686 | root->bind_num("ms")->bind_num("randomize") | |
f66bb29a | 687 | ->bind_privilege(pr_all) |
b12c8986 DB |
688 | ->bind(builder); |
689 | root->bind_num("msec") | |
f66bb29a | 690 | ->bind_privilege(pr_all) |
b12c8986 DB |
691 | ->bind(builder); |
692 | root->bind_num("msec")->bind_num("randomize") | |
f66bb29a | 693 | ->bind_privilege(pr_all) |
b12c8986 DB |
694 | ->bind(builder); |
695 | ||
696 | root->bind_num("us") | |
f66bb29a | 697 | ->bind_privilege(pr_all) |
b12c8986 DB |
698 | ->bind(builder); |
699 | root->bind_num("us")->bind_num("randomize") | |
f66bb29a | 700 | ->bind_privilege(pr_all) |
b12c8986 DB |
701 | ->bind(builder); |
702 | root->bind_num("usec") | |
f66bb29a | 703 | ->bind_privilege(pr_all) |
b12c8986 DB |
704 | ->bind(builder); |
705 | root->bind_num("usec")->bind_num("randomize") | |
f66bb29a | 706 | ->bind_privilege(pr_all) |
b12c8986 DB |
707 | ->bind(builder); |
708 | ||
709 | root->bind_num("ns") | |
f66bb29a | 710 | ->bind_privilege(pr_all) |
b12c8986 DB |
711 | ->bind(builder); |
712 | root->bind_num("ns")->bind_num("randomize") | |
f66bb29a | 713 | ->bind_privilege(pr_all) |
b12c8986 DB |
714 | ->bind(builder); |
715 | root->bind_num("nsec") | |
f66bb29a | 716 | ->bind_privilege(pr_all) |
b12c8986 DB |
717 | ->bind(builder); |
718 | root->bind_num("nsec")->bind_num("randomize") | |
f66bb29a | 719 | ->bind_privilege(pr_all) |
b12c8986 DB |
720 | ->bind(builder); |
721 | ||
722 | root->bind_num("jiffies") | |
f66bb29a | 723 | ->bind_privilege(pr_all) |
b12c8986 DB |
724 | ->bind(builder); |
725 | root->bind_num("jiffies")->bind_num("randomize") | |
f66bb29a | 726 | ->bind_privilege(pr_all) |
b12c8986 DB |
727 | ->bind(builder); |
728 | ||
729 | root->bind_num("hz") | |
f66bb29a | 730 | ->bind_privilege(pr_all) |
b12c8986 DB |
731 | ->bind(builder); |
732 | ||
2b5ecafd DS |
733 | // Not ok for unprivileged users, because register_timer_hook only |
734 | // allows a single attached callback. No resource-sharing -> no | |
735 | // unprivileged access. | |
736 | // | |
737 | // Sigh, but for dyninst users, we want a semantic error that | |
738 | // profile probes aren't supported (which will come from | |
739 | // timer_builder::build()), not a privilege error. So, we'll fake | |
740 | // it so that profile probes are allowed for all. | |
741 | if (!s.runtime_usermode_p()) { | |
742 | root->bind("profile") | |
08357f6b | 743 | ->bind("tick") |
2b5ecafd DS |
744 | ->bind(builder); |
745 | } | |
746 | else { | |
747 | root->bind("profile") | |
08357f6b | 748 | ->bind("tick") |
2b5ecafd DS |
749 | ->bind_privilege(pr_all) |
750 | ->bind(builder); | |
751 | } | |
912e8c59 JS |
752 | } |
753 | ||
754 | ||
755 | ||
756 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |