*
* LTTng filter bytecode generation
*
- * Copyright 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * 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.
+ * SPDX-License-Identifier: LGPL-2.1-only
*
- * 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 <stdlib.h>
#include <string.h>
#include <errno.h>
#include <common/align.h>
+#include <common/compat/string.h>
#include "filter-bytecode.h"
#include "filter-ir.h"
int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx,
struct ir_op *node);
-static inline int fls(unsigned int x)
-{
- int r = 32;
-
- if (!x)
- return 0;
- if (!(x & 0xFFFF0000U)) {
- x <<= 16;
- r -= 16;
- }
- if (!(x & 0xFF000000U)) {
- x <<= 8;
- r -= 8;
- }
- if (!(x & 0xF0000000U)) {
- x <<= 4;
- r -= 4;
- }
- if (!(x & 0xC0000000U)) {
- x <<= 2;
- r -= 2;
- }
- if (!(x & 0x80000000U)) {
- x <<= 1;
- r -= 1;
- }
- return r;
-}
-
static inline int get_count_order(unsigned int count)
{
int order;
- order = fls(count) - 1;
+ order = lttng_fls(count) - 1;
if (count & (count - 1))
order++;
return order;
return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
}
+static
+int append_str(char **s, const char *append)
+{
+ char *old = *s;
+ char *new;
+ size_t oldlen = (old == NULL) ? 0 : strlen(old);
+ size_t appendlen = strlen(append);
+
+ new = calloc(oldlen + appendlen + 1, 1);
+ if (!new) {
+ return -ENOMEM;
+ }
+ if (oldlen) {
+ strcpy(new, old);
+ }
+ strcat(new, append);
+ *s = new;
+ free(old);
+ return 0;
+}
+
+/*
+ * 1: match
+ * 0: no match
+ * < 0: error
+ */
+static
+int load_expression_legacy_match(const struct ir_load_expression *exp,
+ enum filter_op *op_type,
+ char **symbol)
+{
+ const struct ir_load_expression_op *op;
+ bool need_dot = false;
+
+ op = exp->child;
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ *op_type = FILTER_OP_GET_CONTEXT_REF;
+ if (append_str(symbol, "$ctx.")) {
+ return -ENOMEM;
+ }
+ need_dot = false;
+ break;
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ *op_type = FILTER_OP_GET_CONTEXT_REF;
+ if (append_str(symbol, "$app.")) {
+ return -ENOMEM;
+ }
+ need_dot = false;
+ break;
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ *op_type = FILTER_OP_LOAD_FIELD_REF;
+ need_dot = false;
+ break;
+
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ default:
+ return 0; /* no match */
+ }
+
+ for (;;) {
+ op = op->next;
+ if (!op) {
+ return 0; /* no match */
+ }
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ goto end;
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ if (need_dot && append_str(symbol, ".")) {
+ return -ENOMEM;
+ }
+ if (append_str(symbol, op->u.symbol)) {
+ return -ENOMEM;
+ }
+ break;
+ default:
+ return 0; /* no match */
+ }
+ need_dot = true;
+ }
+end:
+ return 1; /* Legacy match */
+}
+
+/*
+ * 1: legacy match
+ * 0: no legacy match
+ * < 0: error
+ */
+static
+int visit_node_load_expression_legacy(struct filter_parser_ctx *ctx,
+ const struct ir_load_expression *exp,
+ const struct ir_load_expression_op *op)
+{
+ struct load_op *insn = NULL;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct field_ref);
+ struct field_ref ref_offset;
+ uint32_t reloc_offset_u32;
+ uint16_t reloc_offset;
+ enum filter_op op_type;
+ char *symbol = NULL;
+ int ret;
+
+ ret = load_expression_legacy_match(exp, &op_type, &symbol);
+ if (ret <= 0) {
+ goto end;
+ }
+ insn = calloc(insn_len, 1);
+ if (!insn) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ insn->op = op_type;
+ ref_offset.offset = (uint16_t) -1U;
+ memcpy(insn->data, &ref_offset, sizeof(ref_offset));
+ /* reloc_offset points to struct load_op */
+ reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
+ if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+ ret = -EINVAL;
+ goto end;
+ }
+ reloc_offset = (uint16_t) reloc_offset_u32;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ if (ret) {
+ goto end;
+ }
+ /* append reloc */
+ ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
+ 1, sizeof(reloc_offset));
+ if (ret) {
+ goto end;
+ }
+ ret = bytecode_push(&ctx->bytecode_reloc, symbol,
+ 1, strlen(symbol) + 1);
+ if (ret) {
+ goto end;
+ }
+ ret = 1; /* legacy */
+end:
+ free(insn);
+ free(symbol);
+ return ret;
+}
+
+static
+int visit_node_load_expression(struct filter_parser_ctx *ctx,
+ const struct ir_op *node)
+{
+ struct ir_load_expression *exp;
+ struct ir_load_expression_op *op;
+ int ret;
+
+ exp = node->u.load.u.expression;
+ if (!exp) {
+ return -EINVAL;
+ }
+ op = exp->child;
+ if (!op) {
+ return -EINVAL;
+ }
+
+ /*
+ * TODO: if we remove legacy load for application contexts, we
+ * need to update session bytecode parser as well.
+ */
+ ret = visit_node_load_expression_legacy(ctx, exp, op);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret > 0) {
+ return 0; /* legacy */
+ }
+
+ for (; op != NULL; op = op->next) {
+ switch (op->type) {
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_GET_CONTEXT_ROOT;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_GET_APP_CONTEXT_ROOT;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_GET_PAYLOAD_ROOT;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct get_symbol);
+ struct get_symbol symbol_offset;
+ uint32_t reloc_offset_u32;
+ uint16_t reloc_offset;
+ uint32_t bytecode_reloc_offset_u32;
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_GET_SYMBOL;
+ bytecode_reloc_offset_u32 =
+ bytecode_get_len(&ctx->bytecode_reloc->b)
+ + sizeof(reloc_offset);
+ symbol_offset.offset =
+ (uint16_t) bytecode_reloc_offset_u32;
+ memcpy(insn->data, &symbol_offset,
+ sizeof(symbol_offset));
+ /* reloc_offset points to struct load_op */
+ reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
+ if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
+ free(insn);
+ return -EINVAL;
+ }
+ reloc_offset = (uint16_t) reloc_offset_u32;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ if (ret) {
+ free(insn);
+ return ret;
+ }
+ /* append reloc */
+ ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
+ 1, sizeof(reloc_offset));
+ if (ret) {
+ free(insn);
+ return ret;
+ }
+ ret = bytecode_push(&ctx->bytecode_reloc,
+ op->u.symbol,
+ 1, strlen(op->u.symbol) + 1);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_INDEX:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op)
+ + sizeof(struct get_index_u64);
+ struct get_index_u64 index;
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_GET_INDEX_U64;
+ index.index = op->u.index;
+ memcpy(insn->data, &index, sizeof(index));
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ {
+ struct load_op *insn;
+ uint32_t insn_len = sizeof(struct load_op);
+ int ret;
+
+ insn = calloc(insn_len, 1);
+ if (!insn)
+ return -ENOMEM;
+ insn->op = FILTER_OP_LOAD_FIELD;
+ ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
+ free(insn);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
static
int visit_node_load(struct filter_parser_ctx *ctx, struct ir_op *node)
{
{
struct load_op *insn;
uint32_t insn_len = sizeof(struct load_op)
- + strlen(node->u.load.u.string) + 1;
+ + strlen(node->u.load.u.string.value) + 1;
insn = calloc(insn_len, 1);
if (!insn)
return -ENOMEM;
- insn->op = FILTER_OP_LOAD_STRING;
- strcpy(insn->data, node->u.load.u.string);
+
+ switch (node->u.load.u.string.type) {
+ case IR_LOAD_STRING_TYPE_GLOB_STAR:
+ /*
+ * We explicitly tell the interpreter here that
+ * this load is a full star globbing pattern so
+ * that the appropriate matching function can be
+ * called. Also, see comment below.
+ */
+ insn->op = FILTER_OP_LOAD_STAR_GLOB_STRING;
+ break;
+ default:
+ /*
+ * This is the "legacy" string, which includes
+ * star globbing patterns with a star only at
+ * the end. Both "plain" and "star at the end"
+ * literal strings are handled at the same place
+ * by the tracer's filter bytecode interpreter,
+ * whereas full star globbing patterns (stars
+ * can be anywhere in the string) is a special
+ * case.
+ */
+ insn->op = FILTER_OP_LOAD_STRING;
+ break;
+ }
+
+ strcpy(insn->data, node->u.load.u.string.value);
ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
free(insn);
return ret;
free(insn);
return ret;
}
- case IR_DATA_FIELD_REF: /* fall-through */
- case IR_DATA_GET_CONTEXT_REF:
- {
- struct load_op *insn;
- uint32_t insn_len = sizeof(struct load_op)
- + sizeof(struct field_ref);
- struct field_ref ref_offset;
- uint32_t reloc_offset_u32;
- uint16_t reloc_offset;
-
- insn = calloc(insn_len, 1);
- if (!insn)
- return -ENOMEM;
- switch(node->data_type) {
- case IR_DATA_FIELD_REF:
- insn->op = FILTER_OP_LOAD_FIELD_REF;
- break;
- case IR_DATA_GET_CONTEXT_REF:
- insn->op = FILTER_OP_GET_CONTEXT_REF;
- break;
- default:
- free(insn);
- return -EINVAL;
- }
- ref_offset.offset = (uint16_t) -1U;
- memcpy(insn->data, &ref_offset, sizeof(ref_offset));
- /* reloc_offset points to struct load_op */
- reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b);
- if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) {
- free(insn);
- return -EINVAL;
- }
- reloc_offset = (uint16_t) reloc_offset_u32;
- ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len);
- if (ret) {
- free(insn);
- return ret;
- }
- /* append reloc */
- ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset,
- 1, sizeof(reloc_offset));
- if (ret) {
- free(insn);
- return ret;
- }
- ret = bytecode_push(&ctx->bytecode_reloc, node->u.load.u.ref,
- 1, strlen(node->u.load.u.ref) + 1);
- free(insn);
- return ret;
- }
+ case IR_DATA_EXPRESSION:
+ return visit_node_load_expression(ctx, node);
}
}
case AST_UNARY_NOT:
insn.op = FILTER_OP_UNARY_NOT;
return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
+ case AST_UNARY_BIT_NOT:
+ insn.op = FILTER_OP_UNARY_BIT_NOT;
+ return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn));
}
}
case AST_OP_MINUS:
insn.op = FILTER_OP_MINUS;
break;
- case AST_OP_RSHIFT:
- insn.op = FILTER_OP_RSHIFT;
+ case AST_OP_BIT_RSHIFT:
+ insn.op = FILTER_OP_BIT_RSHIFT;
break;
- case AST_OP_LSHIFT:
- insn.op = FILTER_OP_LSHIFT;
+ case AST_OP_BIT_LSHIFT:
+ insn.op = FILTER_OP_BIT_LSHIFT;
break;
- case AST_OP_BIN_AND:
- insn.op = FILTER_OP_BIN_AND;
+ case AST_OP_BIT_AND:
+ insn.op = FILTER_OP_BIT_AND;
break;
- case AST_OP_BIN_OR:
- insn.op = FILTER_OP_BIN_OR;
+ case AST_OP_BIT_OR:
+ insn.op = FILTER_OP_BIT_OR;
break;
- case AST_OP_BIN_XOR:
- insn.op = FILTER_OP_BIN_XOR;
+ case AST_OP_BIT_XOR:
+ insn.op = FILTER_OP_BIT_XOR;
break;
case AST_OP_EQ:
return ret;
/* Cast to s64 if float or field ref */
if ((node->u.binary.left->data_type == IR_DATA_FIELD_REF
- || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF)
+ || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.left->data_type == IR_DATA_EXPRESSION)
|| node->u.binary.left->data_type == IR_DATA_FLOAT) {
struct cast_op cast_insn;
if (node->u.binary.left->data_type == IR_DATA_FIELD_REF
- || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF) {
+ || node->u.binary.left->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.left->data_type == IR_DATA_EXPRESSION) {
cast_insn.op = FILTER_OP_CAST_TO_S64;
} else {
cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;
return ret;
/* Cast to s64 if float or field ref */
if ((node->u.binary.right->data_type == IR_DATA_FIELD_REF
- || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF)
+ || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.right->data_type == IR_DATA_EXPRESSION)
|| node->u.binary.right->data_type == IR_DATA_FLOAT) {
struct cast_op cast_insn;
if (node->u.binary.right->data_type == IR_DATA_FIELD_REF
- || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF) {
+ || node->u.binary.right->data_type == IR_DATA_GET_CONTEXT_REF
+ || node->u.binary.right->data_type == IR_DATA_EXPRESSION) {
cast_insn.op = FILTER_OP_CAST_TO_S64;
} else {
cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64;