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 \
--- /dev/null
+#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 */
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;
}
} 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 \
#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)
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;
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;
#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;
}
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.
*/
kfree(ctx->fields);
kfree(ctx);
}
+
+int lttng_context_init(void)
+{
+ int ret;
+
+ ret = lttng_add_vpid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+#if 0 //TODO
+ ret = lttng_add_pid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_pthread_id_to_ctx");
+ }
+ ret = lttng_add_procname_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_vtid_to_ctx");
+ }
+ ret = lttng_add_prio_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_vpid_to_ctx");
+ }
+ ret = lttng_add_nice_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+ ret = lttng_add_tid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+ ret = lttng_add_vtid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+ ret = lttng_add_ppid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+ ret = lttng_add_vppid_to_ctx(<tng_static_ctx);
+ if (ret) {
+ printk(KERN_WARNING "Cannot add context lttng_add_procname_to_ctx");
+ }
+ ret = lttng_add_hostname_to_ctx(<tng_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;
+}
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) {
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;
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)
{
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);
*/
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:
} 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);
+ }
}
}
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;
kmem_cache_destroy(event_cache);
error_kmem:
lttng_tracepoint_exit();
+error_tp:
+ lttng_context_exit();
return ret;
}
lttng_session_destroy(session);
kmem_cache_destroy(event_cache);
lttng_tracepoint_exit();
+ lttng_context_exit();
}
module_exit(lttng_events_exit);
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.
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;
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.
*/
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 {
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;
}
#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);
--- /dev/null
+/*
+ * 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 = <tng_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 = <tng_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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 = <tng_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);
+ }
+}
--- /dev/null
+#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 */