runtime/lib_timer.c - ktap

Global variables defined

Data types defined

Functions defined

Source code

  1. /*
  2. * lib_timer.c - timer library support for ktap
  3. *
  4. * This file is part of ktap by Jovi Zhangwei.
  5. *
  6. * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
  7. *
  8. * ktap is free software; you can redistribute it and/or modify it
  9. * under the terms and conditions of the GNU General Public License,
  10. * version 2, as published by the Free Software Foundation.
  11. *
  12. * ktap is distributed in the hope it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along with
  18. * this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  20. */

  21. #include <linux/ctype.h>
  22. #include <linux/slab.h>
  23. #include <linux/delay.h>
  24. #include <linux/sched.h>
  25. #include "../include/ktap_types.h"
  26. #include "ktap.h"
  27. #include "kp_obj.h"
  28. #include "kp_vm.h"
  29. #include "kp_events.h"

  30. struct ktap_hrtimer {
  31.     struct hrtimer timer;
  32.     ktap_state_t *ks;
  33.     ktap_func_t *fn;
  34.     u64 ns;
  35.     struct list_head list;
  36. };

  37. /*
  38. * Currently ktap disallow tracing event in timer callback closure,
  39. * that will corrupt ktap_state_t and ktap stack, because timer closure
  40. * and event closure use same irq percpu ktap_state_t and stack.
  41. * We can use a different percpu ktap_state_t and stack for timer purpuse,
  42. * but that's don't bring any big value with cost on memory consuming.
  43. *
  44. * So just simply disable tracing in timer closure,
  45. * get_recursion_context()/put_recursion_context() is used for this purpose.
  46. */
  47. static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
  48. {
  49.     struct ktap_hrtimer *t;
  50.     ktap_state_t *ks;
  51.     int rctx;

  52.     rcu_read_lock_sched_notrace();

  53.     t = container_of(timer, struct ktap_hrtimer, timer);
  54.     rctx = get_recursion_context(t->ks);

  55.     ks = kp_vm_new_thread(t->ks, rctx);
  56.     set_func(ks->top, t->fn);
  57.     incr_top(ks);
  58.     kp_vm_call(ks, ks->top - 1, 0);
  59.     kp_vm_exit_thread(ks);

  60.     hrtimer_add_expires_ns(timer, t->ns);

  61.     put_recursion_context(ks, rctx);
  62.     rcu_read_unlock_sched_notrace();

  63.     return HRTIMER_RESTART;
  64. }

  65. static int set_tick_timer(ktap_state_t *ks, u64 period, ktap_func_t *fn)
  66. {
  67.     struct ktap_hrtimer *t;

  68.     t = kp_malloc(ks, sizeof(*t));
  69.     if (unlikely(!t))
  70.         return -ENOMEM;
  71.     t->ks = ks;
  72.     t->fn = fn;
  73.     t->ns = period;

  74.     INIT_LIST_HEAD(&t->list);
  75.     list_add(&t->list, &(G(ks)->timers));

  76.     hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  77.     t->timer.function = hrtimer_ktap_fn;
  78.     hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);

  79.     return 0;
  80. }

  81. static int set_profile_timer(ktap_state_t *ks, u64 period, ktap_func_t *fn)
  82. {
  83.     struct perf_event_attr attr;

  84.     memset(&attr, 0, sizeof(attr));
  85.     attr.type = PERF_TYPE_SOFTWARE;
  86.     attr.config = PERF_COUNT_SW_CPU_CLOCK;
  87.     attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
  88.                PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
  89.     attr.sample_period = period;
  90.     attr.size = sizeof(attr);
  91.     attr.disabled = 0;

  92.     return kp_event_create(ks, &attr, NULL, NULL, fn);
  93. }

  94. static int do_tick_profile(ktap_state_t *ks, int is_tick)
  95. {
  96.     const char *str = kp_arg_checkstring(ks, 1);
  97.     ktap_func_t *fn = kp_arg_checkfunction(ks, 2);
  98.     const char *tmp;
  99.     char interval_str[32] = {0};
  100.     char suffix[10] = {0};
  101.     int i = 0, ret, n;
  102.     int factor;

  103.     tmp = str;
  104.     while (isdigit(*tmp))
  105.         tmp++;

  106.     strncpy(interval_str, str, tmp - str);
  107.     if (kstrtoint(interval_str, 10, &n))
  108.         goto error;

  109.     strncpy(suffix, tmp, 9);
  110.     while (suffix[i] != ' ' && suffix[i] != '\0')
  111.         i++;

  112.     suffix[i] = '\0';

  113.     if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
  114.         factor = NSEC_PER_SEC;
  115.     else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
  116.         factor = NSEC_PER_MSEC;
  117.     else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
  118.         factor = NSEC_PER_USEC;
  119.     else
  120.         goto error;

  121.     if (is_tick)
  122.         ret = set_tick_timer(ks, (u64)factor * n, fn);
  123.     else
  124.         ret = set_profile_timer(ks, (u64)factor * n, fn);

  125.     return ret;

  126. error:
  127.     kp_error(ks, "cannot parse timer interval: %s\n", str);
  128.     return -1;
  129. }

  130. /*
  131. * tick-n probes fire on only one CPU per interval.
  132. * valid time suffixes: sec/s, msec/ms, usec/us
  133. */
  134. static int kplib_timer_tick(ktap_state_t *ks)
  135. {
  136.     /* timer.tick cannot be called in trace_end state */
  137.     if (G(ks)->state != KTAP_RUNNING) {
  138.         kp_error(ks,
  139.              "timer.tick only can be called in RUNNING state\n");
  140.         return -1;
  141.     }

  142.     return do_tick_profile(ks, 1);
  143. }

  144. /*
  145. * A profile-n probe fires every fixed interval on every CPU
  146. * valid time suffixes: sec/s, msec/ms, usec/us
  147. */
  148. static int kplib_timer_profile(ktap_state_t *ks)
  149. {
  150.     /* timer.profile cannot be called in trace_end state */
  151.     if (G(ks)->state != KTAP_RUNNING) {
  152.         kp_error(ks,
  153.              "timer.profile only can be called in RUNNING state\n");
  154.         return -1;
  155.     }

  156.     return do_tick_profile(ks, 0);
  157. }

  158. void kp_exit_timers(ktap_state_t *ks)
  159. {
  160.     struct ktap_hrtimer *t, *tmp;
  161.     struct list_head *timers_list = &(G(ks)->timers);

  162.     list_for_each_entry_safe(t, tmp, timers_list, list) {
  163.         hrtimer_cancel(&t->timer);
  164.         kp_free(ks, t);
  165.     }
  166. }

  167. static const ktap_libfunc_t timer_lib_funcs[] = {
  168.     {"profile",    kplib_timer_profile},
  169.     {"tick",    kplib_timer_tick},
  170.     {NULL}
  171. };

  172. int kp_lib_init_timer(ktap_state_t *ks)
  173. {
  174.     return kp_vm_register_lib(ks, "timer", timer_lib_funcs);
  175. }