From 039546fa756906b997e3cb47733b13d2779488c4 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Mon, 2 Jun 2014 17:22:52 -0400 Subject: [PATCH] hrtimer probes: support on-the-fly [dis]arming This patch adds support for on-the-fly arming/disarming of hrtimer probes. Although it is only supported for non-usermode, there were a few changes required in dyninst/timer.c so that the same interface is exposed for both usermode and kernel mode. runtime/dyninst/timer.c - Decouple timer creation from timer starting by factoring out _stp_hrtimer_start() from _stp_hrtimer_create(). - Make _stp_hrtimer_cancel() actually just cancel the timer, and not completely delete it. - Add new _stp_hrtimer_delete() to delete the timer. runtime/linux/timer.c - Similarly, factor out _stp_hrtimer_start() from _stp_hrtimer_create(). - Add new _stp_hrtimer_delete(), which also does a cancel. tapset-timers.cxx - Declare hrtimer_derived_probe as a probe that supports on-the-fly operations. - In emit_module_init(): unconditionally create the timer, but if in STP_ON_THE_FLY mode, only bother to start it if its condition is met. - In emit_module_refresh(): check for which timers to start/cancel. --- runtime/dyninst/timer.c | 38 ++++++++++++-------- runtime/linux/timer.c | 20 +++++++++-- tapset-timers.cxx | 80 ++++++++++++++++++++++++++++++++++------- 3 files changed, 108 insertions(+), 30 deletions(-) diff --git a/runtime/dyninst/timer.c b/runtime/dyninst/timer.c index 417e228da..d7d3b9c1f 100644 --- a/runtime/dyninst/timer.c +++ b/runtime/dyninst/timer.c @@ -35,20 +35,8 @@ static void _stp_hrtimer_init(void) static int -_stp_hrtimer_create(struct stap_hrtimer_probe *shp, void (*function)(sigval_t)) +_stp_hrtimer_start(struct stap_hrtimer_probe *shp) { - int rc; - - /* Create the timer. */ - shp->sigev.sigev_notify = SIGEV_THREAD; - shp->sigev.sigev_value.sival_ptr = shp; - shp->sigev.sigev_notify_function = function; - shp->sigev.sigev_notify_attributes = NULL; - rc = timer_create(CLOCK_MONOTONIC, &shp->sigev, &shp->timer_id); - if (rc) { - return rc; - } - /* Specify a timer with the correct initial value (possibly * randomized a bit). * @@ -72,8 +60,21 @@ _stp_hrtimer_create(struct stap_hrtimer_probe *shp, void (*function)(sigval_t)) shp->its.it_value.tv_sec = i / NSEC_PER_SEC; shp->its.it_value.tv_nsec = i % NSEC_PER_SEC; } - rc = timer_settime(shp->timer_id, 0, &shp->its, NULL); - return rc; + return timer_settime(shp->timer_id, 0, &shp->its, NULL); +} + + +static int +_stp_hrtimer_create(struct stap_hrtimer_probe *shp, void (*function)(sigval_t)) +{ + int rc; + + /* Create the timer. */ + shp->sigev.sigev_notify = SIGEV_THREAD; + shp->sigev.sigev_value.sival_ptr = shp; + shp->sigev.sigev_notify_function = function; + shp->sigev.sigev_notify_attributes = NULL; + return timer_create(CLOCK_MONOTONIC, &shp->sigev, &shp->timer_id); } @@ -98,6 +99,13 @@ static void _stp_hrtimer_update(struct stap_hrtimer_probe *shp) static void _stp_hrtimer_cancel(struct stap_hrtimer_probe *shp) +{ + shp->its.it_value.tv_sec = 0; + shp->its.it_value.tv_nsec = 0; + timer_settime(shp->timer_id, 0, &shp->its, NULL); +} + +static void _stp_hrtimer_delete(struct stap_hrtimer_probe *shp) { (void) timer_delete(shp->timer_id); } diff --git a/runtime/linux/timer.c b/runtime/linux/timer.c index afc11e3d2..5beb5792a 100644 --- a/runtime/linux/timer.c +++ b/runtime/linux/timer.c @@ -18,9 +18,10 @@ static unsigned long stap_hrtimer_resolution = 0; struct stap_hrtimer_probe { struct hrtimer hrtimer; - const struct stap_probe * const probe; + const struct stap_probe * probe; int64_t intrv; int64_t rnd; + unsigned enabled; }; // The function signature changed in 2.6.21. @@ -88,17 +89,24 @@ static inline void _stp_hrtimer_update(struct stap_hrtimer_probe *stp) } +static int +_stp_hrtimer_start(struct stap_hrtimer_probe *stp) +{ + (void)hrtimer_start(&stp->hrtimer, _stp_hrtimer_get_interval(stp), + HRTIMER_MODE_REL); + return 0; +} + static int _stp_hrtimer_create(struct stap_hrtimer_probe *stp, hrtimer_return_t (*function)(struct hrtimer *)) { hrtimer_init(&stp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); stp->hrtimer.function = function; - (void)hrtimer_start(&stp->hrtimer, _stp_hrtimer_get_interval(stp), - HRTIMER_MODE_REL); return 0; } +// For kernel-mode, there is no difference between cancel/delete. static void _stp_hrtimer_cancel(struct stap_hrtimer_probe *stp) @@ -106,6 +114,12 @@ _stp_hrtimer_cancel(struct stap_hrtimer_probe *stp) hrtimer_cancel(&stp->hrtimer); } +static void +_stp_hrtimer_delete(struct stap_hrtimer_probe *stp) +{ + _stp_hrtimer_cancel(stp); +} + #else /* kernel version < 2.6.17 */ #error "not implemented" diff --git a/tapset-timers.cxx b/tapset-timers.cxx index bbc8b1fe6..436d9c9c0 100644 --- a/tapset-timers.cxx +++ b/tapset-timers.cxx @@ -211,6 +211,7 @@ struct hrtimer_derived_probe: public derived_probe // unprivileged users. void emit_privilege_assertion (translator_output*) {} void print_dupe_stamp(ostream& o) { print_dupe_stamp_unprivileged (o); } + bool on_the_fly_supported () { return true; } }; @@ -219,6 +220,7 @@ struct hrtimer_derived_probe_group: public generic_dpg public: void emit_module_decls (systemtap_session& s); void emit_module_init (systemtap_session& s); + void emit_module_refresh (systemtap_session& s); void emit_module_exit (systemtap_session& s); }; @@ -306,23 +308,77 @@ hrtimer_derived_probe_group::emit_module_init (systemtap_session& s) { if (probes.empty()) return; - s.op->newline() << "_stp_hrtimer_init();"; - s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {"; - s.op->newline(1) << "struct stap_hrtimer_probe* stp = & stap_hrtimer_probes [i];"; - s.op->newline() << "probe_point = stp->probe->pp;"; + s.op->newline( 0) << "_stp_hrtimer_init();"; + s.op->newline( 0) << "for (i=0; i<" << probes.size() << "; i++) {"; + s.op->newline(+1) << "struct stap_hrtimer_probe* stp = & stap_hrtimer_probes [i];"; + s.op->newline( 0) << "probe_point = stp->probe->pp;"; // Note: no partial failure rollback is needed for kernel hrtimer // probes (hrtimer_start only "fails" if the timer was already // active, which cannot be). But, stapdyn timer probes need a // rollback, and it won't hurt the kernel hrtimers. - s.op->newline() << "rc = _stp_hrtimer_create(stp, _stp_hrtimer_notify_function);"; - s.op->newline() << "if (rc) {"; - s.op->indent(1); - s.op->newline() << "for (j=i-1; j>=0; j--)"; // partial rollback - s.op->newline(1) << "_stp_hrtimer_cancel(& stap_hrtimer_probes[j]);"; - s.op->newline(-1) << "break;"; // don't attempt to register any more + s.op->newline( 0) << "rc = _stp_hrtimer_create(stp, _stp_hrtimer_notify_function);"; + s.op->newline( 0) << "if (rc) {"; + s.op->newline(+1) << "for (j=i-1; j>=0; j--) {"; // partial rollback + s.op->newline(+1) << "_stp_hrtimer_cancel(& stap_hrtimer_probes[j]);"; + s.op->newline( 0) << "#ifdef STP_ON_THE_FLY"; + s.op->newline( 0) << "stap_hrtimer_probes[j].enabled = 0;"; + s.op->newline( 0) << "#endif"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "break;"; // don't attempt to register any more + s.op->newline(-1) << "}"; + + // If the probe condition is off, then don't bother starting the timer + s.op->newline( 0) << "#ifdef STP_ON_THE_FLY"; + s.op->newline( 0) << "if (!stp->probe->cond_enabled) {"; + s.op->newline(+1) << "dbug_otf(\"not starting (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "continue;"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "#endif"; + + // Start the timer (with rollback on failure) + s.op->newline( 0) << "rc = _stp_hrtimer_start(stp);"; + s.op->newline( 0) << "if (rc) {"; + s.op->newline(+1) << "for (j=i-1; j>=0; j--) {"; // partial rollback + s.op->newline(+1) << "_stp_hrtimer_cancel(& stap_hrtimer_probes[j]);"; + s.op->newline( 0) << "#ifdef STP_ON_THE_FLY"; + s.op->newline( 0) << "stap_hrtimer_probes[j].enabled = 0;"; + s.op->newline( 0) << "#endif"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "break;"; // don't attempt to register any more + s.op->newline(-1) << "}"; + + // Mark as enabled since we successfully started the timer + s.op->newline( 0) << "#ifdef STP_ON_THE_FLY"; + s.op->newline( 0) << "stp->enabled = 1;"; + s.op->newline( 0) << "#endif"; + s.op->newline(-1) << "}"; // for loop +} + + +void +hrtimer_derived_probe_group::emit_module_refresh (systemtap_session& s) +{ + if (probes.empty()) return; + + // Check if we need to enable/disable any timers + s.op->newline( 0) << "#ifdef STP_ON_THE_FLY"; + + s.op->newline( 0) << "for (i=0; i <" << probes.size() << "; i++) {"; + s.op->newline(+1) << "struct stap_hrtimer_probe* stp = &stap_hrtimer_probes[i];"; + // timer disabled, but condition says enabled? + s.op->newline( 0) << "if (!stp->enabled && stp->probe->cond_enabled) {"; + s.op->newline(+1) << "dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_start(stp);"; + // timer enabled, but condition says disabled? + s.op->newline(-1) << "} else if (stp->enabled && !stp->probe->cond_enabled) {"; + s.op->newline(+1) << "dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_cancel(stp);"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "stp->enabled = stp->probe->cond_enabled;"; s.op->newline(-1) << "}"; - s.op->newline(-1) << "}"; // for loop + + s.op->newline( 0) << "#endif"; } @@ -333,7 +389,7 @@ hrtimer_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "for (i=0; i<" << probes.size() << "; i++)"; s.op->indent(1); - s.op->newline() << "_stp_hrtimer_cancel(& stap_hrtimer_probes[i]);"; + s.op->newline() << "_stp_hrtimer_delete(& stap_hrtimer_probes[i]);"; s.op->indent(-1); } -- 2.43.5