runtime/kp_str.c - ktap

Functions defined

Macros defined

Source code

  1. /*
  2. * kp_str.c - ktap string data struction manipulation
  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. * Adapted from luajit and lua interpreter.
  9. * Copyright (C) 2005-2014 Mike Pall.
  10. * Copyright (C) 1994-2008 Lua.org, PUC-Rio.
  11. *
  12. * ktap is free software; you can redistribute it and/or modify it
  13. * under the terms and conditions of the GNU General Public License,
  14. * version 2, as published by the Free Software Foundation.
  15. *
  16. * ktap is distributed in the hope it will be useful, but WITHOUT
  17. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  18. * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  19. * more details.
  20. *
  21. * You should have received a copy of the GNU General Public License along with
  22. * this program; if not, write to the Free Software Foundation, Inc.,
  23. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  24. */

  25. #include "../include/ktap_types.h"
  26. #include "kp_obj.h"
  27. #include "kp_str.h"
  28. #include "kp_mempool.h"

  29. #include <linux/ctype.h>
  30. #include <linux/module.h>
  31. #include <linux/kallsyms.h>
  32. #include "ktap.h"
  33. #include "kp_transport.h"
  34. #include "kp_vm.h"
  35. #include "kp_events.h"

  36. int kp_str_cmp(const ktap_str_t *ls, const ktap_str_t *rs)
  37. {
  38.     const char *l = getstr(ls);
  39.     size_t ll = ls->len;
  40.     const char *r = getstr(rs);
  41.     size_t lr = rs->len;

  42.     for (;;) {
  43.         int temp = strcmp(l, r);
  44.         if (temp != 0)
  45.             return temp;
  46.         else {
  47.             /* strings are equal up to a `\0' */

  48.             /* index of first `\0' in both strings */
  49.             size_t len = strlen(l);

  50.             /* r is finished? */
  51.             if (len == lr)
  52.                 return (len == ll) ? 0 : 1;
  53.             else if (len == ll)  /* l is finished? */
  54.                 return -1;

  55.             /*
  56.              * both strings longer than `len';
  57.              * go on comparing (after the `\0')
  58.              */
  59.             len++;
  60.             l += len; ll -= len; r += len; lr -= len;
  61.         }
  62.     }
  63. }

  64. /* Fast string data comparison. Caveat: unaligned access to 1st string! */
  65. static __always_inline int str_fastcmp(const char *a, const char *b, int len)
  66. {
  67.     int i = 0;

  68.     kp_assert(len > 0);
  69.     kp_assert((((uintptr_t)a + len - 1)&(PAGE_SIZE - 1)) <= PAGE_SIZE - 4);

  70.     do/* Note: innocuous access up to end of string + 3. */
  71.         uint32_t v = *(uint32_t *)(a + i) ^ *(const uint32_t *)(b + i);
  72.         if (v) {
  73.             i -= len;
  74. #if KP_LE
  75.             return (int32_t)i >= -3 ? (v << (32 + (i << 3))) : 1;
  76. #else
  77.             return (int32_t)i >= -3 ? (v >> (32 + (i << 3))) : 1;
  78. #endif
  79.         }
  80.         i += 4;
  81.     } while (i < len);
  82.     return 0;
  83. }


  84. //TODO: change hash algo

  85. #define STRING_HASHLIMIT    5
  86. static __always_inline unsigned int kp_str_hash(const char *str, size_t len)
  87. {
  88.     unsigned int h = 201236 ^ len;
  89.     size_t step = (len >> STRING_HASHLIMIT) + 1;
  90.     size_t l1;

  91.     for (l1 = len; l1 >= step; l1 -= step)
  92.         h = h ^ ((h<<5) + (h>>2) + (u8)(str[l1 - 1]));

  93.     return h;
  94. }


  95. /*
  96. * resizes the string table
  97. */
  98. int kp_str_resize(ktap_state_t *ks, int newmask)
  99. {
  100.     ktap_global_state_t *g = G(ks);
  101.     ktap_str_t **newhash;

  102.     newhash = kp_zalloc(ks, (newmask + 1) * sizeof(ktap_str_t *));
  103.     if (!newhash)
  104.         return -ENOMEM;

  105.     g->strmask = newmask;
  106.     g->strhash = newhash;
  107.     return 0;
  108. }

  109. /*
  110. * Intern a string and return string object.
  111. */
  112. ktap_str_t * kp_str_new(ktap_state_t *ks, const char *str, size_t len)
  113. {
  114.     ktap_global_state_t *g = G(ks);
  115.     ktap_str_t *s;
  116.     ktap_obj_t *o;
  117.     unsigned int h = kp_str_hash(str, len);
  118.     unsigned long flags;

  119.     if (len >= KP_MAX_STR)
  120.         return NULL;

  121.     local_irq_save(flags);
  122.     arch_spin_lock(&g->str_lock);

  123.     o = (ktap_obj_t *)g->strhash[h & g->strmask];
  124.     if (likely((((uintptr_t)str+len-1) & (PAGE_SIZE-1)) <= PAGE_SIZE-4)) {
  125.         while (o != NULL) {
  126.             ktap_str_t *sx = (ktap_str_t *)o;
  127.             if (sx->len == len &&
  128.                 !str_fastcmp(str, getstr(sx), len)) {
  129.                 arch_spin_unlock(&g->str_lock);
  130.                 local_irq_restore(flags);
  131.                 return sx; /* Return existing string. */
  132.             }
  133.             o = gch(o)->nextgc;
  134.         }
  135.     } else { /* Slow path: end of string is too close to a page boundary */
  136.         while (o != NULL) {
  137.             ktap_str_t *sx = (ktap_str_t *)o;
  138.             if (sx->len == len &&
  139.                 !memcmp(str, getstr(sx), len)) {
  140.                 arch_spin_unlock(&g->str_lock);
  141.                 local_irq_restore(flags);
  142.                 return sx; /* Return existing string. */
  143.             }
  144.             o = gch(o)->nextgc;
  145.         }
  146.     }

  147.     /* create a new string, allocate it from mempool, not use kmalloc. */
  148.     s = kp_mempool_alloc(ks, sizeof(ktap_str_t) + len + 1);
  149.     if (unlikely(!s))
  150.         goto out;
  151.     s->gct = ~KTAP_TSTR;
  152.     s->len = len;
  153.     s->hash = h;
  154.     s->reserved = 0;
  155.     memcpy(s + 1, str, len);
  156.     ((char *)(s + 1))[len] = '\0'/* ending 0 */

  157.     /* Add it to string hash table */
  158.     h &= g->strmask;
  159.     s->nextgc = (ktap_obj_t *)g->strhash[h];
  160.     g->strhash[h] = s;
  161.     if (g->strnum++ > KP_MAX_STRNUM) {
  162.         kp_error(ks, "exceed max string number %d\n", KP_MAX_STRNUM);
  163.         s = NULL;
  164.     }

  165. out:
  166.     arch_spin_unlock(&g->str_lock);
  167.     local_irq_restore(flags);
  168.     return s; /* Return newly interned string. */
  169. }

  170. void kp_str_freeall(ktap_state_t *ks)
  171. {
  172.     /* don't need to free string in here, it will handled by mempool */
  173.     kp_free(ks, G(ks)->strhash);
  174. }

  175. /* kp_str_fmt - printf implementation */

  176. /* macro to `unsign' a character */
  177. #define uchar(c)    ((unsigned char)(c))

  178. #define L_ESC        '%'

  179. /* valid flags in a format specification */
  180. #define FLAGS    "-+ #0"

  181. #define INTFRMLEN    "ll"
  182. #define INTFRM_T    long long

  183. /*
  184. * maximum size of each format specification (such as '%-099.99d')
  185. * (+10 accounts for %99.99x plus margin of error)
  186. */
  187. #define MAX_FORMAT    (sizeof(FLAGS) + sizeof(INTFRMLEN) + 10)

  188. static const char *scanformat(ktap_state_t *ks, const char *strfrmt, char *form)
  189. {
  190.     const char *p = strfrmt;
  191.     while (*p != '\0' && strchr(FLAGS, *p) != NULL)
  192.         p++;  /* skip flags */

  193.     if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) {
  194.         kp_error(ks, "invalid format (repeated flags)\n");
  195.         return NULL;
  196.     }

  197.     if (isdigit(uchar(*p)))
  198.         p++;  /* skip width */

  199.     if (isdigit(uchar(*p)))
  200.         p++;  /* (2 digits at most) */

  201.     if (*p == '.') {
  202.         p++;
  203.         if (isdigit(uchar(*p)))
  204.             p++;  /* skip precision */
  205.         if (isdigit(uchar(*p)))
  206.             p++;  /* (2 digits at most) */
  207.     }

  208.     if (isdigit(uchar(*p))) {
  209.         kp_error(ks, "invalid format (width or precision too long)\n");
  210.         return NULL;
  211.     }

  212.     *(form++) = '%';
  213.     memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char));
  214.     form += p - strfrmt + 1;
  215.     *form = '\0';
  216.     return p;
  217. }


  218. /*
  219. * add length modifier into formats
  220. */
  221. static void addlenmod(char *form, const char *lenmod)
  222. {
  223.     size_t l = strlen(form);
  224.     size_t lm = strlen(lenmod);
  225.     char spec = form[l - 1];

  226.     strcpy(form + l - 1, lenmod);
  227.     form[l + lm - 1] = spec;
  228.     form[l + lm] = '\0';
  229. }


  230. static void arg_error(ktap_state_t *ks, int narg, const char *extramsg)
  231. {
  232.     kp_error(ks, "bad argument #%d: (%s)\n", narg, extramsg);
  233. }

  234. int kp_str_fmt(ktap_state_t *ks, struct trace_seq *seq)
  235. {
  236.     int arg = 1;
  237.     size_t sfl;
  238.     ktap_val_t *arg_fmt = kp_arg(ks, 1);
  239.     int argnum = kp_arg_nr(ks);
  240.     const char *strfrmt, *strfrmt_end;

  241.     strfrmt = svalue(arg_fmt);
  242.     sfl = rawtsvalue(arg_fmt)->len;
  243.     strfrmt_end = strfrmt + sfl;

  244.     while (strfrmt < strfrmt_end) {
  245.         if (*strfrmt != L_ESC)
  246.             trace_seq_putc(seq, *strfrmt++);
  247.         else if (*++strfrmt == L_ESC)
  248.             trace_seq_putc(seq, *strfrmt++);
  249.         else { /* format item */
  250.             char form[MAX_FORMAT];

  251.             if (++arg > argnum) {
  252.                 arg_error(ks, arg, "no value");
  253.                 return -1;
  254.             }

  255.             strfrmt = scanformat(ks, strfrmt, form);
  256.             switch (*strfrmt++) {
  257.             case 'c':
  258.                 kp_arg_checknumber(ks, arg);

  259.                 trace_seq_printf(seq, form,
  260.                          nvalue(kp_arg(ks, arg)));
  261.                 break;
  262.             case 'd'case 'i': {
  263.                 ktap_number n;
  264.                 INTFRM_T ni;

  265.                 kp_arg_checknumber(ks, arg);

  266.                 n = nvalue(kp_arg(ks, arg));
  267.                 ni = (INTFRM_T)n;
  268.                 addlenmod(form, INTFRMLEN);
  269.                 trace_seq_printf(seq, form, ni);
  270.                 break;
  271.             }
  272.             case 'p': {
  273.                 char str[KSYM_SYMBOL_LEN];

  274.                 kp_arg_checknumber(ks, arg);

  275.                 SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg)));
  276.                 _trace_seq_puts(seq, str);
  277.                 break;
  278.             }
  279.             case 'o'case 'u'case 'x'case 'X': {
  280.                 ktap_number n;
  281.                 unsigned INTFRM_T ni;

  282.                 kp_arg_checknumber(ks, arg);

  283.                 n = nvalue(kp_arg(ks, arg));
  284.                 ni = (unsigned INTFRM_T)n;
  285.                 addlenmod(form, INTFRMLEN);
  286.                 trace_seq_printf(seq, form, ni);
  287.                 break;
  288.             }
  289.             case 's': {
  290.                 ktap_val_t *v = kp_arg(ks, arg);
  291.                 const char *s;
  292.                 size_t l;

  293.                 if (is_nil(v)) {
  294.                     _trace_seq_puts(seq, "nil");
  295.                     return 0;
  296.                 }

  297.                 if (is_eventstr(v)) {
  298.                     const char *str = kp_event_tostr(ks);
  299.                     if (!str)
  300.                         return  -1;
  301.                     _trace_seq_puts(seq,
  302.                         kp_event_tostr(ks));
  303.                     return 0;
  304.                 }

  305.                 kp_arg_checkstring(ks, arg);

  306.                 s = svalue(v);
  307.                 l = rawtsvalue(v)->len;
  308.                 if (!strchr(form, '.') && l >= 100) {
  309.                     /*
  310.                      * no precision and string is too long
  311.                      * to be formatted;
  312.                      * keep original string
  313.                      */
  314.                     _trace_seq_puts(seq, s);
  315.                     break;
  316.                 } else {
  317.                     trace_seq_printf(seq, form, s);
  318.                     break;
  319.                 }
  320.             }
  321.             default: /* also treat cases `pnLlh' */
  322.                 kp_error(ks, "invalid option " KTAP_QL("%%%c")
  323.                          " to " KTAP_QL("format"),
  324.                          *(strfrmt - 1));
  325.                 return -1;
  326.             }
  327.         }
  328.     }

  329.     return 0;
  330. }