Implement filtering infrastructure
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thu, 6 Nov 2014 22:21:59 +0000 (17:21 -0500)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Fri, 10 Apr 2015 20:41:45 +0000 (16:41 -0400)
Implement filtering infrastructure in the enablers. It is not yet active
in the tracepoint probes, since we need to modify the
LTTNG_TRACEPOINT_EVENT() to follow lttng-ust more closely beforehand.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
13 files changed:
Makefile
filter-bytecode.h [new file with mode: 0644]
lttng-abi.c
lttng-abi.h
lttng-context-vpid.c
lttng-context.c
lttng-events.c
lttng-events.h
lttng-filter-interpreter.c [new file with mode: 0644]
lttng-filter-specialize.c [new file with mode: 0644]
lttng-filter-validator.c [new file with mode: 0644]
lttng-filter.c [new file with mode: 0644]
lttng-filter.h [new file with mode: 0644]

index 23049ca34582b5e3f22e5b28465145c6111319c2..1234519a036d8c9f0a9baa1dc8dced92da6a9347 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,10 @@ lttng-tracer-objs :=  lttng-events.o lttng-abi.o \
                        lttng-context-vppid.o lttng-calibrate.o \
                        lttng-context-hostname.o wrapper/random.o \
                        probes/lttng.o wrapper/trace-clock.o \
-                       lttng-tracker-pid.o
+                       lttng-tracker-pid.o \
+                       lttng-filter.o lttng-filter-interpreter.o \
+                       lttng-filter-specialize.o \
+                       lttng-filter-validator.o
 
 obj-m += lttng-statedump.o
 lttng-statedump-objs := lttng-statedump-impl.o wrapper/irqdesc.o \
diff --git a/filter-bytecode.h b/filter-bytecode.h
new file mode 100644 (file)
index 0000000..eb0d549
--- /dev/null
@@ -0,0 +1,181 @@
+#ifndef _FILTER_BYTECODE_H
+#define _FILTER_BYTECODE_H
+
+/*
+ * filter-bytecode.h
+ *
+ * LTTng filter bytecode
+ *
+ * Copyright 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * offsets are absolute from start of bytecode.
+ */
+
+struct field_ref {
+       /* Initially, symbol offset. After link, field offset. */
+       uint16_t offset;
+} __attribute__((packed));
+
+struct literal_numeric {
+       int64_t v;
+} __attribute__((packed));
+
+struct literal_double {
+       double v;
+} __attribute__((packed));
+
+struct literal_string {
+       char string[0];
+} __attribute__((packed));
+
+enum filter_op {
+       FILTER_OP_UNKNOWN = 0,
+
+       FILTER_OP_RETURN,
+
+       /* binary */
+       FILTER_OP_MUL,
+       FILTER_OP_DIV,
+       FILTER_OP_MOD,
+       FILTER_OP_PLUS,
+       FILTER_OP_MINUS,
+       FILTER_OP_RSHIFT,
+       FILTER_OP_LSHIFT,
+       FILTER_OP_BIN_AND,
+       FILTER_OP_BIN_OR,
+       FILTER_OP_BIN_XOR,
+
+       /* binary comparators */
+       FILTER_OP_EQ,
+       FILTER_OP_NE,
+       FILTER_OP_GT,
+       FILTER_OP_LT,
+       FILTER_OP_GE,
+       FILTER_OP_LE,
+
+       /* string binary comparator */
+       FILTER_OP_EQ_STRING,
+       FILTER_OP_NE_STRING,
+       FILTER_OP_GT_STRING,
+       FILTER_OP_LT_STRING,
+       FILTER_OP_GE_STRING,
+       FILTER_OP_LE_STRING,
+
+       /* s64 binary comparator */
+       FILTER_OP_EQ_S64,
+       FILTER_OP_NE_S64,
+       FILTER_OP_GT_S64,
+       FILTER_OP_LT_S64,
+       FILTER_OP_GE_S64,
+       FILTER_OP_LE_S64,
+
+       /* double binary comparator */
+       FILTER_OP_EQ_DOUBLE,
+       FILTER_OP_NE_DOUBLE,
+       FILTER_OP_GT_DOUBLE,
+       FILTER_OP_LT_DOUBLE,
+       FILTER_OP_GE_DOUBLE,
+       FILTER_OP_LE_DOUBLE,
+
+       /* Mixed S64-double binary comparators */
+       FILTER_OP_EQ_DOUBLE_S64,
+       FILTER_OP_NE_DOUBLE_S64,
+       FILTER_OP_GT_DOUBLE_S64,
+       FILTER_OP_LT_DOUBLE_S64,
+       FILTER_OP_GE_DOUBLE_S64,
+       FILTER_OP_LE_DOUBLE_S64,
+
+       FILTER_OP_EQ_S64_DOUBLE,
+       FILTER_OP_NE_S64_DOUBLE,
+       FILTER_OP_GT_S64_DOUBLE,
+       FILTER_OP_LT_S64_DOUBLE,
+       FILTER_OP_GE_S64_DOUBLE,
+       FILTER_OP_LE_S64_DOUBLE,
+
+       /* unary */
+       FILTER_OP_UNARY_PLUS,
+       FILTER_OP_UNARY_MINUS,
+       FILTER_OP_UNARY_NOT,
+       FILTER_OP_UNARY_PLUS_S64,
+       FILTER_OP_UNARY_MINUS_S64,
+       FILTER_OP_UNARY_NOT_S64,
+       FILTER_OP_UNARY_PLUS_DOUBLE,
+       FILTER_OP_UNARY_MINUS_DOUBLE,
+       FILTER_OP_UNARY_NOT_DOUBLE,
+
+       /* logical */
+       FILTER_OP_AND,
+       FILTER_OP_OR,
+
+       /* load field ref */
+       FILTER_OP_LOAD_FIELD_REF,
+       FILTER_OP_LOAD_FIELD_REF_STRING,
+       FILTER_OP_LOAD_FIELD_REF_SEQUENCE,
+       FILTER_OP_LOAD_FIELD_REF_S64,
+       FILTER_OP_LOAD_FIELD_REF_DOUBLE,
+
+       /* load immediate from operand */
+       FILTER_OP_LOAD_STRING,
+       FILTER_OP_LOAD_S64,
+       FILTER_OP_LOAD_DOUBLE,
+
+       /* cast */
+       FILTER_OP_CAST_TO_S64,
+       FILTER_OP_CAST_DOUBLE_TO_S64,
+       FILTER_OP_CAST_NOP,
+
+       /* get context ref */
+       FILTER_OP_GET_CONTEXT_REF,
+       FILTER_OP_GET_CONTEXT_REF_STRING,
+       FILTER_OP_GET_CONTEXT_REF_S64,
+       FILTER_OP_GET_CONTEXT_REF_DOUBLE,
+
+       NR_FILTER_OPS,
+};
+
+typedef uint8_t filter_opcode_t;
+
+struct load_op {
+       filter_opcode_t op;
+       char data[0];
+       /* data to load. Size known by enum filter_opcode and null-term char. */
+} __attribute__((packed));
+
+struct binary_op {
+       filter_opcode_t op;
+} __attribute__((packed));
+
+struct unary_op {
+       filter_opcode_t op;
+} __attribute__((packed));
+
+/* skip_offset is absolute from start of bytecode */
+struct logical_op {
+       filter_opcode_t op;
+       uint16_t skip_offset;   /* bytecode insn, if skip second test */
+} __attribute__((packed));
+
+struct cast_op {
+       filter_opcode_t op;
+} __attribute__((packed));
+
+struct return_op {
+       filter_opcode_t op;
+} __attribute__((packed));
+
+#endif /* _FILTER_BYTECODE_H */
index ede2ae79bd7b15c45ec4ef3864a048cc85f2e006..beaad907d072ca5db410d8fc2a23e1feb0d17a25 100644 (file)
@@ -1323,6 +1323,18 @@ long lttng_event_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        WARN_ON_ONCE(1);
                        return -ENOSYS;
                }
+       case LTTNG_KERNEL_FILTER:
+               switch (*evtype) {
+               case LTTNG_TYPE_EVENT:
+                       return -EINVAL;
+               case LTTNG_TYPE_ENABLER:
+               {
+                       enabler = file->private_data;
+                       return lttng_enabler_attach_bytecode(enabler,
+                               (struct lttng_kernel_filter_bytecode __user *) arg);
+               }
+
+               }
        default:
                return -ENOIOCTLCMD;
        }
index 7239f76f5f41f5eb63cce30f808a5d1339d3228c..e029bc70f3e862dc4e54800f3e12488a0b61dda2 100644 (file)
@@ -161,6 +161,14 @@ struct lttng_kernel_context {
        } u;
 } __attribute__((packed));
 
+#define FILTER_BYTECODE_MAX_LEN                65536
+struct lttng_kernel_filter_bytecode {
+       uint32_t len;
+       uint32_t reloc_offset;
+       uint64_t seqnum;
+       char data[0];
+} __attribute__((packed));
+
 /* LTTng file descriptor ioctl */
 #define LTTNG_KERNEL_SESSION                   _IO(0xF6, 0x45)
 #define LTTNG_KERNEL_TRACER_VERSION            \
@@ -201,6 +209,9 @@ struct lttng_kernel_context {
 #define LTTNG_KERNEL_ENABLE                    _IO(0xF6, 0x82)
 #define LTTNG_KERNEL_DISABLE                   _IO(0xF6, 0x83)
 
+/* Event FD ioctl */
+#define LTTNG_KERNEL_FILTER                    _IO(0xF6, 0x90)
+
 /* LTTng-specific ioctls for the lib ringbuffer */
 /* returns the timestamp begin of the current sub-buffer */
 #define LTTNG_RING_BUFFER_GET_TIMESTAMP_BEGIN  _IOR(0xF6, 0x20, uint64_t)
index ccb2023d3329b1050d0e87f0068a586a4b7c0e00..9a8df9a972b89f7c32dfa4335cde048f6b9ae1e5 100644 (file)
@@ -56,6 +56,22 @@ void vpid_record(struct lttng_ctx_field *field,
        chan->ops->event_write(ctx, &vpid, sizeof(vpid));
 }
 
+static
+void vpid_get_value(struct lttng_ctx_field *field,
+               union lttng_ctx_value *value)
+{
+       pid_t vpid;
+
+       /*
+        * nsproxy can be NULL when scheduled out of exit.
+        */
+       if (!current->nsproxy)
+               vpid = 0;
+       else
+               vpid = task_tgid_vnr(current);
+       value->s64 = vpid;
+}
+
 int lttng_add_vpid_to_ctx(struct lttng_ctx **ctx)
 {
        struct lttng_ctx_field *field;
@@ -77,6 +93,7 @@ int lttng_add_vpid_to_ctx(struct lttng_ctx **ctx)
        field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
        field->get_size = vpid_get_size;
        field->record = vpid_record;
+       field->get_value = vpid_get_value;
        lttng_context_update(*ctx);
        wrapper_vmalloc_sync_all();
        return 0;
index b79b7f667bf483ce6611cfae31190fb1c19f12b4..b625f4391de8d40d634c436ec04253b1808ab81d 100644 (file)
 #include "lttng-events.h"
 #include "lttng-tracer.h"
 
+/*
+ * The filter implementation requires that two consecutive "get" for the
+ * same context performed by the same thread return the same result.
+ */
+
+/*
+ * Static array of contexts, for $ctx filters.
+ */
+struct lttng_ctx *lttng_static_ctx;
+
 int lttng_find_context(struct lttng_ctx *ctx, const char *name)
 {
        unsigned int i;
@@ -43,6 +53,23 @@ int lttng_find_context(struct lttng_ctx *ctx, const char *name)
 }
 EXPORT_SYMBOL_GPL(lttng_find_context);
 
+int lttng_get_context_index(struct lttng_ctx *ctx, const char *name)
+{
+       unsigned int i;
+
+       if (!ctx)
+               return -1;
+       for (i = 0; i < ctx->nr_fields; i++) {
+               /* Skip allocated (but non-initialized) contexts */
+               if (!ctx->fields[i].event_field.name)
+                       continue;
+               if (!strcmp(ctx->fields[i].event_field.name, name))
+                       return i;
+       }
+       return -1;
+}
+EXPORT_SYMBOL_GPL(lttng_get_context_index);
+
 /*
  * Note: as we append context information, the pointer location may change.
  */
@@ -192,3 +219,58 @@ void lttng_destroy_context(struct lttng_ctx *ctx)
        kfree(ctx->fields);
        kfree(ctx);
 }
+
+int lttng_context_init(void)
+{
+       int ret;
+
+       ret = lttng_add_vpid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+#if 0  //TODO
+       ret = lttng_add_pid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_pthread_id_to_ctx");
+       }
+       ret = lttng_add_procname_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_vtid_to_ctx");
+       }
+       ret = lttng_add_prio_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_vpid_to_ctx");
+       }
+       ret = lttng_add_nice_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+       ret = lttng_add_tid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+       ret = lttng_add_vtid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+       ret = lttng_add_ppid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+       ret = lttng_add_vppid_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+       ret = lttng_add_hostname_to_ctx(&lttng_static_ctx);
+       if (ret) {
+               printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+       }
+#endif
+       return 0;
+}
+
+void lttng_context_exit(void)
+{
+       lttng_destroy_context(lttng_static_ctx);
+       lttng_static_ctx = NULL;
+}
index 430307686b10a3a2f4c2ae3403fc349db51e2592..c103c6ed0d8a53392afd21352734587819bb3d2e 100644 (file)
@@ -512,6 +512,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan,
        event->id = chan->free_event_id++;
        event->instrumentation = itype;
        event->evtype = LTTNG_TYPE_EVENT;
+       INIT_LIST_HEAD(&event->bytecode_runtime_head);
        INIT_LIST_HEAD(&event->enablers_ref_head);
 
        switch (itype) {
@@ -1246,6 +1247,7 @@ struct lttng_enabler *lttng_enabler_create(enum lttng_enabler_type type,
        if (!enabler)
                return NULL;
        enabler->type = type;
+       INIT_LIST_HEAD(&enabler->filter_bytecode_head);
        memcpy(&enabler->event_param, event_param,
                sizeof(enabler->event_param));
        enabler->chan = chan;
@@ -1277,6 +1279,36 @@ int lttng_enabler_disable(struct lttng_enabler *enabler)
        return 0;
 }
 
+int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler,
+               struct lttng_kernel_filter_bytecode __user *bytecode)
+{
+       struct lttng_filter_bytecode_node *bytecode_node;
+       uint32_t bytecode_len;
+       int ret;
+
+       ret = get_user(bytecode_len, &bytecode->len);
+       if (ret)
+               return ret;
+       bytecode_node = kzalloc(sizeof(*bytecode_node) + bytecode_len,
+                       GFP_KERNEL);
+       if (!bytecode_node)
+               return -ENOMEM;
+       ret = copy_from_user(&bytecode_node->bc, bytecode,
+               sizeof(*bytecode) + bytecode_len);
+       if (ret)
+               goto error_free;
+       bytecode_node->enabler = enabler;
+       /* Enforce length based on allocated size */
+       bytecode_node->bc.len = bytecode_len;
+       list_add_tail(&bytecode_node->node, &enabler->filter_bytecode_head);
+       lttng_session_lazy_sync_enablers(enabler->chan->session);
+       return 0;
+
+error_free:
+       kfree(bytecode_node);
+       return ret;
+}
+
 int lttng_enabler_attach_context(struct lttng_enabler *enabler,
                struct lttng_kernel_context *context_param)
 {
@@ -1286,6 +1318,14 @@ int lttng_enabler_attach_context(struct lttng_enabler *enabler,
 static
 void lttng_enabler_destroy(struct lttng_enabler *enabler)
 {
+       struct lttng_filter_bytecode_node *filter_node, *tmp_filter_node;
+
+       /* Destroy filter bytecode */
+       list_for_each_entry_safe(filter_node, tmp_filter_node,
+                       &enabler->filter_bytecode_head, node) {
+               kfree(filter_node);
+       }
+
        /* Destroy contexts */
        lttng_destroy_context(enabler->ctx);
 
@@ -1313,7 +1353,8 @@ void lttng_session_sync_enablers(struct lttng_session *session)
         */
        list_for_each_entry(event, &session->events, list) {
                struct lttng_enabler_ref *enabler_ref;
-               int enabled = 0;
+               struct lttng_bytecode_runtime *runtime;
+               int enabled = 0, has_enablers_without_bytecode = 0;
 
                switch (event->instrumentation) {
                case LTTNG_KERNEL_TRACEPOINT:
@@ -1348,6 +1389,24 @@ void lttng_session_sync_enablers(struct lttng_session *session)
                } else {
                        _lttng_event_unregister(event);
                }
+
+               /* Check if has enablers without bytecode enabled */
+               list_for_each_entry(enabler_ref,
+                               &event->enablers_ref_head, node) {
+                       if (enabler_ref->ref->enabled
+                                       && list_empty(&enabler_ref->ref->filter_bytecode_head)) {
+                               has_enablers_without_bytecode = 1;
+                               break;
+                       }
+               }
+               event->has_enablers_without_bytecode =
+                       has_enablers_without_bytecode;
+
+               /* Enable filters */
+               list_for_each_entry(runtime,
+                               &event->bytecode_runtime_head, node) {
+                       lttng_filter_sync_state(runtime);
+               }
        }
 }
 
@@ -2053,9 +2112,12 @@ static int __init lttng_events_init(void)
        if (ret)
                return ret;
 
-       ret = lttng_tracepoint_init();
+       ret = lttng_context_init();
        if (ret)
                return ret;
+       ret = lttng_tracepoint_init();
+       if (ret)
+               goto error_tp;
        event_cache = KMEM_CACHE(lttng_event, 0);
        if (!event_cache) {
                ret = -ENOMEM;
@@ -2075,6 +2137,8 @@ error_abi:
        kmem_cache_destroy(event_cache);
 error_kmem:
        lttng_tracepoint_exit();
+error_tp:
+       lttng_context_exit();
        return ret;
 }
 
@@ -2090,6 +2154,7 @@ static void __exit lttng_events_exit(void)
                lttng_session_destroy(session);
        kmem_cache_destroy(event_cache);
        lttng_tracepoint_exit();
+       lttng_context_exit();
 }
 
 module_exit(lttng_events_exit);
index c360af0b0ebb7c7a659900fb18be0fea969f6ce3..d9fdd0cf091b117d68f5cfe0de0edf99af1c835a 100644 (file)
@@ -140,6 +140,12 @@ struct lttng_event_field {
        struct lttng_type type;
 };
 
+union lttng_ctx_value {
+       int64_t s64;
+       const char *str;
+       double d;
+};
+
 /*
  * We need to keep this perf counter field separately from struct
  * lttng_ctx_field because cpu hotplug needs fixed-location addresses.
@@ -157,6 +163,8 @@ struct lttng_ctx_field {
        void (*record)(struct lttng_ctx_field *field,
                       struct lib_ring_buffer_ctx *ctx,
                       struct lttng_channel *chan);
+       void (*get_value)(struct lttng_ctx_field *field,
+                        union lttng_ctx_value *value);
        union {
                struct lttng_perf_counter_field *perf_counter;
        } u;
@@ -196,6 +204,33 @@ enum lttng_event_type {
        LTTNG_TYPE_ENABLER = 1,
 };
 
+struct lttng_filter_bytecode_node {
+       struct list_head node;
+       struct lttng_enabler *enabler;
+       /*
+        * struct lttng_kernel_filter_bytecode has var. sized array, must be
+        * last field.
+        */
+       struct lttng_kernel_filter_bytecode bc;
+};
+
+/*
+ * Filter return value masks.
+ */
+enum lttng_filter_ret {
+       LTTNG_FILTER_DISCARD = 0,
+       LTTNG_FILTER_RECORD_FLAG = (1ULL << 0),
+       /* Other bits are kept for future use. */
+};
+
+struct lttng_bytecode_runtime {
+       /* Associated bytecode */
+       struct lttng_filter_bytecode_node *bc;
+       uint64_t (*filter)(void *filter_data, const char *filter_stack_data);
+       int link_failed;
+       struct list_head node;  /* list of bytecode runtime in event */
+};
+
 /*
  * Objects in a linked-list of enablers, owned by an event.
  */
@@ -237,6 +272,9 @@ struct lttng_event {
        struct list_head enablers_ref_head;
        struct hlist_node hlist;        /* session ht of events */
        int registered;                 /* has reg'd tracepoint probe */
+       /* list of struct lttng_bytecode_runtime, sorted by seqnum */
+       struct list_head bytecode_runtime_head;
+       int has_enablers_without_bytecode;
 };
 
 enum lttng_enabler_type {
@@ -254,6 +292,8 @@ struct lttng_enabler {
        enum lttng_enabler_type type;
 
        struct list_head node;  /* per-session list of enablers */
+       /* head list of struct lttng_ust_filter_bytecode_node */
+       struct list_head filter_bytecode_head;
 
        struct lttng_kernel_event event_param;
        struct lttng_channel *chan;
@@ -555,9 +595,18 @@ int lttng_abi_syscall_list(void)
 }
 #endif
 
+void lttng_filter_sync_state(struct lttng_bytecode_runtime *runtime);
+int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler,
+               struct lttng_kernel_filter_bytecode __user *bytecode);
+
+extern struct lttng_ctx *lttng_static_ctx;
+
+int lttng_context_init(void);
+void lttng_context_exit(void);
 struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx);
 void lttng_context_update(struct lttng_ctx *ctx);
 int lttng_find_context(struct lttng_ctx *ctx, const char *name);
+int lttng_get_context_index(struct lttng_ctx *ctx, const char *name);
 void lttng_remove_context_field(struct lttng_ctx **ctx,
                                struct lttng_ctx_field *field);
 void lttng_destroy_context(struct lttng_ctx *ctx);
diff --git a/lttng-filter-interpreter.c b/lttng-filter-interpreter.c
new file mode 100644 (file)
index 0000000..4018264
--- /dev/null
@@ -0,0 +1,746 @@
+/*
+ * lttng-filter-interpreter.c
+ *
+ * LTTng modules filter interpreter.
+ *
+ * Copyright (C) 2010-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lttng-filter.h"
+
+/*
+ * -1: wildcard found.
+ * -2: unknown escape char.
+ * 0: normal char.
+ */
+
+static
+int parse_char(const char **p)
+{
+       switch (**p) {
+       case '\\':
+               (*p)++;
+               switch (**p) {
+               case '\\':
+               case '*':
+                       return 0;
+               default:
+                       return -2;
+               }
+       case '*':
+               return -1;
+       default:
+               return 0;
+       }
+}
+
+static
+int stack_strcmp(struct estack *stack, int top, const char *cmp_type)
+{
+       const char *p = estack_bx(stack, top)->u.s.str, *q = estack_ax(stack, top)->u.s.str;
+       int ret;
+       int diff;
+
+       for (;;) {
+               int escaped_r0 = 0;
+
+               if (unlikely(p - estack_bx(stack, top)->u.s.str > estack_bx(stack, top)->u.s.seq_len || *p == '\0')) {
+                       if (q - estack_ax(stack, top)->u.s.str > estack_ax(stack, top)->u.s.seq_len || *q == '\0') {
+                               return 0;
+                       } else {
+                               if (estack_ax(stack, top)->u.s.literal) {
+                                       ret = parse_char(&q);
+                                       if (ret == -1)
+                                               return 0;
+                               }
+                               return -1;
+                       }
+               }
+               if (unlikely(q - estack_ax(stack, top)->u.s.str > estack_ax(stack, top)->u.s.seq_len || *q == '\0')) {
+                       if (p - estack_bx(stack, top)->u.s.str > estack_bx(stack, top)->u.s.seq_len || *p == '\0') {
+                               return 0;
+                       } else {
+                               if (estack_bx(stack, top)->u.s.literal) {
+                                       ret = parse_char(&p);
+                                       if (ret == -1)
+                                               return 0;
+                               }
+                               return 1;
+                       }
+               }
+               if (estack_bx(stack, top)->u.s.literal) {
+                       ret = parse_char(&p);
+                       if (ret == -1) {
+                               return 0;
+                       } else if (ret == -2) {
+                               escaped_r0 = 1;
+                       }
+                       /* else compare both char */
+               }
+               if (estack_ax(stack, top)->u.s.literal) {
+                       ret = parse_char(&q);
+                       if (ret == -1) {
+                               return 0;
+                       } else if (ret == -2) {
+                               if (!escaped_r0)
+                                       return -1;
+                       } else {
+                               if (escaped_r0)
+                                       return 1;
+                       }
+               } else {
+                       if (escaped_r0)
+                               return 1;
+               }
+               diff = *p - *q;
+               if (diff != 0)
+                       break;
+               p++;
+               q++;
+       }
+       return diff;
+}
+
+uint64_t lttng_filter_false(void *filter_data,
+               const char *filter_stack_data)
+{
+       return 0;
+}
+
+#ifdef INTERPRETER_USE_SWITCH
+
+/*
+ * Fallback for compilers that do not support taking address of labels.
+ */
+
+#define START_OP                                                       \
+       start_pc = &bytecode->data[0];                                  \
+       for (pc = next_pc = start_pc; pc - start_pc < bytecode->len;    \
+                       pc = next_pc) {                                 \
+               dbg_printk("Executing op %s (%u)\n",                    \
+                       lttng_filter_print_op((unsigned int) *(filter_opcode_t *) pc), \
+                       (unsigned int) *(filter_opcode_t *) pc);        \
+               switch (*(filter_opcode_t *) pc)        {
+
+#define OP(name)       case name
+
+#define PO             break
+
+#define END_OP         }                                               \
+       }
+
+#else
+
+/*
+ * Dispatch-table based interpreter.
+ */
+
+#define START_OP                                                       \
+       start_pc = &bytecode->data[0];                                  \
+       pc = next_pc = start_pc;                                        \
+       if (unlikely(pc - start_pc >= bytecode->len))                   \
+               goto end;                                               \
+       goto *dispatch[*(filter_opcode_t *) pc];
+
+#define OP(name)                                                       \
+LABEL_##name
+
+#define PO                                                             \
+               pc = next_pc;                                           \
+               goto *dispatch[*(filter_opcode_t *) pc];
+
+#define END_OP
+
+#endif
+
+/*
+ * Return 0 (discard), or raise the 0x1 flag (log event).
+ * Currently, other flags are kept for future extensions and have no
+ * effect.
+ */
+uint64_t lttng_filter_interpret_bytecode(void *filter_data,
+               const char *filter_stack_data)
+{
+       struct bytecode_runtime *bytecode = filter_data;
+       void *pc, *next_pc, *start_pc;
+       int ret = -EINVAL;
+       uint64_t retval = 0;
+       struct estack _stack;
+       struct estack *stack = &_stack;
+       register int64_t ax = 0, bx = 0;
+       register int top = FILTER_STACK_EMPTY;
+#ifndef INTERPRETER_USE_SWITCH
+       static void *dispatch[NR_FILTER_OPS] = {
+               [ FILTER_OP_UNKNOWN ] = &&LABEL_FILTER_OP_UNKNOWN,
+
+               [ FILTER_OP_RETURN ] = &&LABEL_FILTER_OP_RETURN,
+
+               /* binary */
+               [ FILTER_OP_MUL ] = &&LABEL_FILTER_OP_MUL,
+               [ FILTER_OP_DIV ] = &&LABEL_FILTER_OP_DIV,
+               [ FILTER_OP_MOD ] = &&LABEL_FILTER_OP_MOD,
+               [ FILTER_OP_PLUS ] = &&LABEL_FILTER_OP_PLUS,
+               [ FILTER_OP_MINUS ] = &&LABEL_FILTER_OP_MINUS,
+               [ FILTER_OP_RSHIFT ] = &&LABEL_FILTER_OP_RSHIFT,
+               [ FILTER_OP_LSHIFT ] = &&LABEL_FILTER_OP_LSHIFT,
+               [ FILTER_OP_BIN_AND ] = &&LABEL_FILTER_OP_BIN_AND,
+               [ FILTER_OP_BIN_OR ] = &&LABEL_FILTER_OP_BIN_OR,
+               [ FILTER_OP_BIN_XOR ] = &&LABEL_FILTER_OP_BIN_XOR,
+
+               /* binary comparators */
+               [ FILTER_OP_EQ ] = &&LABEL_FILTER_OP_EQ,
+               [ FILTER_OP_NE ] = &&LABEL_FILTER_OP_NE,
+               [ FILTER_OP_GT ] = &&LABEL_FILTER_OP_GT,
+               [ FILTER_OP_LT ] = &&LABEL_FILTER_OP_LT,
+               [ FILTER_OP_GE ] = &&LABEL_FILTER_OP_GE,
+               [ FILTER_OP_LE ] = &&LABEL_FILTER_OP_LE,
+
+               /* string binary comparator */
+               [ FILTER_OP_EQ_STRING ] = &&LABEL_FILTER_OP_EQ_STRING,
+               [ FILTER_OP_NE_STRING ] = &&LABEL_FILTER_OP_NE_STRING,
+               [ FILTER_OP_GT_STRING ] = &&LABEL_FILTER_OP_GT_STRING,
+               [ FILTER_OP_LT_STRING ] = &&LABEL_FILTER_OP_LT_STRING,
+               [ FILTER_OP_GE_STRING ] = &&LABEL_FILTER_OP_GE_STRING,
+               [ FILTER_OP_LE_STRING ] = &&LABEL_FILTER_OP_LE_STRING,
+
+               /* s64 binary comparator */
+               [ FILTER_OP_EQ_S64 ] = &&LABEL_FILTER_OP_EQ_S64,
+               [ FILTER_OP_NE_S64 ] = &&LABEL_FILTER_OP_NE_S64,
+               [ FILTER_OP_GT_S64 ] = &&LABEL_FILTER_OP_GT_S64,
+               [ FILTER_OP_LT_S64 ] = &&LABEL_FILTER_OP_LT_S64,
+               [ FILTER_OP_GE_S64 ] = &&LABEL_FILTER_OP_GE_S64,
+               [ FILTER_OP_LE_S64 ] = &&LABEL_FILTER_OP_LE_S64,
+
+               /* double binary comparator */
+               [ FILTER_OP_EQ_DOUBLE ] = &&LABEL_FILTER_OP_EQ_DOUBLE,
+               [ FILTER_OP_NE_DOUBLE ] = &&LABEL_FILTER_OP_NE_DOUBLE,
+               [ FILTER_OP_GT_DOUBLE ] = &&LABEL_FILTER_OP_GT_DOUBLE,
+               [ FILTER_OP_LT_DOUBLE ] = &&LABEL_FILTER_OP_LT_DOUBLE,
+               [ FILTER_OP_GE_DOUBLE ] = &&LABEL_FILTER_OP_GE_DOUBLE,
+               [ FILTER_OP_LE_DOUBLE ] = &&LABEL_FILTER_OP_LE_DOUBLE,
+
+               /* Mixed S64-double binary comparators */
+               [ FILTER_OP_EQ_DOUBLE_S64 ] = &&LABEL_FILTER_OP_EQ_DOUBLE_S64,
+               [ FILTER_OP_NE_DOUBLE_S64 ] = &&LABEL_FILTER_OP_NE_DOUBLE_S64,
+               [ FILTER_OP_GT_DOUBLE_S64 ] = &&LABEL_FILTER_OP_GT_DOUBLE_S64,
+               [ FILTER_OP_LT_DOUBLE_S64 ] = &&LABEL_FILTER_OP_LT_DOUBLE_S64,
+               [ FILTER_OP_GE_DOUBLE_S64 ] = &&LABEL_FILTER_OP_GE_DOUBLE_S64,
+               [ FILTER_OP_LE_DOUBLE_S64 ] = &&LABEL_FILTER_OP_LE_DOUBLE_S64,
+
+               [ FILTER_OP_EQ_S64_DOUBLE ] = &&LABEL_FILTER_OP_EQ_S64_DOUBLE,
+               [ FILTER_OP_NE_S64_DOUBLE ] = &&LABEL_FILTER_OP_NE_S64_DOUBLE,
+               [ FILTER_OP_GT_S64_DOUBLE ] = &&LABEL_FILTER_OP_GT_S64_DOUBLE,
+               [ FILTER_OP_LT_S64_DOUBLE ] = &&LABEL_FILTER_OP_LT_S64_DOUBLE,
+               [ FILTER_OP_GE_S64_DOUBLE ] = &&LABEL_FILTER_OP_GE_S64_DOUBLE,
+               [ FILTER_OP_LE_S64_DOUBLE ] = &&LABEL_FILTER_OP_LE_S64_DOUBLE,
+
+               /* unary */
+               [ FILTER_OP_UNARY_PLUS ] = &&LABEL_FILTER_OP_UNARY_PLUS,
+               [ FILTER_OP_UNARY_MINUS ] = &&LABEL_FILTER_OP_UNARY_MINUS,
+               [ FILTER_OP_UNARY_NOT ] = &&LABEL_FILTER_OP_UNARY_NOT,
+               [ FILTER_OP_UNARY_PLUS_S64 ] = &&LABEL_FILTER_OP_UNARY_PLUS_S64,
+               [ FILTER_OP_UNARY_MINUS_S64 ] = &&LABEL_FILTER_OP_UNARY_MINUS_S64,
+               [ FILTER_OP_UNARY_NOT_S64 ] = &&LABEL_FILTER_OP_UNARY_NOT_S64,
+               [ FILTER_OP_UNARY_PLUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_PLUS_DOUBLE,
+               [ FILTER_OP_UNARY_MINUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_MINUS_DOUBLE,
+               [ FILTER_OP_UNARY_NOT_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_NOT_DOUBLE,
+
+               /* logical */
+               [ FILTER_OP_AND ] = &&LABEL_FILTER_OP_AND,
+               [ FILTER_OP_OR ] = &&LABEL_FILTER_OP_OR,
+
+               /* load field ref */
+               [ FILTER_OP_LOAD_FIELD_REF ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF,
+               [ FILTER_OP_LOAD_FIELD_REF_STRING ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_STRING,
+               [ FILTER_OP_LOAD_FIELD_REF_SEQUENCE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_SEQUENCE,
+               [ FILTER_OP_LOAD_FIELD_REF_S64 ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_S64,
+               [ FILTER_OP_LOAD_FIELD_REF_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_DOUBLE,
+
+               /* load from immediate operand */
+               [ FILTER_OP_LOAD_STRING ] = &&LABEL_FILTER_OP_LOAD_STRING,
+               [ FILTER_OP_LOAD_S64 ] = &&LABEL_FILTER_OP_LOAD_S64,
+               [ FILTER_OP_LOAD_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_DOUBLE,
+
+               /* cast */
+               [ FILTER_OP_CAST_TO_S64 ] = &&LABEL_FILTER_OP_CAST_TO_S64,
+               [ FILTER_OP_CAST_DOUBLE_TO_S64 ] = &&LABEL_FILTER_OP_CAST_DOUBLE_TO_S64,
+               [ FILTER_OP_CAST_NOP ] = &&LABEL_FILTER_OP_CAST_NOP,
+
+               /* get context ref */
+               [ FILTER_OP_GET_CONTEXT_REF ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF,
+               [ FILTER_OP_GET_CONTEXT_REF_STRING ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_STRING,
+               [ FILTER_OP_GET_CONTEXT_REF_S64 ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_S64,
+               [ FILTER_OP_GET_CONTEXT_REF_DOUBLE ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_DOUBLE,
+       };
+#endif /* #ifndef INTERPRETER_USE_SWITCH */
+
+       START_OP
+
+               OP(FILTER_OP_UNKNOWN):
+               OP(FILTER_OP_LOAD_FIELD_REF):
+               OP(FILTER_OP_GET_CONTEXT_REF):
+#ifdef INTERPRETER_USE_SWITCH
+               default:
+#endif /* INTERPRETER_USE_SWITCH */
+                       printk(KERN_WARNING "unknown bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               OP(FILTER_OP_RETURN):
+                       /* LTTNG_FILTER_DISCARD  or LTTNG_FILTER_RECORD_FLAG */
+                       retval = !!estack_ax_v;
+                       ret = 0;
+                       goto end;
+
+               /* binary */
+               OP(FILTER_OP_MUL):
+               OP(FILTER_OP_DIV):
+               OP(FILTER_OP_MOD):
+               OP(FILTER_OP_PLUS):
+               OP(FILTER_OP_MINUS):
+               OP(FILTER_OP_RSHIFT):
+               OP(FILTER_OP_LSHIFT):
+               OP(FILTER_OP_BIN_AND):
+               OP(FILTER_OP_BIN_OR):
+               OP(FILTER_OP_BIN_XOR):
+                       printk(KERN_WARNING "unsupported bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               OP(FILTER_OP_EQ):
+               OP(FILTER_OP_NE):
+               OP(FILTER_OP_GT):
+               OP(FILTER_OP_LT):
+               OP(FILTER_OP_GE):
+               OP(FILTER_OP_LE):
+                       printk(KERN_WARNING "unsupported non-specialized bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               OP(FILTER_OP_EQ_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, "==") == 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_NE_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, "!=") != 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_GT_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, ">") > 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_LT_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, "<") < 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_GE_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, ">=") >= 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_LE_STRING):
+               {
+                       int res;
+
+                       res = (stack_strcmp(stack, top, "<=") <= 0);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+
+               OP(FILTER_OP_EQ_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v == estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_NE_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v != estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_GT_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v > estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_LT_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v < estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_GE_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v >= estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+               OP(FILTER_OP_LE_S64):
+               {
+                       int res;
+
+                       res = (estack_bx_v <= estack_ax_v);
+                       estack_pop(stack, top, ax, bx);
+                       estack_ax_v = res;
+                       next_pc += sizeof(struct binary_op);
+                       PO;
+               }
+
+               OP(FILTER_OP_EQ_DOUBLE):
+               OP(FILTER_OP_NE_DOUBLE):
+               OP(FILTER_OP_GT_DOUBLE):
+               OP(FILTER_OP_LT_DOUBLE):
+               OP(FILTER_OP_GE_DOUBLE):
+               OP(FILTER_OP_LE_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               /* Mixed S64-double binary comparators */
+               OP(FILTER_OP_EQ_DOUBLE_S64):
+               OP(FILTER_OP_NE_DOUBLE_S64):
+               OP(FILTER_OP_GT_DOUBLE_S64):
+               OP(FILTER_OP_LT_DOUBLE_S64):
+               OP(FILTER_OP_GE_DOUBLE_S64):
+               OP(FILTER_OP_LE_DOUBLE_S64):
+               OP(FILTER_OP_EQ_S64_DOUBLE):
+               OP(FILTER_OP_NE_S64_DOUBLE):
+               OP(FILTER_OP_GT_S64_DOUBLE):
+               OP(FILTER_OP_LT_S64_DOUBLE):
+               OP(FILTER_OP_GE_S64_DOUBLE):
+               OP(FILTER_OP_LE_S64_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               /* unary */
+               OP(FILTER_OP_UNARY_PLUS):
+               OP(FILTER_OP_UNARY_MINUS):
+               OP(FILTER_OP_UNARY_NOT):
+                       printk(KERN_WARNING "unsupported non-specialized bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+
+               OP(FILTER_OP_UNARY_PLUS_S64):
+               {
+                       next_pc += sizeof(struct unary_op);
+                       PO;
+               }
+               OP(FILTER_OP_UNARY_MINUS_S64):
+               {
+                       estack_ax_v = -estack_ax_v;
+                       next_pc += sizeof(struct unary_op);
+                       PO;
+               }
+               OP(FILTER_OP_UNARY_PLUS_DOUBLE):
+               OP(FILTER_OP_UNARY_MINUS_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+               OP(FILTER_OP_UNARY_NOT_S64):
+               {
+                       estack_ax_v = !estack_ax_v;
+                       next_pc += sizeof(struct unary_op);
+                       PO;
+               }
+               OP(FILTER_OP_UNARY_NOT_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               /* logical */
+               OP(FILTER_OP_AND):
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       /* If AX is 0, skip and evaluate to 0 */
+                       if (unlikely(estack_ax_v == 0)) {
+                               dbg_printk("Jumping to bytecode offset %u\n",
+                                       (unsigned int) insn->skip_offset);
+                               next_pc = start_pc + insn->skip_offset;
+                       } else {
+                               /* Pop 1 when jump not taken */
+                               estack_pop(stack, top, ax, bx);
+                               next_pc += sizeof(struct logical_op);
+                       }
+                       PO;
+               }
+               OP(FILTER_OP_OR):
+               {
+                       struct logical_op *insn = (struct logical_op *) pc;
+
+                       /* If AX is nonzero, skip and evaluate to 1 */
+
+                       if (unlikely(estack_ax_v != 0)) {
+                               estack_ax_v = 1;
+                               dbg_printk("Jumping to bytecode offset %u\n",
+                                       (unsigned int) insn->skip_offset);
+                               next_pc = start_pc + insn->skip_offset;
+                       } else {
+                               /* Pop 1 when jump not taken */
+                               estack_pop(stack, top, ax, bx);
+                               next_pc += sizeof(struct logical_op);
+                       }
+                       PO;
+               }
+
+
+               /* load field ref */
+               OP(FILTER_OP_LOAD_FIELD_REF_STRING):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+                       struct field_ref *ref = (struct field_ref *) insn->data;
+
+                       dbg_printk("load field ref offset %u type string\n",
+                               ref->offset);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax(stack, top)->u.s.str =
+                               *(const char * const *) &filter_stack_data[ref->offset];
+                       if (unlikely(!estack_ax(stack, top)->u.s.str)) {
+                               dbg_printk("Filter warning: loading a NULL string.\n");
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       estack_ax(stack, top)->u.s.seq_len = UINT_MAX;
+                       estack_ax(stack, top)->u.s.literal = 0;
+                       dbg_printk("ref load string %s\n", estack_ax(stack, top)->u.s.str);
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       PO;
+               }
+
+               OP(FILTER_OP_LOAD_FIELD_REF_SEQUENCE):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+                       struct field_ref *ref = (struct field_ref *) insn->data;
+
+                       dbg_printk("load field ref offset %u type sequence\n",
+                               ref->offset);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax(stack, top)->u.s.seq_len =
+                               *(unsigned long *) &filter_stack_data[ref->offset];
+                       estack_ax(stack, top)->u.s.str =
+                               *(const char **) (&filter_stack_data[ref->offset
+                                                               + sizeof(unsigned long)]);
+                       if (unlikely(!estack_ax(stack, top)->u.s.str)) {
+                               dbg_printk("Filter warning: loading a NULL sequence.\n");
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       estack_ax(stack, top)->u.s.literal = 0;
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       PO;
+               }
+
+               OP(FILTER_OP_LOAD_FIELD_REF_S64):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+                       struct field_ref *ref = (struct field_ref *) insn->data;
+
+                       dbg_printk("load field ref offset %u type s64\n",
+                               ref->offset);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax_v =
+                               ((struct literal_numeric *) &filter_stack_data[ref->offset])->v;
+                       dbg_printk("ref load s64 %lld\n",
+                               (long long) estack_ax_v);
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       PO;
+               }
+
+               OP(FILTER_OP_LOAD_FIELD_REF_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               /* load from immediate operand */
+               OP(FILTER_OP_LOAD_STRING):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+
+                       dbg_printk("load string %s\n", insn->data);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax(stack, top)->u.s.str = insn->data;
+                       estack_ax(stack, top)->u.s.seq_len = UINT_MAX;
+                       estack_ax(stack, top)->u.s.literal = 1;
+                       next_pc += sizeof(struct load_op) + strlen(insn->data) + 1;
+                       PO;
+               }
+
+               OP(FILTER_OP_LOAD_S64):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+
+                       estack_push(stack, top, ax, bx);
+                       estack_ax_v = ((struct literal_numeric *) insn->data)->v;
+                       dbg_printk("load s64 %lld\n",
+                               (long long) estack_ax_v);
+                       next_pc += sizeof(struct load_op)
+                                       + sizeof(struct literal_numeric);
+                       PO;
+               }
+
+               OP(FILTER_OP_LOAD_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               /* cast */
+               OP(FILTER_OP_CAST_TO_S64):
+                       printk(KERN_WARNING "unsupported non-specialized bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               OP(FILTER_OP_CAST_DOUBLE_TO_S64):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+               OP(FILTER_OP_CAST_NOP):
+               {
+                       next_pc += sizeof(struct cast_op);
+                       PO;
+               }
+
+               /* get context ref */
+               OP(FILTER_OP_GET_CONTEXT_REF_STRING):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+                       struct field_ref *ref = (struct field_ref *) insn->data;
+                       struct lttng_ctx_field *ctx_field;
+                       union lttng_ctx_value v;
+
+                       dbg_printk("get context ref offset %u type string\n",
+                               ref->offset);
+                       ctx_field = &lttng_static_ctx->fields[ref->offset];
+                       ctx_field->get_value(ctx_field, &v);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax(stack, top)->u.s.str = v.str;
+                       if (unlikely(!estack_ax(stack, top)->u.s.str)) {
+                               dbg_printk("Filter warning: loading a NULL string.\n");
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       estack_ax(stack, top)->u.s.seq_len = UINT_MAX;
+                       estack_ax(stack, top)->u.s.literal = 0;
+                       dbg_printk("ref get context string %s\n", estack_ax(stack, top)->u.s.str);
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       PO;
+               }
+
+               OP(FILTER_OP_GET_CONTEXT_REF_S64):
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+                       struct field_ref *ref = (struct field_ref *) insn->data;
+                       struct lttng_ctx_field *ctx_field;
+                       union lttng_ctx_value v;
+
+                       dbg_printk("get context ref offset %u type s64\n",
+                               ref->offset);
+                       ctx_field = &lttng_static_ctx->fields[ref->offset];
+                       ctx_field->get_value(ctx_field, &v);
+                       estack_push(stack, top, ax, bx);
+                       estack_ax_v = v.s64;
+                       dbg_printk("ref get context s64 %lld\n",
+                               (long long) estack_ax_v);
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       PO;
+               }
+
+               OP(FILTER_OP_GET_CONTEXT_REF_DOUBLE):
+               {
+                       BUG_ON(1);
+                       PO;
+               }
+
+       END_OP
+end:
+       /* return 0 (discard) on error */
+       if (ret)
+               return 0;
+       return retval;
+}
+
+#undef START_OP
+#undef OP
+#undef PO
+#undef END_OP
diff --git a/lttng-filter-specialize.c b/lttng-filter-specialize.c
new file mode 100644 (file)
index 0000000..0c1d395
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * lttng-filter-specialize.c
+ *
+ * LTTng modules filter code specializer.
+ *
+ * Copyright (C) 2010-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lttng-filter.h"
+
+int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode)
+{
+       void *pc, *next_pc, *start_pc;
+       int ret = -EINVAL;
+       struct vstack _stack;
+       struct vstack *stack = &_stack;
+
+       vstack_init(stack);
+
+       start_pc = &bytecode->data[0];
+       for (pc = next_pc = start_pc; pc - start_pc < bytecode->len;
+                       pc = next_pc) {
+               switch (*(filter_opcode_t *) pc) {
+               case FILTER_OP_UNKNOWN:
+               default:
+                       printk(KERN_WARNING "unknown bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               case FILTER_OP_RETURN:
+                       ret = 0;
+                       goto end;
+
+               /* binary */
+               case FILTER_OP_MUL:
+               case FILTER_OP_DIV:
+               case FILTER_OP_MOD:
+               case FILTER_OP_PLUS:
+               case FILTER_OP_MINUS:
+               case FILTER_OP_RSHIFT:
+               case FILTER_OP_LSHIFT:
+               case FILTER_OP_BIN_AND:
+               case FILTER_OP_BIN_OR:
+               case FILTER_OP_BIN_XOR:
+                       printk(KERN_WARNING "unsupported bytecode op %u\n",
+                               (unsigned int) *(filter_opcode_t *) pc);
+                       ret = -EINVAL;
+                       goto end;
+
+               case FILTER_OP_EQ:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_EQ_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_EQ_S64;
+                               else
+                                       insn->op = FILTER_OP_EQ_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_EQ_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_EQ_DOUBLE;
+                               break;
+                       }
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               case FILTER_OP_NE:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_NE_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_NE_S64;
+                               else
+                                       insn->op = FILTER_OP_NE_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_NE_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_NE_DOUBLE;
+                               break;
+                       }
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               case FILTER_OP_GT:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_GT_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_GT_S64;
+                               else
+                                       insn->op = FILTER_OP_GT_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_GT_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_GT_DOUBLE;
+                               break;
+                       }
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               case FILTER_OP_LT:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_LT_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_LT_S64;
+                               else
+                                       insn->op = FILTER_OP_LT_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_LT_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_LT_DOUBLE;
+                               break;
+                       }
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               case FILTER_OP_GE:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_GE_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_GE_S64;
+                               else
+                                       insn->op = FILTER_OP_GE_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_GE_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_GE_DOUBLE;
+                               break;
+                       }
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+               case FILTER_OP_LE:
+               {
+                       struct binary_op *insn = (struct binary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               insn->op = FILTER_OP_LE_STRING;
+                               break;
+                       case REG_S64:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_LE_S64;
+                               else
+                                       insn->op = FILTER_OP_LE_DOUBLE_S64;
+                               break;
+                       case REG_DOUBLE:
+                               if (vstack_bx(stack)->type == REG_S64)
+                                       insn->op = FILTER_OP_LE_S64_DOUBLE;
+                               else
+                                       insn->op = FILTER_OP_LE_DOUBLE;
+                               break;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               case FILTER_OP_EQ_STRING:
+               case FILTER_OP_NE_STRING:
+               case FILTER_OP_GT_STRING:
+               case FILTER_OP_LT_STRING:
+               case FILTER_OP_GE_STRING:
+               case FILTER_OP_LE_STRING:
+               case FILTER_OP_EQ_S64:
+               case FILTER_OP_NE_S64:
+               case FILTER_OP_GT_S64:
+               case FILTER_OP_LT_S64:
+               case FILTER_OP_GE_S64:
+               case FILTER_OP_LE_S64:
+               case FILTER_OP_EQ_DOUBLE:
+               case FILTER_OP_NE_DOUBLE:
+               case FILTER_OP_GT_DOUBLE:
+               case FILTER_OP_LT_DOUBLE:
+               case FILTER_OP_GE_DOUBLE:
+               case FILTER_OP_LE_DOUBLE:
+               case FILTER_OP_EQ_DOUBLE_S64:
+               case FILTER_OP_NE_DOUBLE_S64:
+               case FILTER_OP_GT_DOUBLE_S64:
+               case FILTER_OP_LT_DOUBLE_S64:
+               case FILTER_OP_GE_DOUBLE_S64:
+               case FILTER_OP_LE_DOUBLE_S64:
+               case FILTER_OP_EQ_S64_DOUBLE:
+               case FILTER_OP_NE_S64_DOUBLE:
+               case FILTER_OP_GT_S64_DOUBLE:
+               case FILTER_OP_LT_S64_DOUBLE:
+               case FILTER_OP_GE_S64_DOUBLE:
+               case FILTER_OP_LE_S64_DOUBLE:
+               {
+                       /* Pop 2, push 1 */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct binary_op);
+                       break;
+               }
+
+               /* unary */
+               case FILTER_OP_UNARY_PLUS:
+               {
+                       struct unary_op *insn = (struct unary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               insn->op = FILTER_OP_UNARY_PLUS_S64;
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_UNARY_PLUS_DOUBLE;
+                               break;
+                       }
+                       /* Pop 1, push 1 */
+                       next_pc += sizeof(struct unary_op);
+                       break;
+               }
+
+               case FILTER_OP_UNARY_MINUS:
+               {
+                       struct unary_op *insn = (struct unary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               insn->op = FILTER_OP_UNARY_MINUS_S64;
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_UNARY_MINUS_DOUBLE;
+                               break;
+                       }
+                       /* Pop 1, push 1 */
+                       next_pc += sizeof(struct unary_op);
+                       break;
+               }
+
+               case FILTER_OP_UNARY_NOT:
+               {
+                       struct unary_op *insn = (struct unary_op *) pc;
+
+                       switch(vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_S64:
+                               insn->op = FILTER_OP_UNARY_NOT_S64;
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_UNARY_NOT_DOUBLE;
+                               break;
+                       }
+                       /* Pop 1, push 1 */
+                       next_pc += sizeof(struct unary_op);
+                       break;
+               }
+
+               case FILTER_OP_UNARY_PLUS_S64:
+               case FILTER_OP_UNARY_MINUS_S64:
+               case FILTER_OP_UNARY_NOT_S64:
+               case FILTER_OP_UNARY_PLUS_DOUBLE:
+               case FILTER_OP_UNARY_MINUS_DOUBLE:
+               case FILTER_OP_UNARY_NOT_DOUBLE:
+               {
+                       /* Pop 1, push 1 */
+                       next_pc += sizeof(struct unary_op);
+                       break;
+               }
+
+               /* logical */
+               case FILTER_OP_AND:
+               case FILTER_OP_OR:
+               {
+                       /* Continue to next instruction */
+                       /* Pop 1 when jump not taken */
+                       if (vstack_pop(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       next_pc += sizeof(struct logical_op);
+                       break;
+               }
+
+               /* load field ref */
+               case FILTER_OP_LOAD_FIELD_REF:
+               {
+                       printk(KERN_WARNING "Unknown field ref type\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               /* get context ref */
+               case FILTER_OP_GET_CONTEXT_REF:
+               {
+                       printk(KERN_WARNING "Unknown get context ref type\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               case FILTER_OP_LOAD_FIELD_REF_STRING:
+               case FILTER_OP_LOAD_FIELD_REF_SEQUENCE:
+               case FILTER_OP_GET_CONTEXT_REF_STRING:
+               {
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_STRING;
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       break;
+               }
+               case FILTER_OP_LOAD_FIELD_REF_S64:
+               case FILTER_OP_GET_CONTEXT_REF_S64:
+               {
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       break;
+               }
+               case FILTER_OP_LOAD_FIELD_REF_DOUBLE:
+               case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
+               {
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_DOUBLE;
+                       next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+                       break;
+               }
+
+               /* load from immediate operand */
+               case FILTER_OP_LOAD_STRING:
+               {
+                       struct load_op *insn = (struct load_op *) pc;
+
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_STRING;
+                       next_pc += sizeof(struct load_op) + strlen(insn->data) + 1;
+                       break;
+               }
+
+               case FILTER_OP_LOAD_S64:
+               {
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct load_op)
+                                       + sizeof(struct literal_numeric);
+                       break;
+               }
+
+               case FILTER_OP_LOAD_DOUBLE:
+               {
+                       if (vstack_push(stack)) {
+                               ret = -EINVAL;
+                               goto end;
+                       }
+                       vstack_ax(stack)->type = REG_DOUBLE;
+                       next_pc += sizeof(struct load_op)
+                                       + sizeof(struct literal_double);
+                       break;
+               }
+
+               /* cast */
+               case FILTER_OP_CAST_TO_S64:
+               {
+                       struct cast_op *insn = (struct cast_op *) pc;
+
+                       switch (vstack_ax(stack)->type) {
+                       default:
+                               printk(KERN_WARNING "unknown register type\n");
+                               ret = -EINVAL;
+                               goto end;
+
+                       case REG_STRING:
+                               printk(KERN_WARNING "Cast op can only be applied to numeric or floating point registers\n");
+                               ret = -EINVAL;
+                               goto end;
+                       case REG_S64:
+                               insn->op = FILTER_OP_CAST_NOP;
+                               break;
+                       case REG_DOUBLE:
+                               insn->op = FILTER_OP_CAST_DOUBLE_TO_S64;
+                               break;
+                       }
+                       /* Pop 1, push 1 */
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct cast_op);
+                       break;
+               }
+               case FILTER_OP_CAST_DOUBLE_TO_S64:
+               {
+                       /* Pop 1, push 1 */
+                       vstack_ax(stack)->type = REG_S64;
+                       next_pc += sizeof(struct cast_op);
+                       break;
+               }
+               case FILTER_OP_CAST_NOP:
+               {
+                       next_pc += sizeof(struct cast_op);
+                       break;
+               }
+
+               }
+       }
+end:
+       return ret;
+}
diff --git a/lttng-filter-validator.c b/lttng-filter-validator.c
new file mode 100644 (file)
index 0000000..71e9a74
--- /dev/null
@@ -0,0 +1,1058 @@
+/*
+ * lttng-filter-validator.c
+ *
+ * LTTng modules filter bytecode validator.
+ *
+ * Copyright (C) 2010-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+
+#include "lttng-filter.h"
+
+#define MERGE_POINT_TABLE_BITS         7
+#define MERGE_POINT_TABLE_SIZE         (1U << MERGE_POINT_TABLE_BITS)
+
+/* merge point table node */
+struct mp_node {
+       struct hlist_node node;
+
+       /* Context at merge point */
+       struct vstack stack;
+       unsigned long target_pc;
+};
+
+struct mp_table {
+       struct hlist_head mp_head[MERGE_POINT_TABLE_SIZE];
+};
+
+static
+int lttng_hash_match(struct mp_node *mp_node, unsigned long key_pc)
+{
+       if (mp_node->target_pc == key_pc)
+               return 1;
+       else
+               return 0;
+}
+
+static
+int merge_points_compare(const struct vstack *stacka,
+                       const struct vstack *stackb)
+{
+       int i, len;
+
+       if (stacka->top != stackb->top)
+               return 1;
+       len = stacka->top + 1;
+       WARN_ON_ONCE(len < 0);
+       for (i = 0; i < len; i++) {
+               if (stacka->e[i].type != stackb->e[i].type)
+                       return 1;
+       }
+       return 0;
+}
+
+static
+int merge_point_add_check(struct mp_table *mp_table, unsigned long target_pc,
+               const struct vstack *stack)
+{
+       struct mp_node *mp_node;
+       unsigned long hash = jhash_1word(target_pc, 0);
+       struct hlist_head *head;
+       struct mp_node *lookup_node;
+       int found = 0;
+
+       dbg_printk("Filter: adding merge point at offset %lu, hash %lu\n",
+                       target_pc, hash);
+       mp_node = kzalloc(sizeof(struct mp_node), GFP_KERNEL);
+       if (!mp_node)
+               return -ENOMEM;
+       mp_node->target_pc = target_pc;
+       memcpy(&mp_node->stack, stack, sizeof(mp_node->stack));
+
+       head = &mp_table->mp_head[hash & (MERGE_POINT_TABLE_SIZE - 1)];
+       hlist_for_each_entry(lookup_node, head, node) {
+               if (lttng_hash_match(lookup_node, target_pc)) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               /* Key already present */
+               dbg_printk("Filter: compare merge points for offset %lu, hash %lu\n",
+                               target_pc, hash);
+               kfree(mp_node);
+               if (merge_points_compare(stack, &lookup_node->stack)) {
+                       printk(KERN_WARNING "Merge points differ for offset %lu\n",
+                               target_pc);
+                       return -EINVAL;
+               }
+       }
+       hlist_add_head(&mp_node->node, head);
+       return 0;
+}
+
+/*
+ * Binary comparators use top of stack and top of stack -1.
+ */
+static
+int bin_op_compare_check(struct vstack *stack, const char *str)
+{
+       if (unlikely(!vstack_ax(stack) || !vstack_bx(stack)))
+               goto error_unknown;
+
+       switch (vstack_ax(stack)->type) {
+       default:
+       case REG_DOUBLE:
+               goto error_unknown;
+
+       case REG_STRING:
+               switch (vstack_bx(stack)->type) {
+               default:
+               case REG_DOUBLE:
+                       goto error_unknown;
+
+               case REG_STRING:
+                       break;
+               case REG_S64:
+                       goto error_mismatch;
+               }
+               break;
+       case REG_S64:
+               switch (vstack_bx(stack)->type) {
+               default:
+               case REG_DOUBLE:
+                       goto error_unknown;
+
+               case REG_STRING:
+                       goto error_mismatch;
+
+               case REG_S64:
+                       break;
+               }
+               break;
+       }
+       return 0;
+
+error_unknown:
+       return -EINVAL;
+
+error_mismatch:
+       printk(KERN_WARNING "type mismatch for '%s' binary operator\n", str);
+       return -EINVAL;
+}
+
+/*
+ * Validate bytecode range overflow within the validation pass.
+ * Called for each instruction encountered.
+ */
+static
+int bytecode_validate_overflow(struct bytecode_runtime *bytecode,
+               void *start_pc, void *pc)
+{
+       int ret = 0;
+
+       switch (*(filter_opcode_t *) pc) {
+       case FILTER_OP_UNKNOWN:
+       default:
+       {
+               printk(KERN_WARNING "unknown bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               break;
+       }
+
+       case FILTER_OP_RETURN:
+       {
+               if (unlikely(pc + sizeof(struct return_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       /* binary */
+       case FILTER_OP_MUL:
+       case FILTER_OP_DIV:
+       case FILTER_OP_MOD:
+       case FILTER_OP_PLUS:
+       case FILTER_OP_MINUS:
+       case FILTER_OP_RSHIFT:
+       case FILTER_OP_LSHIFT:
+       case FILTER_OP_BIN_AND:
+       case FILTER_OP_BIN_OR:
+       case FILTER_OP_BIN_XOR:
+       case FILTER_OP_EQ_DOUBLE:
+       case FILTER_OP_NE_DOUBLE:
+       case FILTER_OP_GT_DOUBLE:
+       case FILTER_OP_LT_DOUBLE:
+       case FILTER_OP_GE_DOUBLE:
+       case FILTER_OP_LE_DOUBLE:
+       /* Floating point */
+       case FILTER_OP_EQ_DOUBLE_S64:
+       case FILTER_OP_NE_DOUBLE_S64:
+       case FILTER_OP_GT_DOUBLE_S64:
+       case FILTER_OP_LT_DOUBLE_S64:
+       case FILTER_OP_GE_DOUBLE_S64:
+       case FILTER_OP_LE_DOUBLE_S64:
+       case FILTER_OP_EQ_S64_DOUBLE:
+       case FILTER_OP_NE_S64_DOUBLE:
+       case FILTER_OP_GT_S64_DOUBLE:
+       case FILTER_OP_LT_S64_DOUBLE:
+       case FILTER_OP_GE_S64_DOUBLE:
+       case FILTER_OP_LE_S64_DOUBLE:
+       case FILTER_OP_LOAD_FIELD_REF_DOUBLE:
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
+       case FILTER_OP_LOAD_DOUBLE:
+       case FILTER_OP_CAST_DOUBLE_TO_S64:
+       case FILTER_OP_UNARY_PLUS_DOUBLE:
+       case FILTER_OP_UNARY_MINUS_DOUBLE:
+       case FILTER_OP_UNARY_NOT_DOUBLE:
+       {
+               printk(KERN_WARNING "unsupported bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               break;
+       }
+
+       case FILTER_OP_EQ:
+       case FILTER_OP_NE:
+       case FILTER_OP_GT:
+       case FILTER_OP_LT:
+       case FILTER_OP_GE:
+       case FILTER_OP_LE:
+       case FILTER_OP_EQ_STRING:
+       case FILTER_OP_NE_STRING:
+       case FILTER_OP_GT_STRING:
+       case FILTER_OP_LT_STRING:
+       case FILTER_OP_GE_STRING:
+       case FILTER_OP_LE_STRING:
+       case FILTER_OP_EQ_S64:
+       case FILTER_OP_NE_S64:
+       case FILTER_OP_GT_S64:
+       case FILTER_OP_LT_S64:
+       case FILTER_OP_GE_S64:
+       case FILTER_OP_LE_S64:
+       {
+               if (unlikely(pc + sizeof(struct binary_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       /* unary */
+       case FILTER_OP_UNARY_PLUS:
+       case FILTER_OP_UNARY_MINUS:
+       case FILTER_OP_UNARY_NOT:
+       case FILTER_OP_UNARY_PLUS_S64:
+       case FILTER_OP_UNARY_MINUS_S64:
+       case FILTER_OP_UNARY_NOT_S64:
+       {
+               if (unlikely(pc + sizeof(struct unary_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       /* logical */
+       case FILTER_OP_AND:
+       case FILTER_OP_OR:
+       {
+               if (unlikely(pc + sizeof(struct logical_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       /* load field ref */
+       case FILTER_OP_LOAD_FIELD_REF:
+       {
+               printk(KERN_WARNING "Unknown field ref type\n");
+               ret = -EINVAL;
+               break;
+       }
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_REF:
+       {
+               printk(KERN_WARNING "Unknown field ref type\n");
+               ret = -EINVAL;
+               break;
+       }
+       case FILTER_OP_LOAD_FIELD_REF_STRING:
+       case FILTER_OP_LOAD_FIELD_REF_SEQUENCE:
+       case FILTER_OP_LOAD_FIELD_REF_S64:
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
+       case FILTER_OP_GET_CONTEXT_REF_S64:
+       {
+               if (unlikely(pc + sizeof(struct load_op) + sizeof(struct field_ref)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       /* load from immediate operand */
+       case FILTER_OP_LOAD_STRING:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               uint32_t str_len, maxlen;
+
+               if (unlikely(pc + sizeof(struct load_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+                       break;
+               }
+
+               maxlen = start_pc + bytecode->len - pc - sizeof(struct load_op);
+               str_len = strnlen(insn->data, maxlen);
+               if (unlikely(str_len >= maxlen)) {
+                       /* Final '\0' not found within range */
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       case FILTER_OP_LOAD_S64:
+       {
+               if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_numeric)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       case FILTER_OP_CAST_TO_S64:
+       case FILTER_OP_CAST_NOP:
+       {
+               if (unlikely(pc + sizeof(struct cast_op)
+                               > start_pc + bytecode->len)) {
+                       ret = -ERANGE;
+               }
+               break;
+       }
+
+       }
+
+       return ret;
+}
+
+static
+unsigned long delete_all_nodes(struct mp_table *mp_table)
+{
+       struct mp_node *mp_node;
+       struct hlist_node *tmp;
+       unsigned long nr_nodes = 0;
+       int i;
+
+       for (i = 0; i < MERGE_POINT_TABLE_SIZE; i++) {
+               struct hlist_head *head;
+
+               head = &mp_table->mp_head[i];
+               hlist_for_each_entry_safe(mp_node, tmp, head, node) {
+                       kfree(mp_node);
+                       nr_nodes++;
+               }
+       }
+       return nr_nodes;
+}
+
+/*
+ * Return value:
+ * 0: success
+ * <0: error
+ */
+static
+int validate_instruction_context(struct bytecode_runtime *bytecode,
+               struct vstack *stack,
+               void *start_pc,
+               void *pc)
+{
+       int ret = 0;
+
+       switch (*(filter_opcode_t *) pc) {
+       case FILTER_OP_UNKNOWN:
+       default:
+       {
+               printk(KERN_WARNING "unknown bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       case FILTER_OP_RETURN:
+       {
+               goto end;
+       }
+
+       /* binary */
+       case FILTER_OP_MUL:
+       case FILTER_OP_DIV:
+       case FILTER_OP_MOD:
+       case FILTER_OP_PLUS:
+       case FILTER_OP_MINUS:
+       case FILTER_OP_RSHIFT:
+       case FILTER_OP_LSHIFT:
+       case FILTER_OP_BIN_AND:
+       case FILTER_OP_BIN_OR:
+       case FILTER_OP_BIN_XOR:
+       /* Floating point */
+       case FILTER_OP_EQ_DOUBLE:
+       case FILTER_OP_NE_DOUBLE:
+       case FILTER_OP_GT_DOUBLE:
+       case FILTER_OP_LT_DOUBLE:
+       case FILTER_OP_GE_DOUBLE:
+       case FILTER_OP_LE_DOUBLE:
+       case FILTER_OP_EQ_DOUBLE_S64:
+       case FILTER_OP_NE_DOUBLE_S64:
+       case FILTER_OP_GT_DOUBLE_S64:
+       case FILTER_OP_LT_DOUBLE_S64:
+       case FILTER_OP_GE_DOUBLE_S64:
+       case FILTER_OP_LE_DOUBLE_S64:
+       case FILTER_OP_EQ_S64_DOUBLE:
+       case FILTER_OP_NE_S64_DOUBLE:
+       case FILTER_OP_GT_S64_DOUBLE:
+       case FILTER_OP_LT_S64_DOUBLE:
+       case FILTER_OP_GE_S64_DOUBLE:
+       case FILTER_OP_LE_S64_DOUBLE:
+       case FILTER_OP_UNARY_PLUS_DOUBLE:
+       case FILTER_OP_UNARY_MINUS_DOUBLE:
+       case FILTER_OP_UNARY_NOT_DOUBLE:
+       case FILTER_OP_LOAD_FIELD_REF_DOUBLE:
+       case FILTER_OP_LOAD_DOUBLE:
+       case FILTER_OP_CAST_DOUBLE_TO_S64:
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
+       {
+               printk(KERN_WARNING "unsupported bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       case FILTER_OP_EQ:
+       {
+               ret = bin_op_compare_check(stack, "==");
+               if (ret)
+                       goto end;
+               break;
+       }
+       case FILTER_OP_NE:
+       {
+               ret = bin_op_compare_check(stack, "!=");
+               if (ret)
+                       goto end;
+               break;
+       }
+       case FILTER_OP_GT:
+       {
+               ret = bin_op_compare_check(stack, ">");
+               if (ret)
+                       goto end;
+               break;
+       }
+       case FILTER_OP_LT:
+       {
+               ret = bin_op_compare_check(stack, "<");
+               if (ret)
+                       goto end;
+               break;
+       }
+       case FILTER_OP_GE:
+       {
+               ret = bin_op_compare_check(stack, ">=");
+               if (ret)
+                       goto end;
+               break;
+       }
+       case FILTER_OP_LE:
+       {
+               ret = bin_op_compare_check(stack, "<=");
+               if (ret)
+                       goto end;
+               break;
+       }
+
+       case FILTER_OP_EQ_STRING:
+       case FILTER_OP_NE_STRING:
+       case FILTER_OP_GT_STRING:
+       case FILTER_OP_LT_STRING:
+       case FILTER_OP_GE_STRING:
+       case FILTER_OP_LE_STRING:
+       {
+               if (!vstack_ax(stack) || !vstack_bx(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               if (vstack_ax(stack)->type != REG_STRING
+                               || vstack_bx(stack)->type != REG_STRING) {
+                       printk(KERN_WARNING "Unexpected register type for string comparator\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+
+       case FILTER_OP_EQ_S64:
+       case FILTER_OP_NE_S64:
+       case FILTER_OP_GT_S64:
+       case FILTER_OP_LT_S64:
+       case FILTER_OP_GE_S64:
+       case FILTER_OP_LE_S64:
+       {
+               if (!vstack_ax(stack) || !vstack_bx(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               if (vstack_ax(stack)->type != REG_S64
+                               || vstack_bx(stack)->type != REG_S64) {
+                       printk(KERN_WARNING "Unexpected register type for s64 comparator\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+
+       /* unary */
+       case FILTER_OP_UNARY_PLUS:
+       case FILTER_OP_UNARY_MINUS:
+       case FILTER_OP_UNARY_NOT:
+       {
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               switch (vstack_ax(stack)->type) {
+               default:
+               case REG_DOUBLE:
+                       printk(KERN_WARNING "unknown register type\n");
+                       ret = -EINVAL;
+                       goto end;
+
+               case REG_STRING:
+                       printk(KERN_WARNING "Unary op can only be applied to numeric or floating point registers\n");
+                       ret = -EINVAL;
+                       goto end;
+               case REG_S64:
+                       break;
+               }
+               break;
+       }
+
+       case FILTER_OP_UNARY_PLUS_S64:
+       case FILTER_OP_UNARY_MINUS_S64:
+       case FILTER_OP_UNARY_NOT_S64:
+       {
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               if (vstack_ax(stack)->type != REG_S64) {
+                       printk(KERN_WARNING "Invalid register type\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+
+       /* logical */
+       case FILTER_OP_AND:
+       case FILTER_OP_OR:
+       {
+               struct logical_op *insn = (struct logical_op *) pc;
+
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               if (vstack_ax(stack)->type != REG_S64) {
+                       printk(KERN_WARNING "Logical comparator expects S64 register\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               dbg_printk("Validate jumping to bytecode offset %u\n",
+                       (unsigned int) insn->skip_offset);
+               if (unlikely(start_pc + insn->skip_offset <= pc)) {
+                       printk(KERN_WARNING "Loops are not allowed in bytecode\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+
+       /* load field ref */
+       case FILTER_OP_LOAD_FIELD_REF:
+       {
+               printk(KERN_WARNING "Unknown field ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
+       case FILTER_OP_LOAD_FIELD_REF_STRING:
+       case FILTER_OP_LOAD_FIELD_REF_SEQUENCE:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printk("Validate load field ref offset %u type string\n",
+                       ref->offset);
+               break;
+       }
+       case FILTER_OP_LOAD_FIELD_REF_S64:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printk("Validate load field ref offset %u type s64\n",
+                       ref->offset);
+               break;
+       }
+
+       /* load from immediate operand */
+       case FILTER_OP_LOAD_STRING:
+       {
+               break;
+       }
+
+       case FILTER_OP_LOAD_S64:
+       {
+               break;
+       }
+
+       case FILTER_OP_CAST_TO_S64:
+       {
+               struct cast_op *insn = (struct cast_op *) pc;
+
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               switch (vstack_ax(stack)->type) {
+               default:
+               case REG_DOUBLE:
+                       printk(KERN_WARNING "unknown register type\n");
+                       ret = -EINVAL;
+                       goto end;
+
+               case REG_STRING:
+                       printk(KERN_WARNING "Cast op can only be applied to numeric or floating point registers\n");
+                       ret = -EINVAL;
+                       goto end;
+               case REG_S64:
+                       break;
+               }
+               if (insn->op == FILTER_OP_CAST_DOUBLE_TO_S64) {
+                       if (vstack_ax(stack)->type != REG_DOUBLE) {
+                               printk(KERN_WARNING "Cast expects double\n");
+                               ret = -EINVAL;
+                               goto end;
+                       }
+               }
+               break;
+       }
+       case FILTER_OP_CAST_NOP:
+       {
+               break;
+       }
+
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_REF:
+       {
+               printk(KERN_WARNING "Unknown get context ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printk("Validate get context ref offset %u type string\n",
+                       ref->offset);
+               break;
+       }
+       case FILTER_OP_GET_CONTEXT_REF_S64:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printk("Validate get context ref offset %u type s64\n",
+                       ref->offset);
+               break;
+       }
+
+       }
+end:
+       return ret;
+}
+
+/*
+ * Return value:
+ * 0: success
+ * <0: error
+ */
+static
+int validate_instruction_all_contexts(struct bytecode_runtime *bytecode,
+               struct mp_table *mp_table,
+               struct vstack *stack,
+               void *start_pc,
+               void *pc)
+{
+       int ret, found = 0;
+       unsigned long target_pc = pc - start_pc;
+       unsigned long hash;
+       struct hlist_head *head;
+       struct mp_node *mp_node;
+
+       /* Validate the context resulting from the previous instruction */
+       ret = validate_instruction_context(bytecode, stack, start_pc, pc);
+       if (ret)
+               return ret;
+
+       /* Validate merge points */
+       hash = jhash_1word(target_pc, 0);
+       head = &mp_table->mp_head[hash & (MERGE_POINT_TABLE_SIZE - 1)];
+       hlist_for_each_entry(mp_node, head, node) {
+               if (lttng_hash_match(mp_node, target_pc)) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               dbg_printk("Filter: validate merge point at offset %lu\n",
+                               target_pc);
+               if (merge_points_compare(stack, &mp_node->stack)) {
+                       printk(KERN_WARNING "Merge points differ for offset %lu\n",
+                               target_pc);
+                       return -EINVAL;
+               }
+               /* Once validated, we can remove the merge point */
+               dbg_printk("Filter: remove merge point at offset %lu\n",
+                               target_pc);
+               hlist_del(&mp_node->node);
+       }
+       return 0;
+}
+
+/*
+ * Return value:
+ * >0: going to next insn.
+ * 0: success, stop iteration.
+ * <0: error
+ */
+static
+int exec_insn(struct bytecode_runtime *bytecode,
+               struct mp_table *mp_table,
+               struct vstack *stack,
+               void **_next_pc,
+               void *pc)
+{
+       int ret = 1;
+       void *next_pc = *_next_pc;
+
+       switch (*(filter_opcode_t *) pc) {
+       case FILTER_OP_UNKNOWN:
+       default:
+       {
+               printk(KERN_WARNING "unknown bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       case FILTER_OP_RETURN:
+       {
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               ret = 0;
+               goto end;
+       }
+
+       /* binary */
+       case FILTER_OP_MUL:
+       case FILTER_OP_DIV:
+       case FILTER_OP_MOD:
+       case FILTER_OP_PLUS:
+       case FILTER_OP_MINUS:
+       case FILTER_OP_RSHIFT:
+       case FILTER_OP_LSHIFT:
+       case FILTER_OP_BIN_AND:
+       case FILTER_OP_BIN_OR:
+       case FILTER_OP_BIN_XOR:
+       /* Floating point */
+       case FILTER_OP_EQ_DOUBLE:
+       case FILTER_OP_NE_DOUBLE:
+       case FILTER_OP_GT_DOUBLE:
+       case FILTER_OP_LT_DOUBLE:
+       case FILTER_OP_GE_DOUBLE:
+       case FILTER_OP_LE_DOUBLE:
+       case FILTER_OP_EQ_DOUBLE_S64:
+       case FILTER_OP_NE_DOUBLE_S64:
+       case FILTER_OP_GT_DOUBLE_S64:
+       case FILTER_OP_LT_DOUBLE_S64:
+       case FILTER_OP_GE_DOUBLE_S64:
+       case FILTER_OP_LE_DOUBLE_S64:
+       case FILTER_OP_EQ_S64_DOUBLE:
+       case FILTER_OP_NE_S64_DOUBLE:
+       case FILTER_OP_GT_S64_DOUBLE:
+       case FILTER_OP_LT_S64_DOUBLE:
+       case FILTER_OP_GE_S64_DOUBLE:
+       case FILTER_OP_LE_S64_DOUBLE:
+       case FILTER_OP_UNARY_PLUS_DOUBLE:
+       case FILTER_OP_UNARY_MINUS_DOUBLE:
+       case FILTER_OP_UNARY_NOT_DOUBLE:
+       case FILTER_OP_LOAD_FIELD_REF_DOUBLE:
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
+       case FILTER_OP_LOAD_DOUBLE:
+       case FILTER_OP_CAST_DOUBLE_TO_S64:
+       {
+               printk(KERN_WARNING "unsupported bytecode op %u\n",
+                       (unsigned int) *(filter_opcode_t *) pc);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       case FILTER_OP_EQ:
+       case FILTER_OP_NE:
+       case FILTER_OP_GT:
+       case FILTER_OP_LT:
+       case FILTER_OP_GE:
+       case FILTER_OP_LE:
+       case FILTER_OP_EQ_STRING:
+       case FILTER_OP_NE_STRING:
+       case FILTER_OP_GT_STRING:
+       case FILTER_OP_LT_STRING:
+       case FILTER_OP_GE_STRING:
+       case FILTER_OP_LE_STRING:
+       case FILTER_OP_EQ_S64:
+       case FILTER_OP_NE_S64:
+       case FILTER_OP_GT_S64:
+       case FILTER_OP_LT_S64:
+       case FILTER_OP_GE_S64:
+       case FILTER_OP_LE_S64:
+       {
+               /* Pop 2, push 1 */
+               if (vstack_pop(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_S64;
+               next_pc += sizeof(struct binary_op);
+               break;
+       }
+
+       /* unary */
+       case FILTER_OP_UNARY_PLUS:
+       case FILTER_OP_UNARY_MINUS:
+       case FILTER_OP_UNARY_NOT:
+       case FILTER_OP_UNARY_PLUS_S64:
+       case FILTER_OP_UNARY_MINUS_S64:
+       case FILTER_OP_UNARY_NOT_S64:
+       {
+               /* Pop 1, push 1 */
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_S64;
+               next_pc += sizeof(struct unary_op);
+               break;
+       }
+
+       /* logical */
+       case FILTER_OP_AND:
+       case FILTER_OP_OR:
+       {
+               struct logical_op *insn = (struct logical_op *) pc;
+               int merge_ret;
+
+               /* Add merge point to table */
+               merge_ret = merge_point_add_check(mp_table,
+                                       insn->skip_offset, stack);
+               if (merge_ret) {
+                       ret = merge_ret;
+                       goto end;
+               }
+               /* Continue to next instruction */
+               /* Pop 1 when jump not taken */
+               if (vstack_pop(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               next_pc += sizeof(struct logical_op);
+               break;
+       }
+
+       /* load field ref */
+       case FILTER_OP_LOAD_FIELD_REF:
+       {
+               printk(KERN_WARNING "Unknown field ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_REF:
+       {
+               printk(KERN_WARNING "Unknown get context ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
+       case FILTER_OP_LOAD_FIELD_REF_STRING:
+       case FILTER_OP_LOAD_FIELD_REF_SEQUENCE:
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
+       {
+               if (vstack_push(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_STRING;
+               next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+               break;
+       }
+       case FILTER_OP_LOAD_FIELD_REF_S64:
+       case FILTER_OP_GET_CONTEXT_REF_S64:
+       {
+               if (vstack_push(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_S64;
+               next_pc += sizeof(struct load_op) + sizeof(struct field_ref);
+               break;
+       }
+
+       /* load from immediate operand */
+       case FILTER_OP_LOAD_STRING:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+
+               if (vstack_push(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_STRING;
+               next_pc += sizeof(struct load_op) + strlen(insn->data) + 1;
+               break;
+       }
+
+       case FILTER_OP_LOAD_S64:
+       {
+               if (vstack_push(stack)) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_S64;
+               next_pc += sizeof(struct load_op)
+                               + sizeof(struct literal_numeric);
+               break;
+       }
+
+       case FILTER_OP_CAST_TO_S64:
+       {
+               /* Pop 1, push 1 */
+               if (!vstack_ax(stack)) {
+                       printk(KERN_WARNING "Empty stack\n");
+                       ret = -EINVAL;
+                       goto end;
+               }
+               vstack_ax(stack)->type = REG_S64;
+               next_pc += sizeof(struct cast_op);
+               break;
+       }
+       case FILTER_OP_CAST_NOP:
+       {
+               next_pc += sizeof(struct cast_op);
+               break;
+       }
+
+       }
+end:
+       *_next_pc = next_pc;
+       return ret;
+}
+
+/*
+ * Never called concurrently (hash seed is shared).
+ */
+int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode)
+{
+       struct mp_table *mp_table;
+       void *pc, *next_pc, *start_pc;
+       int ret = -EINVAL;
+       struct vstack stack;
+
+       vstack_init(&stack);
+
+       mp_table = kzalloc(sizeof(*mp_table), GFP_KERNEL);
+       if (!mp_table) {
+               printk(KERN_WARNING "Error allocating hash table for bytecode validation\n");
+               return -ENOMEM;
+       }
+       start_pc = &bytecode->data[0];
+       for (pc = next_pc = start_pc; pc - start_pc < bytecode->len;
+                       pc = next_pc) {
+               ret = bytecode_validate_overflow(bytecode, start_pc, pc);
+               if (ret != 0) {
+                       if (ret == -ERANGE)
+                               printk(KERN_WARNING "filter bytecode overflow\n");
+                       goto end;
+               }
+               dbg_printk("Validating op %s (%u)\n",
+                       lttng_filter_print_op((unsigned int) *(filter_opcode_t *) pc),
+                       (unsigned int) *(filter_opcode_t *) pc);
+
+               /*
+                * For each instruction, validate the current context
+                * (traversal of entire execution flow), and validate
+                * all  merge points targeting this instruction.
+                */
+               ret = validate_instruction_all_contexts(bytecode, mp_table,
+                                       &stack, start_pc, pc);
+               if (ret)
+                       goto end;
+               ret = exec_insn(bytecode, mp_table, &stack, &next_pc, pc);
+               if (ret <= 0)
+                       goto end;
+       }
+end:
+       if (delete_all_nodes(mp_table)) {
+               if (!ret) {
+                       printk(KERN_WARNING "Unexpected merge points\n");
+                       ret = -EINVAL;
+               }
+       }
+       kfree(mp_table);
+       return ret;
+}
diff --git a/lttng-filter.c b/lttng-filter.c
new file mode 100644 (file)
index 0000000..6cd750c
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * lttng-filter.c
+ *
+ * LTTng modules filter code.
+ *
+ * Copyright (C) 2010-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "lttng-filter.h"
+
+static const char *opnames[] = {
+       [ FILTER_OP_UNKNOWN ] = "UNKNOWN",
+
+       [ FILTER_OP_RETURN ] = "RETURN",
+
+       /* binary */
+       [ FILTER_OP_MUL ] = "MUL",
+       [ FILTER_OP_DIV ] = "DIV",
+       [ FILTER_OP_MOD ] = "MOD",
+       [ FILTER_OP_PLUS ] = "PLUS",
+       [ FILTER_OP_MINUS ] = "MINUS",
+       [ FILTER_OP_RSHIFT ] = "RSHIFT",
+       [ FILTER_OP_LSHIFT ] = "LSHIFT",
+       [ FILTER_OP_BIN_AND ] = "BIN_AND",
+       [ FILTER_OP_BIN_OR ] = "BIN_OR",
+       [ FILTER_OP_BIN_XOR ] = "BIN_XOR",
+
+       /* binary comparators */
+       [ FILTER_OP_EQ ] = "EQ",
+       [ FILTER_OP_NE ] = "NE",
+       [ FILTER_OP_GT ] = "GT",
+       [ FILTER_OP_LT ] = "LT",
+       [ FILTER_OP_GE ] = "GE",
+       [ FILTER_OP_LE ] = "LE",
+
+       /* string binary comparators */
+       [ FILTER_OP_EQ_STRING ] = "EQ_STRING",
+       [ FILTER_OP_NE_STRING ] = "NE_STRING",
+       [ FILTER_OP_GT_STRING ] = "GT_STRING",
+       [ FILTER_OP_LT_STRING ] = "LT_STRING",
+       [ FILTER_OP_GE_STRING ] = "GE_STRING",
+       [ FILTER_OP_LE_STRING ] = "LE_STRING",
+
+       /* s64 binary comparators */
+       [ FILTER_OP_EQ_S64 ] = "EQ_S64",
+       [ FILTER_OP_NE_S64 ] = "NE_S64",
+       [ FILTER_OP_GT_S64 ] = "GT_S64",
+       [ FILTER_OP_LT_S64 ] = "LT_S64",
+       [ FILTER_OP_GE_S64 ] = "GE_S64",
+       [ FILTER_OP_LE_S64 ] = "LE_S64",
+
+       /* double binary comparators */
+       [ FILTER_OP_EQ_DOUBLE ] = "EQ_DOUBLE",
+       [ FILTER_OP_NE_DOUBLE ] = "NE_DOUBLE",
+       [ FILTER_OP_GT_DOUBLE ] = "GT_DOUBLE",
+       [ FILTER_OP_LT_DOUBLE ] = "LT_DOUBLE",
+       [ FILTER_OP_GE_DOUBLE ] = "GE_DOUBLE",
+       [ FILTER_OP_LE_DOUBLE ] = "LE_DOUBLE",
+
+       /* Mixed S64-double binary comparators */
+       [ FILTER_OP_EQ_DOUBLE_S64 ] = "EQ_DOUBLE_S64",
+       [ FILTER_OP_NE_DOUBLE_S64 ] = "NE_DOUBLE_S64",
+       [ FILTER_OP_GT_DOUBLE_S64 ] = "GT_DOUBLE_S64",
+       [ FILTER_OP_LT_DOUBLE_S64 ] = "LT_DOUBLE_S64",
+       [ FILTER_OP_GE_DOUBLE_S64 ] = "GE_DOUBLE_S64",
+       [ FILTER_OP_LE_DOUBLE_S64 ] = "LE_DOUBLE_S64",
+
+       [ FILTER_OP_EQ_S64_DOUBLE ] = "EQ_S64_DOUBLE",
+       [ FILTER_OP_NE_S64_DOUBLE ] = "NE_S64_DOUBLE",
+       [ FILTER_OP_GT_S64_DOUBLE ] = "GT_S64_DOUBLE",
+       [ FILTER_OP_LT_S64_DOUBLE ] = "LT_S64_DOUBLE",
+       [ FILTER_OP_GE_S64_DOUBLE ] = "GE_S64_DOUBLE",
+       [ FILTER_OP_LE_S64_DOUBLE ] = "LE_S64_DOUBLE",
+
+       /* unary */
+       [ FILTER_OP_UNARY_PLUS ] = "UNARY_PLUS",
+       [ FILTER_OP_UNARY_MINUS ] = "UNARY_MINUS",
+       [ FILTER_OP_UNARY_NOT ] = "UNARY_NOT",
+       [ FILTER_OP_UNARY_PLUS_S64 ] = "UNARY_PLUS_S64",
+       [ FILTER_OP_UNARY_MINUS_S64 ] = "UNARY_MINUS_S64",
+       [ FILTER_OP_UNARY_NOT_S64 ] = "UNARY_NOT_S64",
+       [ FILTER_OP_UNARY_PLUS_DOUBLE ] = "UNARY_PLUS_DOUBLE",
+       [ FILTER_OP_UNARY_MINUS_DOUBLE ] = "UNARY_MINUS_DOUBLE",
+       [ FILTER_OP_UNARY_NOT_DOUBLE ] = "UNARY_NOT_DOUBLE",
+
+       /* logical */
+       [ FILTER_OP_AND ] = "AND",
+       [ FILTER_OP_OR ] = "OR",
+
+       /* load field ref */
+       [ FILTER_OP_LOAD_FIELD_REF ] = "LOAD_FIELD_REF",
+       [ FILTER_OP_LOAD_FIELD_REF_STRING ] = "LOAD_FIELD_REF_STRING",
+       [ FILTER_OP_LOAD_FIELD_REF_SEQUENCE ] = "LOAD_FIELD_REF_SEQUENCE",
+       [ FILTER_OP_LOAD_FIELD_REF_S64 ] = "LOAD_FIELD_REF_S64",
+       [ FILTER_OP_LOAD_FIELD_REF_DOUBLE ] = "LOAD_FIELD_REF_DOUBLE",
+
+       /* load from immediate operand */
+       [ FILTER_OP_LOAD_STRING ] = "LOAD_STRING",
+       [ FILTER_OP_LOAD_S64 ] = "LOAD_S64",
+       [ FILTER_OP_LOAD_DOUBLE ] = "LOAD_DOUBLE",
+
+       /* cast */
+       [ FILTER_OP_CAST_TO_S64 ] = "CAST_TO_S64",
+       [ FILTER_OP_CAST_DOUBLE_TO_S64 ] = "CAST_DOUBLE_TO_S64",
+       [ FILTER_OP_CAST_NOP ] = "CAST_NOP",
+
+       /* get context ref */
+       [ FILTER_OP_GET_CONTEXT_REF ] = "GET_CONTEXT_REF",
+       [ FILTER_OP_GET_CONTEXT_REF_STRING ] = "GET_CONTEXT_REF_STRING",
+       [ FILTER_OP_GET_CONTEXT_REF_S64 ] = "GET_CONTEXT_REF_S64",
+       [ FILTER_OP_GET_CONTEXT_REF_DOUBLE ] = "GET_CONTEXT_REF_DOUBLE",
+};
+
+const char *lttng_filter_print_op(enum filter_op op)
+{
+       if (op >= NR_FILTER_OPS)
+               return "UNKNOWN";
+       else
+               return opnames[op];
+}
+
+static
+int apply_field_reloc(struct lttng_event *event,
+               struct bytecode_runtime *runtime,
+               uint32_t runtime_len,
+               uint32_t reloc_offset,
+               const char *field_name)
+{
+       const struct lttng_event_desc *desc;
+       const struct lttng_event_field *fields, *field = NULL;
+       unsigned int nr_fields, i;
+       struct field_ref *field_ref;
+       struct load_op *op;
+       uint32_t field_offset = 0;
+
+       dbg_printk("Apply field reloc: %u %s\n", reloc_offset, field_name);
+
+       /* Lookup event by name */
+       desc = event->desc;
+       if (!desc)
+               return -EINVAL;
+       fields = desc->fields;
+       if (!fields)
+               return -EINVAL;
+       nr_fields = desc->nr_fields;
+       for (i = 0; i < nr_fields; i++) {
+               if (!strcmp(fields[i].name, field_name)) {
+                       field = &fields[i];
+                       break;
+               }
+               /* compute field offset */
+               switch (fields[i].type.atype) {
+               case atype_integer:
+               case atype_enum:
+                       field_offset += sizeof(int64_t);
+                       break;
+               case atype_array:
+               case atype_sequence:
+                       field_offset += sizeof(unsigned long);
+                       field_offset += sizeof(void *);
+                       break;
+               case atype_string:
+                       field_offset += sizeof(void *);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+       if (!field)
+               return -EINVAL;
+
+       /* Check if field offset is too large for 16-bit offset */
+       if (field_offset > FILTER_BYTECODE_MAX_LEN - 1)
+               return -EINVAL;
+
+       /* set type */
+       op = (struct load_op *) &runtime->data[reloc_offset];
+       field_ref = (struct field_ref *) op->data;
+       switch (field->type.atype) {
+       case atype_integer:
+       case atype_enum:
+               op->op = FILTER_OP_LOAD_FIELD_REF_S64;
+               break;
+       case atype_array:
+       case atype_sequence:
+               op->op = FILTER_OP_LOAD_FIELD_REF_SEQUENCE;
+               break;
+       case atype_string:
+               op->op = FILTER_OP_LOAD_FIELD_REF_STRING;
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* set offset */
+       field_ref->offset = (uint16_t) field_offset;
+       return 0;
+}
+
+static
+int apply_context_reloc(struct lttng_event *event,
+               struct bytecode_runtime *runtime,
+               uint32_t runtime_len,
+               uint32_t reloc_offset,
+               const char *context_name)
+{
+       struct field_ref *field_ref;
+       struct load_op *op;
+       struct lttng_ctx_field *ctx_field;
+       int idx;
+
+       dbg_printk("Apply context reloc: %u %s\n", reloc_offset, context_name);
+
+       /* Get context index */
+       idx = lttng_get_context_index(lttng_static_ctx, context_name);
+       if (idx < 0)
+               return -ENOENT;
+
+       /* Check if idx is too large for 16-bit offset */
+       if (idx > FILTER_BYTECODE_MAX_LEN - 1)
+               return -EINVAL;
+
+       /* Get context return type */
+       ctx_field = &lttng_static_ctx->fields[idx];
+       op = (struct load_op *) &runtime->data[reloc_offset];
+       field_ref = (struct field_ref *) op->data;
+       switch (ctx_field->event_field.type.atype) {
+       case atype_integer:
+       case atype_enum:
+               op->op = FILTER_OP_GET_CONTEXT_REF_S64;
+               break;
+               /* Sequence and array supported as string */
+       case atype_string:
+       case atype_array:
+       case atype_sequence:
+               op->op = FILTER_OP_GET_CONTEXT_REF_STRING;
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* set offset to context index within channel contexts */
+       field_ref->offset = (uint16_t) idx;
+       return 0;
+}
+
+static
+int apply_reloc(struct lttng_event *event,
+               struct bytecode_runtime *runtime,
+               uint32_t runtime_len,
+               uint32_t reloc_offset,
+               const char *name)
+{
+       struct load_op *op;
+
+       dbg_printk("Apply reloc: %u %s\n", reloc_offset, name);
+
+       /* Ensure that the reloc is within the code */
+       if (runtime_len - reloc_offset < sizeof(uint16_t))
+               return -EINVAL;
+
+       op = (struct load_op *) &runtime->data[reloc_offset];
+       switch (op->op) {
+       case FILTER_OP_LOAD_FIELD_REF:
+               return apply_field_reloc(event, runtime, runtime_len,
+                       reloc_offset, name);
+       case FILTER_OP_GET_CONTEXT_REF:
+               return apply_context_reloc(event, runtime, runtime_len,
+                       reloc_offset, name);
+       default:
+               printk(KERN_WARNING "Unknown reloc op type %u\n", op->op);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+int bytecode_is_linked(struct lttng_filter_bytecode_node *filter_bytecode,
+               struct lttng_event *event)
+{
+       struct lttng_bytecode_runtime *bc_runtime;
+
+       list_for_each_entry(bc_runtime,
+                       &event->bytecode_runtime_head, node) {
+               if (bc_runtime->bc == filter_bytecode)
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Take a bytecode with reloc table and link it to an event to create a
+ * bytecode runtime.
+ */
+static
+int _lttng_filter_event_link_bytecode(struct lttng_event *event,
+               struct lttng_filter_bytecode_node *filter_bytecode,
+               struct list_head *insert_loc)
+{
+       int ret, offset, next_offset;
+       struct bytecode_runtime *runtime = NULL;
+       size_t runtime_alloc_len;
+
+       if (!filter_bytecode)
+               return 0;
+       /* Bytecode already linked */
+       if (bytecode_is_linked(filter_bytecode, event))
+               return 0;
+
+       dbg_printk("Linking...\n");
+
+       /* We don't need the reloc table in the runtime */
+       runtime_alloc_len = sizeof(*runtime) + filter_bytecode->bc.reloc_offset;
+       runtime = kzalloc(runtime_alloc_len, GFP_KERNEL);
+       if (!runtime) {
+               ret = -ENOMEM;
+               goto alloc_error;
+       }
+       runtime->p.bc = filter_bytecode;
+       runtime->len = filter_bytecode->bc.reloc_offset;
+       /* copy original bytecode */
+       memcpy(runtime->data, filter_bytecode->bc.data, runtime->len);
+       /*
+        * apply relocs. Those are a uint16_t (offset in bytecode)
+        * followed by a string (field name).
+        */
+       for (offset = filter_bytecode->bc.reloc_offset;
+                       offset < filter_bytecode->bc.len;
+                       offset = next_offset) {
+               uint16_t reloc_offset =
+                       *(uint16_t *) &filter_bytecode->bc.data[offset];
+               const char *name =
+                       (const char *) &filter_bytecode->bc.data[offset + sizeof(uint16_t)];
+
+               ret = apply_reloc(event, runtime, runtime->len, reloc_offset, name);
+               if (ret) {
+                       goto link_error;
+               }
+               next_offset = offset + sizeof(uint16_t) + strlen(name) + 1;
+       }
+       /* Validate bytecode */
+       ret = lttng_filter_validate_bytecode(runtime);
+       if (ret) {
+               goto link_error;
+       }
+       /* Specialize bytecode */
+       ret = lttng_filter_specialize_bytecode(runtime);
+       if (ret) {
+               goto link_error;
+       }
+       runtime->p.filter = lttng_filter_interpret_bytecode;
+       runtime->p.link_failed = 0;
+       list_add_rcu(&runtime->p.node, insert_loc);
+       dbg_printk("Linking successful.\n");
+       return 0;
+
+link_error:
+       runtime->p.filter = lttng_filter_false;
+       runtime->p.link_failed = 1;
+       list_add_rcu(&runtime->p.node, insert_loc);
+alloc_error:
+       dbg_printk("Linking failed.\n");
+       return ret;
+}
+
+void lttng_filter_sync_state(struct lttng_bytecode_runtime *runtime)
+{
+       struct lttng_filter_bytecode_node *bc = runtime->bc;
+
+       if (!bc->enabler->enabled || runtime->link_failed)
+               runtime->filter = lttng_filter_false;
+       else
+               runtime->filter = lttng_filter_interpret_bytecode;
+}
+
+/*
+ * Link bytecode for all enablers referenced by an event.
+ */
+void lttng_enabler_event_link_bytecode(struct lttng_event *event,
+               struct lttng_enabler *enabler)
+{
+       struct lttng_filter_bytecode_node *bc;
+       struct lttng_bytecode_runtime *runtime;
+
+       /* Can only be called for events with desc attached */
+       WARN_ON_ONCE(!event->desc);
+
+       /* Link each bytecode. */
+       list_for_each_entry(bc, &enabler->filter_bytecode_head, node) {
+               int found = 0, ret;
+               struct list_head *insert_loc;
+
+               list_for_each_entry(runtime,
+                               &event->bytecode_runtime_head, node) {
+                       if (runtime->bc == bc) {
+                               found = 1;
+                               break;
+                       }
+               }
+               /* Skip bytecode already linked */
+               if (found)
+                       continue;
+
+               /*
+                * Insert at specified priority (seqnum) in increasing
+                * order.
+                */
+               list_for_each_entry_reverse(runtime,
+                               &event->bytecode_runtime_head, node) {
+                       if (runtime->bc->bc.seqnum < bc->bc.seqnum) {
+                               /* insert here */
+                               insert_loc = &runtime->node;
+                               goto add_within;
+                       }
+               }
+               /* Add to head to list */
+               insert_loc = &event->bytecode_runtime_head;
+       add_within:
+               dbg_printk("linking bytecode\n");
+               ret = _lttng_filter_event_link_bytecode(event, bc,
+                               insert_loc);
+               if (ret) {
+                       dbg_printk("[lttng filter] warning: cannot link event bytecode\n");
+               }
+       }
+}
+
+/*
+ * We own the filter_bytecode if we return success.
+ */
+int lttng_filter_enabler_attach_bytecode(struct lttng_enabler *enabler,
+               struct lttng_filter_bytecode_node *filter_bytecode)
+{
+       list_add(&filter_bytecode->node, &enabler->filter_bytecode_head);
+       return 0;
+}
+
+void lttng_free_enabler_filter_bytecode(struct lttng_enabler *enabler)
+{
+       struct lttng_filter_bytecode_node *filter_bytecode, *tmp;
+
+       list_for_each_entry_safe(filter_bytecode, tmp,
+                       &enabler->filter_bytecode_head, node) {
+               kfree(filter_bytecode);
+       }
+}
+
+void lttng_free_event_filter_runtime(struct lttng_event *event)
+{
+       struct bytecode_runtime *runtime, *tmp;
+
+       list_for_each_entry_safe(runtime, tmp,
+                       &event->bytecode_runtime_head, p.node) {
+               kfree(runtime);
+       }
+}
diff --git a/lttng-filter.h b/lttng-filter.h
new file mode 100644 (file)
index 0000000..98b97c2
--- /dev/null
@@ -0,0 +1,176 @@
+#ifndef _LTTNG_FILTER_H
+#define _LTTNG_FILTER_H
+
+/*
+ * lttng-filter.h
+ *
+ * LTTng modules filter header.
+ *
+ * Copyright (C) 2010-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "lttng-events.h"
+#include "filter-bytecode.h"
+
+/* Filter stack length, in number of entries */
+#define FILTER_STACK_LEN       10      /* includes 2 dummy */
+#define FILTER_STACK_EMPTY     1
+
+#ifdef DEBUG
+#define dbg_printk(fmt, args...)                               \
+       printk(KERN_DEBUG "[debug bytecode in %s:%s@%u] " fmt,          \
+               __FILE__, __func__, __LINE__, ## args)
+#else
+#define dbg_printk(fmt, args...)                               \
+do {                                                           \
+       /* do nothing but check printf format */                \
+       if (0)                                                  \
+               printk(KERN_DEBUG "[debug bytecode in %s:%s@%u] " fmt,  \
+                       __FILE__, __func__, __LINE__, ## args); \
+} while (0)
+#endif
+
+/* Linked bytecode. Child of struct lttng_bytecode_runtime. */
+struct bytecode_runtime {
+       struct lttng_bytecode_runtime p;
+       uint16_t len;
+       char data[0];
+};
+
+enum entry_type {
+       REG_S64,
+       REG_DOUBLE,
+       REG_STRING,
+       REG_TYPE_UNKNOWN,
+};
+
+/* Validation stack */
+struct vstack_entry {
+       enum entry_type type;
+};
+
+struct vstack {
+       int top;        /* top of stack */
+       struct vstack_entry e[FILTER_STACK_LEN];
+};
+
+static inline
+void vstack_init(struct vstack *stack)
+{
+       stack->top = -1;
+}
+
+static inline
+struct vstack_entry *vstack_ax(struct vstack *stack)
+{
+       if (unlikely(stack->top < 0))
+               return NULL;
+       return &stack->e[stack->top];
+}
+
+static inline
+struct vstack_entry *vstack_bx(struct vstack *stack)
+{
+       if (unlikely(stack->top < 1))
+               return NULL;
+       return &stack->e[stack->top - 1];
+}
+
+static inline
+int vstack_push(struct vstack *stack)
+{
+       if (stack->top >= FILTER_STACK_LEN - 1) {
+               printk(KERN_WARNING "Stack full\n");
+               return -EINVAL;
+       }
+       ++stack->top;
+       return 0;
+}
+
+static inline
+int vstack_pop(struct vstack *stack)
+{
+       if (unlikely(stack->top < 0)) {
+               printk(KERN_WARNING "Stack empty\n");
+               return -EINVAL;
+       }
+       stack->top--;
+       return 0;
+}
+
+/* Execution stack */
+struct estack_entry {
+       union {
+               int64_t v;
+
+               struct {
+                       const char *str;
+                       size_t seq_len;
+                       int literal;            /* is string literal ? */
+               } s;
+       } u;
+};
+
+struct estack {
+       int top;        /* top of stack */
+       struct estack_entry e[FILTER_STACK_LEN];
+};
+
+#define estack_ax_v    ax
+#define estack_bx_v    bx
+
+#define estack_ax(stack, top)                                  \
+       ({                                                      \
+               WARN_ON_ONCE((top) <= FILTER_STACK_EMPTY);      \
+               &(stack)->e[top];                               \
+       })
+
+#define estack_bx(stack, top)                                  \
+       ({                                                      \
+               WARN_ON_ONCE((top) <= FILTER_STACK_EMPTY + 1);  \
+               &(stack)->e[(top) - 1];                         \
+       })
+
+#define estack_push(stack, top, ax, bx)                                \
+       do {                                                    \
+               WARN_ON_ONCE((top) >= FILTER_STACK_LEN - 1);    \
+               (stack)->e[(top) - 1].u.v = (bx);               \
+               (bx) = (ax);                                    \
+               ++(top);                                        \
+       } while (0)
+
+#define estack_pop(stack, top, ax, bx)                         \
+       do {                                                    \
+               WARN_ON_ONCE((top) <= FILTER_STACK_EMPTY);      \
+               (ax) = (bx);                                    \
+               (bx) = (stack)->e[(top) - 2].u.v;               \
+               (top)--;                                        \
+       } while (0)
+
+const char *lttng_filter_print_op(enum filter_op op);
+
+int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode);
+int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode);
+
+uint64_t lttng_filter_false(void *filter_data,
+               const char *filter_stack_data);
+uint64_t lttng_filter_interpret_bytecode(void *filter_data,
+               const char *filter_stack_data);
+
+#endif /* _LTTNG_FILTER_H */
This page took 0.101195 seconds and 4 git commands to generate.