X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust%2Flttng-filter.c;h=6d64924b94a6683e665d30cc265d9e09c240dd3d;hb=225b6640cf81f528f6662b502a5cb631e12f0eca;hp=b617bc9ad1e1aa47529c875b3ec4d05e3de0e7b5;hpb=2d78951a159c97fd2bfebb84a9b22ef97674d56a;p=lttng-ust.git diff --git a/liblttng-ust/lttng-filter.c b/liblttng-ust/lttng-filter.c index b617bc9a..6d64924b 100644 --- a/liblttng-ust/lttng-filter.c +++ b/liblttng-ust/lttng-filter.c @@ -24,26 +24,1215 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include "filter-bytecode.h" + +#define NR_REG 2 + +#ifndef min_t +#define min_t(type, a, b) \ + ((type) (a) < (type) (b) ? (type) (a) : (type) (b)) +#endif + +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#ifdef DEBUG +#define dbg_printf(fmt, args...) printf("[debug bytecode] " fmt, ## args) +#else +#define dbg_printf(fmt, args...) \ +do { \ + /* do nothing but check printf format */ \ + if (0) \ + printf("[debug bytecode] " fmt, ## args); \ +} while (0) +#endif + +/* Linked bytecode */ +struct bytecode_runtime { + uint16_t len; + char data[0]; +}; + +enum reg_type { + REG_S64, + REG_DOUBLE, + REG_STRING, + REG_TYPE_UNKNOWN, +}; + +/* Validation registers */ +struct vreg { + enum reg_type type; + int literal; /* is string literal ? */ +}; + +/* Execution registers */ +struct reg { + enum reg_type type; + int64_t v; + double d; + + const char *str; + size_t seq_len; + int literal; /* is string literal ? */ +}; + +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", + [ FILTER_OP_EQ ] = "EQ", + [ FILTER_OP_NE ] = "NE", + [ FILTER_OP_GT ] = "GT", + [ FILTER_OP_LT ] = "LT", + [ FILTER_OP_GE ] = "GE", + [ FILTER_OP_LE ] = "LE", + + /* unary */ + [ FILTER_OP_UNARY_PLUS ] = "UNARY_PLUS", + [ FILTER_OP_UNARY_MINUS ] = "UNARY_MINUS", + [ FILTER_OP_UNARY_NOT ] = "UNARY_NOT", + + /* logical */ + [ FILTER_OP_AND ] = "AND", + [ FILTER_OP_OR ] = "OR", + + /* load */ + [ 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", + + [ FILTER_OP_LOAD_STRING ] = "LOAD_STRING", + [ FILTER_OP_LOAD_S64 ] = "LOAD_S64", + [ FILTER_OP_LOAD_DOUBLE ] = "LOAD_DOUBLE", +}; + +static +const char *print_op(enum filter_op op) +{ + if (op >= NR_FILTER_OPS) + return "UNKNOWN"; + else + return opnames[op]; +} + +/* + * -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 reg_strcmp(struct reg reg[NR_REG], const char *cmp_type) +{ + const char *p = reg[REG_R0].str, *q = reg[REG_R1].str; + int ret; + int diff; + + for (;;) { + int escaped_r0 = 0; + + if (unlikely(p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0')) { + if (q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0') + diff = 0; + else + diff = -1; + break; + } + if (unlikely(q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0')) { + if (p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0') + diff = 0; + else + diff = 1; + break; + } + if (reg[REG_R0].literal) { + ret = parse_char(&p); + if (ret == -1) { + return 0; + } else if (ret == -2) { + escaped_r0 = 1; + } + /* else compare both char */ + } + if (reg[REG_R1].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; +} + +static +int lttng_filter_false(void *filter_data, + const char *filter_stack_data) +{ + return 0; +} static int lttng_filter_interpret_bytecode(void *filter_data, const char *filter_stack_data) { - /* TODO */ + struct bytecode_runtime *bytecode = filter_data; + void *pc, *next_pc, *start_pc; + int ret = -EINVAL; + int retval = 0; + struct reg reg[NR_REG]; + + start_pc = &bytecode->data[0]; + for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; + pc = next_pc) { + dbg_printf("Executing op %s (%u)\n", + print_op((unsigned int) *(filter_opcode_t *) pc), + (unsigned int) *(filter_opcode_t *) pc); + switch (*(filter_opcode_t *) pc) { + case FILTER_OP_UNKNOWN: + case FILTER_OP_LOAD_FIELD_REF: + default: + ERR("unknown bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_RETURN: + retval = !!reg[0].v; + 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: + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_EQ: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, "==") == 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v == reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v == reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d == reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d == reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_NE: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, "!=") != 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v != reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v != reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d != reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d != reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GT: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, ">") > 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v > reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v > reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d > reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d > reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LT: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, "<") < 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v < reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v < reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d < reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d < reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GE: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, ">=") >= 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v >= reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v >= reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d >= reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d >= reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LE: + { + switch (reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + reg[REG_R0].v = (reg_strcmp(reg, "<=") <= 0); + break; + case REG_S64: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].v <= reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].v <= reg[REG_R1].d); + break; + } + break; + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + reg[REG_R0].v = (reg[REG_R0].d <= reg[REG_R1].v); + break; + case REG_DOUBLE: + reg[REG_R0].v = (reg[REG_R0].d <= reg[REG_R1].d); + break; + } + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + /* unary */ + case FILTER_OP_UNARY_PLUS: + { + next_pc += sizeof(struct unary_op); + break; + } + case FILTER_OP_UNARY_MINUS: + { + struct unary_op *insn = (struct unary_op *) pc; + + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Unary minus can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + reg[insn->reg].v = -reg[insn->reg].v; + break; + case REG_DOUBLE: + reg[insn->reg].d = -reg[insn->reg].d; + break; + } + next_pc += sizeof(struct unary_op); + break; + } + case FILTER_OP_UNARY_NOT: + { + struct unary_op *insn = (struct unary_op *) pc; + + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Unary not can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + reg[insn->reg].v = !reg[insn->reg].v; + break; + case REG_DOUBLE: + reg[insn->reg].d = !reg[insn->reg].d; + break; + } + reg[insn->reg].v = !reg[insn->reg].v; + next_pc += sizeof(struct unary_op); + break; + } + /* logical */ + case FILTER_OP_AND: + { + struct logical_op *insn = (struct logical_op *) pc; + + /* If REG_R0 is 0, skip and evaluate to 0 */ + if ((reg[REG_R0].type == REG_S64 && reg[REG_R0].v == 0) + || (reg[REG_R0].type == REG_DOUBLE && reg[REG_R0].d == 0.0)) { + dbg_printf("Jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + next_pc = start_pc + insn->skip_offset; + } else { + next_pc += sizeof(struct logical_op); + } + break; + } + case FILTER_OP_OR: + { + struct logical_op *insn = (struct logical_op *) pc; + + /* If REG_R0 is nonzero, skip and evaluate to 1 */ + + if ((reg[REG_R0].type == REG_S64 && reg[REG_R0].v != 0) + || (reg[REG_R0].type == REG_DOUBLE && reg[REG_R0].d != 0.0)) { + reg[REG_R0].v = 1; + dbg_printf("Jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + next_pc = start_pc + insn->skip_offset; + } else { + next_pc += sizeof(struct logical_op); + } + break; + } + + /* load */ + case FILTER_OP_LOAD_FIELD_REF_STRING: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type string\n", + ref->offset); + reg[insn->reg].str = + *(const char * const *) &filter_stack_data[ref->offset]; + reg[insn->reg].type = REG_STRING; + reg[insn->reg].seq_len = UINT_MAX; + reg[insn->reg].literal = 0; + dbg_printf("ref load string %s\n", reg[insn->reg].str); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + 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_printf("load field ref offset %u type sequence\n", + ref->offset); + reg[insn->reg].seq_len = + *(unsigned long *) &filter_stack_data[ref->offset]; + reg[insn->reg].str = + *(const char **) (&filter_stack_data[ref->offset + + sizeof(unsigned long)]); + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + 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_printf("load field ref offset %u type s64\n", + ref->offset); + memcpy(®[insn->reg].v, &filter_stack_data[ref->offset], + sizeof(struct literal_numeric)); + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 0; + dbg_printf("ref load s64 %" PRIi64 "\n", reg[insn->reg].v); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + case FILTER_OP_LOAD_FIELD_REF_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type double\n", + ref->offset); + memcpy(®[insn->reg].d, &filter_stack_data[ref->offset], + sizeof(struct literal_double)); + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 0; + dbg_printf("ref load double %g\n", reg[insn->reg].d); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + case FILTER_OP_LOAD_STRING: + { + struct load_op *insn = (struct load_op *) pc; + + dbg_printf("load string %s\n", insn->data); + reg[insn->reg].str = insn->data; + reg[insn->reg].type = REG_STRING; + reg[insn->reg].seq_len = UINT_MAX; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + break; + } + + case FILTER_OP_LOAD_S64: + { + struct load_op *insn = (struct load_op *) pc; + + memcpy(®[insn->reg].v, insn->data, + sizeof(struct literal_numeric)); + dbg_printf("load s64 %" PRIi64 "\n", reg[insn->reg].v); + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_numeric); + break; + } + + case FILTER_OP_LOAD_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + + memcpy(®[insn->reg].d, insn->data, + sizeof(struct literal_double)); + dbg_printf("load s64 %g\n", reg[insn->reg].d); + reg[insn->reg].type = REG_DOUBLE; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_double); + break; + } + } + } +end: + /* return 0 (discard) on error */ + if (ret) + return 0; + return retval; +} + +static +int bin_op_compare_check(struct vreg reg[NR_REG], const char *str) +{ + switch (reg[REG_R0].type) { + default: + goto error_unknown; + + case REG_STRING: + switch (reg[REG_R1].type) { + default: + goto error_unknown; + + case REG_STRING: + break; + case REG_S64: + case REG_DOUBLE: + goto error_mismatch; + } + break; + case REG_S64: + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + goto error_unknown; + + case REG_STRING: + goto error_mismatch; + + case REG_S64: + case REG_DOUBLE: + break; + } + break; + } return 0; + +error_unknown: + + return -EINVAL; +error_mismatch: + ERR("type mismatch for '%s' binary operator\n", str); + return -EINVAL; } +static +int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode) +{ + void *pc, *next_pc, *start_pc; + int ret = -EINVAL; + struct vreg reg[NR_REG]; + int i; + + for (i = 0; i < NR_REG; i++) { + reg[i].type = REG_TYPE_UNKNOWN; + reg[i].literal = 0; + } + + start_pc = &bytecode->data[0]; + for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; + pc = next_pc) { + if (unlikely(pc >= start_pc + bytecode->len)) { + ERR("filter bytecode overflow\n"); + ret = -EINVAL; + goto end; + } + dbg_printf("Validating op %s (%u)\n", + print_op((unsigned int) *(filter_opcode_t *) pc), + (unsigned int) *(filter_opcode_t *) pc); + switch (*(filter_opcode_t *) pc) { + case FILTER_OP_UNKNOWN: + default: + ERR("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: + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_EQ: + { + ret = bin_op_compare_check(reg, "=="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_NE: + { + ret = bin_op_compare_check(reg, "!="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GT: + { + ret = bin_op_compare_check(reg, ">"); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LT: + { + ret = bin_op_compare_check(reg, "<"); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GE: + { + ret = bin_op_compare_check(reg, ">="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LE: + { + ret = bin_op_compare_check(reg, "<="); + if (ret) + goto end; + reg[REG_R0].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: + { + struct unary_op *insn = (struct unary_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Unary op can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + break; + case REG_DOUBLE: + break; + } + next_pc += sizeof(struct unary_op); + break; + } + /* logical */ + case FILTER_OP_AND: + case FILTER_OP_OR: + { + struct logical_op *insn = (struct logical_op *) pc; + + if (unlikely(reg[REG_R0].type == REG_TYPE_UNKNOWN + || reg[REG_R0].type == REG_TYPE_UNKNOWN + || reg[REG_R0].type == REG_STRING + || reg[REG_R1].type == REG_STRING)) { + ERR("Logical comparator can only be applied to numeric and floating point registers\n"); + ret = -EINVAL; + goto end; + } + + dbg_printf("Validate jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + if (unlikely(start_pc + insn->skip_offset <= pc)) { + ERR("Loops are not allowed in bytecode\n"); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct logical_op); + break; + } + + /* load */ + case FILTER_OP_LOAD_FIELD_REF: + { + ERR("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; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type string\n", + ref->offset); + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + 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; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type s64\n", + ref->offset); + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + case FILTER_OP_LOAD_FIELD_REF_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type double\n", + ref->offset); + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + case FILTER_OP_LOAD_STRING: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + break; + } + + case FILTER_OP_LOAD_S64: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_numeric); + break; + } + + case FILTER_OP_LOAD_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_DOUBLE; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_double); + break; + } + } + } +end: + return ret; +} + +static +int apply_field_reloc(struct ltt_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_printf("Apply reloc: %u %s\n", reloc_offset, field_name); + + /* Ensure that the reloc is within the code */ + if (runtime_len - reloc_offset < sizeof(uint16_t)) + return -EINVAL; + + /* 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; + case atype_float: + field_offset += sizeof(double); + 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) + 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; + case atype_float: + op->op = FILTER_OP_LOAD_FIELD_REF_DOUBLE; + break; + default: + return -EINVAL; + } + /* set offset */ + field_ref->offset = (uint16_t) field_offset; + 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 ltt_event *event, struct lttng_ust_filter_bytecode *filter_bytecode) { + int ret, offset, next_offset; + struct bytecode_runtime *runtime = NULL; + size_t runtime_alloc_len; + if (!filter_bytecode) return 0; + /* Even is not connected to any description */ + if (!event->desc) + return 0; + /* Bytecode already linked */ + if (event->filter || event->filter_data) + return 0; + + dbg_printf("Linking\n"); + /* We don't need the reloc table in the runtime */ + runtime_alloc_len = sizeof(*runtime) + filter_bytecode->reloc_offset; + runtime = zmalloc(runtime_alloc_len); + if (!runtime) { + ret = -ENOMEM; + goto link_error; + } + runtime->len = filter_bytecode->reloc_offset; + /* copy original bytecode */ + memcpy(runtime->data, filter_bytecode->data, runtime->len); + /* + * apply relocs. Those are a uint16_t (offset in bytecode) + * followed by a string (field name). + */ + for (offset = filter_bytecode->reloc_offset; + offset < filter_bytecode->len; + offset = next_offset) { + uint16_t reloc_offset = + *(uint16_t *) &filter_bytecode->data[offset]; + const char *field_name = + (const char *) &filter_bytecode->data[offset + sizeof(uint16_t)]; + + ret = apply_field_reloc(event, runtime, runtime->len, reloc_offset, field_name); + if (ret) { + goto link_error; + } + next_offset = offset + sizeof(uint16_t) + strlen(field_name) + 1; + } + /* Validate bytecode */ + ret = lttng_filter_validate_bytecode(runtime); + if (ret) { + goto link_error; + } + event->filter_data = runtime; event->filter = lttng_filter_interpret_bytecode; - /* TODO */ - /* event->filter_data = ; */ return 0; + +link_error: + event->filter = lttng_filter_false; + free(runtime); + return ret; } void lttng_filter_event_link_bytecode(struct ltt_event *event, @@ -51,7 +1240,7 @@ void lttng_filter_event_link_bytecode(struct ltt_event *event, { int ret; - ret = _lttng_filter_event_link_bytecode(event, event->filter_bytecode); + ret = _lttng_filter_event_link_bytecode(event, filter_bytecode); if (ret) { fprintf(stderr, "[lttng filter] error linking event bytecode\n"); } @@ -86,44 +1275,30 @@ void lttng_filter_wildcard_link_bytecode(struct session_wildcard *wildcard) /* * Need to attach filter to an event before starting tracing for the - * session. + * session. We own the filter_bytecode if we return success. */ int lttng_filter_event_attach_bytecode(struct ltt_event *event, struct lttng_ust_filter_bytecode *filter_bytecode) { - struct lttng_ust_filter_bytecode *bc; - if (event->chan->session->been_active) return -EPERM; if (event->filter_bytecode) return -EEXIST; - - bc = zmalloc(sizeof(struct lttng_ust_filter_bytecode) - + filter_bytecode->len); - if (!bc) - return -ENOMEM; - event->filter_bytecode = bc; + event->filter_bytecode = filter_bytecode; return 0; } /* * Need to attach filter to a wildcard before starting tracing for the - * session. + * session. We own the filter_bytecode if we return success. */ int lttng_filter_wildcard_attach_bytecode(struct session_wildcard *wildcard, struct lttng_ust_filter_bytecode *filter_bytecode) { - struct lttng_ust_filter_bytecode *bc; - if (wildcard->chan->session->been_active) return -EPERM; if (wildcard->filter_bytecode) return -EEXIST; - - bc = zmalloc(sizeof(struct lttng_ust_filter_bytecode) - + filter_bytecode->len); - if (!bc) - return -ENOMEM; - wildcard->filter_bytecode = bc; + wildcard->filter_bytecode = filter_bytecode; return 0; }