From: Jonathan Rajotte Date: Thu, 28 Nov 2019 21:09:16 +0000 (-0500) Subject: Move filter related code to libfilter under libcommon X-Git-Tag: v2.13.0-rc1~487 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=e4d2f27a74baf6942ac8fcafd5ea53e775ceceb3;p=lttng-tools.git Move filter related code to libfilter under libcommon Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I006dcac801ce1ec742de3a03f21a1c5f8b698298 --- diff --git a/.gitignore b/.gitignore index 78c99b781..49aa0d1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -61,11 +61,11 @@ compile_commands.json /src/bin/lttng-crash/lttng-crash /src/bin/lttng-relayd/lttng-relayd /src/lib/lttng-ctl/lttng-ctl.pc -/src/lib/lttng-ctl/filter/filter-grammar-test -/src/lib/lttng-ctl/filter/filter-lexer.c -/src/lib/lttng-ctl/filter/filter-parser.c -/src/lib/lttng-ctl/filter/filter-parser.h -/src/lib/lttng-ctl/filter/filter-parser.output +/src/common/filter/filter-grammar-test +/src/common/filter/filter-lexer.c +/src/common/filter/filter-parser.c +/src/common/filter/filter-parser.h +/src/common/filter/filter-parser.output /extras/bindings/swig/python/lttng.i /extras/bindings/swig/python/lttng.py diff --git a/configure.ac b/configure.ac index 4e186d471..c6b83b78a 100644 --- a/configure.ac +++ b/configure.ac @@ -1081,7 +1081,7 @@ AC_SUBST(AM_LDFLAGS) # In a scenario where lttng-tools is built from a distribution tarball and in a # out-of-tree manner, the generated "version.i" has priority on the one from # the source (distribution tarball) and must be found first. -AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS" +AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include config.h $AM_CPPFLAGS" AC_SUBST(AM_CPPFLAGS) lttngincludedir="${includedir}/lttng" @@ -1129,9 +1129,9 @@ AC_CONFIG_FILES([ src/common/config/Makefile src/common/string-utils/Makefile src/common/fd-tracker/Makefile + src/common/filter/Makefile src/lib/Makefile src/lib/lttng-ctl/Makefile - src/lib/lttng-ctl/filter/Makefile src/lib/lttng-ctl/lttng-ctl.pc src/bin/Makefile src/bin/lttng-consumerd/Makefile diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index d094c5aaf..a6bd7a9d7 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -37,4 +37,5 @@ lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ $(top_builddir)/src/common/libcommon.la \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/string-utils/libstring-utils.la \ + $(top_builddir)/src/common/filter/libfilter.la \ $(POPT_LIBS) diff --git a/src/common/Makefile.am b/src/common/Makefile.am index c855dcd7a..6a841e92a 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = subdir-objects -SUBDIRS = string-utils +SUBDIRS = string-utils filter # Make sure to always distribute all folders # since SUBDIRS is decided at configure time. @@ -20,8 +20,8 @@ DIST_SUBDIRS = \ config \ consumer \ string-utils \ - fd-tracker - + fd-tracker \ + filter # Common library noinst_LTLIBRARIES = libcommon.la EXTRA_DIST = mi-lttng-4.0.xsd @@ -87,7 +87,8 @@ libcommon_la_LIBADD = \ $(top_builddir)/src/common/config/libconfig.la \ $(top_builddir)/src/common/compat/libcompat.la \ $(top_builddir)/src/common/hashtable/libhashtable.la \ - $(top_builddir)/src/common/fd-tracker/libfd-tracker.la + $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \ + $(top_builddir)/src/common/filter/libfilter.la if BUILD_LIB_COMPAT SUBDIRS += compat diff --git a/src/common/filter/Makefile.am b/src/common/filter/Makefile.am new file mode 100644 index 000000000..cac4cb175 --- /dev/null +++ b/src/common/filter/Makefile.am @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only + +AM_CPPFLAGS += -I$(srcdir) -I$(builddir) + +noinst_PROGRAMS = filter-grammar-test +noinst_LTLIBRARIES = libfilter.la +noinst_HEADERS = filter-ast.h \ + filter-symbols.h + +BUILT_SOURCES = filter-parser.h + +libfilter_la_SOURCES = \ + filter-parser.y filter-lexer.l \ + filter-visitor-xml.c \ + filter-visitor-generate-ir.c \ + filter-visitor-ir-check-binary-op-nesting.c \ + filter-visitor-ir-validate-string.c \ + filter-visitor-ir-validate-globbing.c \ + filter-visitor-ir-normalize-glob-patterns.c \ + filter-visitor-generate-bytecode.c \ + filter-ast.h \ + filter-bytecode.h \ + filter-ir.h \ + memstream.h +libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS) +libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la + +AM_YFLAGS = -t -d -v -Wno-yacc + +# start with empty files to clean +CLEANFILES = + +if HAVE_BISON +# we have bison: we can clean the generated parser files +CLEANFILES += filter-parser.c filter-parser.h filter-parser.output +else # HAVE_BISON +# create target used to stop the build if we want to build the parser, +# but we don't have the necessary tool to do so +ERR_MSG = "Error: Cannot build target because bison is missing." +ERR_MSG += "Make sure bison is installed and run the configure script again." + +filter-parser.c filter-parser.h: filter-parser.y + @echo $(ERR_MSG) + @false + +all-local: filter-parser.c filter-parser.h +endif # HAVE_BISON + +if HAVE_FLEX +# we have flex: we can clean the generated lexer files +CLEANFILES += filter-lexer.c +else # HAVE_FLEX +# create target used to stop the build if we want to build the lexer, +# but we don't have the necessary tool to do so +ERR_MSG = "Error: Cannot build target because flex is missing." +ERR_MSG += "Make sure flex is installed and run the configure script again." + +filter-lexer.c: filter-lexer.l + @echo $(ERR_MSG) + @false + +all-local: filter-lexer.c +endif # HAVE_FLEX + +filter_grammar_test_SOURCES = filter-grammar-test.c +filter_grammar_test_LDADD = libfilter.la diff --git a/src/common/filter/filter-ast.h b/src/common/filter/filter-ast.h new file mode 100644 index 000000000..29fde10f8 --- /dev/null +++ b/src/common/filter/filter-ast.h @@ -0,0 +1,188 @@ +#ifndef _FILTER_AST_H +#define _FILTER_AST_H + +/* + * filter-ast.h + * + * LTTng filter AST + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +/* + * Note: filter-ast.h should be included before filter-parser.h. + */ + +#include +#include + +#define printf_debug(fmt, args...) \ + do { \ + if (filter_parser_debug) \ + fprintf(stdout, "[debug] " fmt, ## args); \ + } while (0) + +// the parameter name (of the reentrant 'yyparse' function) +// data is a pointer to a 'SParserParam' structure +//#define YYPARSE_PARAM parser_ctx + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +extern int filter_parser_debug; + +struct filter_node; +struct filter_parser; + +enum node_type { + NODE_UNKNOWN = 0, + NODE_ROOT, + + NODE_EXPRESSION, + NODE_OP, + NODE_UNARY_OP, + + NR_NODE_TYPES, +}; + +enum op_type { + AST_OP_UNKNOWN = 0, + AST_OP_MUL, + AST_OP_DIV, + AST_OP_MOD, + AST_OP_PLUS, + AST_OP_MINUS, + AST_OP_BIT_RSHIFT, + AST_OP_BIT_LSHIFT, + AST_OP_AND, + AST_OP_OR, + AST_OP_BIT_AND, + AST_OP_BIT_OR, + AST_OP_BIT_XOR, + + AST_OP_EQ, + AST_OP_NE, + AST_OP_GT, + AST_OP_LT, + AST_OP_GE, + AST_OP_LE, +}; + +enum unary_op_type { + AST_UNARY_UNKNOWN = 0, + AST_UNARY_PLUS, + AST_UNARY_MINUS, + AST_UNARY_NOT, + AST_UNARY_BIT_NOT, +}; + +enum ast_link_type { + AST_LINK_UNKNOWN = 0, + AST_LINK_DOT, + AST_LINK_RARROW, + AST_LINK_BRACKET, +}; + +struct filter_node { + /* + * Parent node is only set on demand by specific visitor. + */ + struct filter_node *parent; + struct cds_list_head gc; + + enum node_type type; + union { + struct { + } unknown; + struct { + struct filter_node *child; + } root; + struct { + enum { + AST_EXP_UNKNOWN = 0, + AST_EXP_STRING, + AST_EXP_CONSTANT, + AST_EXP_FLOAT_CONSTANT, + AST_EXP_IDENTIFIER, + AST_EXP_GLOBAL_IDENTIFIER, + AST_EXP_NESTED, + } type; + enum ast_link_type post_op; /* reverse */ + enum ast_link_type pre_op; /* forward */ + union { + const char *string; + uint64_t constant; + double float_constant; + const char *identifier; + /* + * child can be nested. + */ + struct filter_node *child; + } u; + /* prev: backward dot/arrow chain (postfix expression) */ + struct filter_node *prev; + /* next: forward dot/arrow chain, generated by a visitor. */ + struct filter_node *next; + /* next_bracket: linked bracket chain (prefix expression) */ + struct filter_node *next_bracket; + } expression; + struct { + enum op_type type; + struct filter_node *lchild; + struct filter_node *rchild; + } op; + struct { + enum unary_op_type type; + struct filter_node *child; + } unary_op; + } u; +}; + +struct filter_ast { + struct filter_node root; + struct cds_list_head allocated_nodes; +}; + +const char *node_type(struct filter_node *node); + +struct ir_op; + +struct filter_parser_ctx { + yyscan_t scanner; + struct filter_ast *ast; + struct cds_list_head allocated_strings; + struct ir_op *ir_root; + struct lttng_filter_bytecode_alloc *bytecode; + struct lttng_filter_bytecode_alloc *bytecode_reloc; +}; + +struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input); +void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx); +int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx); +int filter_parser_ctx_create_from_filter_expression( + const char *filter_expression, struct filter_parser_ctx **ctxp); + +static inline +struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx) +{ + return parser_ctx->ast; +} + +int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream, + int indent); +int filter_visitor_ir_generate(struct filter_parser_ctx *ctx); +void filter_ir_free(struct filter_parser_ctx *ctx); +int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx); +void filter_bytecode_free(struct filter_parser_ctx *ctx); +int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx); +int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx); +int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx); +int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx); +int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx); + +#endif /* _FILTER_AST_H */ diff --git a/src/common/filter/filter-bytecode.h b/src/common/filter/filter-bytecode.h new file mode 100644 index 000000000..053bb08bb --- /dev/null +++ b/src/common/filter/filter-bytecode.h @@ -0,0 +1,243 @@ +#ifndef _FILTER_BYTECODE_H +#define _FILTER_BYTECODE_H + +/* + * filter-bytecode.h + * + * LTTng filter bytecode + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include "filter-ast.h" + +/* + * offsets are absolute from start of bytecode. + */ + +struct field_ref { + /* Initially, symbol offset. After link, field offset. */ + uint16_t offset; +} LTTNG_PACKED; + +struct get_symbol { + /* Symbol offset. */ + uint16_t offset; +} LTTNG_PACKED; + +struct get_index_u16 { + uint16_t index; +} LTTNG_PACKED; + +struct get_index_u64 { + uint64_t index; +} LTTNG_PACKED; + +struct literal_numeric { + int64_t v; +} LTTNG_PACKED; + +struct literal_double { + double v; +} LTTNG_PACKED; + +struct literal_string { + char string[0]; +} LTTNG_PACKED; + +enum filter_op { + FILTER_OP_UNKNOWN = 0, + + FILTER_OP_RETURN = 1, + + /* binary */ + FILTER_OP_MUL = 2, + FILTER_OP_DIV = 3, + FILTER_OP_MOD = 4, + FILTER_OP_PLUS = 5, + FILTER_OP_MINUS = 6, + FILTER_OP_BIT_RSHIFT = 7, + FILTER_OP_BIT_LSHIFT = 8, + FILTER_OP_BIT_AND = 9, + FILTER_OP_BIT_OR = 10, + FILTER_OP_BIT_XOR = 11, + + /* binary comparators */ + FILTER_OP_EQ = 12, + FILTER_OP_NE = 13, + FILTER_OP_GT = 14, + FILTER_OP_LT = 15, + FILTER_OP_GE = 16, + FILTER_OP_LE = 17, + + /* string binary comparator: apply to */ + FILTER_OP_EQ_STRING = 18, + FILTER_OP_NE_STRING = 19, + FILTER_OP_GT_STRING = 20, + FILTER_OP_LT_STRING = 21, + FILTER_OP_GE_STRING = 22, + FILTER_OP_LE_STRING = 23, + + /* s64 binary comparator */ + FILTER_OP_EQ_S64 = 24, + FILTER_OP_NE_S64 = 25, + FILTER_OP_GT_S64 = 26, + FILTER_OP_LT_S64 = 27, + FILTER_OP_GE_S64 = 28, + FILTER_OP_LE_S64 = 29, + + /* double binary comparator */ + FILTER_OP_EQ_DOUBLE = 30, + FILTER_OP_NE_DOUBLE = 31, + FILTER_OP_GT_DOUBLE = 32, + FILTER_OP_LT_DOUBLE = 33, + FILTER_OP_GE_DOUBLE = 34, + FILTER_OP_LE_DOUBLE = 35, + + /* Mixed S64-double binary comparators */ + FILTER_OP_EQ_DOUBLE_S64 = 36, + FILTER_OP_NE_DOUBLE_S64 = 37, + FILTER_OP_GT_DOUBLE_S64 = 38, + FILTER_OP_LT_DOUBLE_S64 = 39, + FILTER_OP_GE_DOUBLE_S64 = 40, + FILTER_OP_LE_DOUBLE_S64 = 41, + + FILTER_OP_EQ_S64_DOUBLE = 42, + FILTER_OP_NE_S64_DOUBLE = 43, + FILTER_OP_GT_S64_DOUBLE = 44, + FILTER_OP_LT_S64_DOUBLE = 45, + FILTER_OP_GE_S64_DOUBLE = 46, + FILTER_OP_LE_S64_DOUBLE = 47, + + /* unary */ + FILTER_OP_UNARY_PLUS = 48, + FILTER_OP_UNARY_MINUS = 49, + FILTER_OP_UNARY_NOT = 50, + FILTER_OP_UNARY_PLUS_S64 = 51, + FILTER_OP_UNARY_MINUS_S64 = 52, + FILTER_OP_UNARY_NOT_S64 = 53, + FILTER_OP_UNARY_PLUS_DOUBLE = 54, + FILTER_OP_UNARY_MINUS_DOUBLE = 55, + FILTER_OP_UNARY_NOT_DOUBLE = 56, + + /* logical */ + FILTER_OP_AND = 57, + FILTER_OP_OR = 58, + + /* load field ref */ + FILTER_OP_LOAD_FIELD_REF = 59, + FILTER_OP_LOAD_FIELD_REF_STRING = 60, + FILTER_OP_LOAD_FIELD_REF_SEQUENCE = 61, + FILTER_OP_LOAD_FIELD_REF_S64 = 62, + FILTER_OP_LOAD_FIELD_REF_DOUBLE = 63, + + /* load immediate from operand */ + FILTER_OP_LOAD_STRING = 64, + FILTER_OP_LOAD_S64 = 65, + FILTER_OP_LOAD_DOUBLE = 66, + + /* cast */ + FILTER_OP_CAST_TO_S64 = 67, + FILTER_OP_CAST_DOUBLE_TO_S64 = 68, + FILTER_OP_CAST_NOP = 69, + + /* get context ref */ + FILTER_OP_GET_CONTEXT_REF = 70, + FILTER_OP_GET_CONTEXT_REF_STRING = 71, + FILTER_OP_GET_CONTEXT_REF_S64 = 72, + FILTER_OP_GET_CONTEXT_REF_DOUBLE = 73, + + /* load userspace field ref */ + FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74, + FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75, + + /* + * load immediate star globbing pattern (literal string) + * from immediate + */ + FILTER_OP_LOAD_STAR_GLOB_STRING = 76, + + /* globbing pattern binary operator: apply to */ + FILTER_OP_EQ_STAR_GLOB_STRING = 77, + FILTER_OP_NE_STAR_GLOB_STRING = 78, + + /* + * Instructions for recursive traversal through composed types. + */ + FILTER_OP_GET_CONTEXT_ROOT = 79, + FILTER_OP_GET_APP_CONTEXT_ROOT = 80, + FILTER_OP_GET_PAYLOAD_ROOT = 81, + + FILTER_OP_GET_SYMBOL = 82, + FILTER_OP_GET_SYMBOL_FIELD = 83, + FILTER_OP_GET_INDEX_U16 = 84, + FILTER_OP_GET_INDEX_U64 = 85, + + FILTER_OP_LOAD_FIELD = 86, + FILTER_OP_LOAD_FIELD_S8 = 87, + FILTER_OP_LOAD_FIELD_S16 = 88, + FILTER_OP_LOAD_FIELD_S32 = 89, + FILTER_OP_LOAD_FIELD_S64 = 90, + FILTER_OP_LOAD_FIELD_U8 = 91, + FILTER_OP_LOAD_FIELD_U16 = 92, + FILTER_OP_LOAD_FIELD_U32 = 93, + FILTER_OP_LOAD_FIELD_U64 = 94, + FILTER_OP_LOAD_FIELD_STRING = 95, + FILTER_OP_LOAD_FIELD_SEQUENCE = 96, + FILTER_OP_LOAD_FIELD_DOUBLE = 97, + + FILTER_OP_UNARY_BIT_NOT = 98, + + FILTER_OP_RETURN_S64 = 99, + + 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. */ +} LTTNG_PACKED; + +struct binary_op { + filter_opcode_t op; +} LTTNG_PACKED; + +struct unary_op { + filter_opcode_t op; +} LTTNG_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 */ +} LTTNG_PACKED; + +struct cast_op { + filter_opcode_t op; +} LTTNG_PACKED; + +struct return_op { + filter_opcode_t op; +} LTTNG_PACKED; + +struct lttng_filter_bytecode_alloc { + uint32_t alloc_len; + struct lttng_filter_bytecode b; +}; + +static inline +unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode) +{ + return bytecode->len; +} + +#endif /* _FILTER_BYTECODE_H */ diff --git a/src/common/filter/filter-grammar-test.c b/src/common/filter/filter-grammar-test.c new file mode 100644 index 000000000..70656e1a8 --- /dev/null +++ b/src/common/filter/filter-grammar-test.c @@ -0,0 +1,142 @@ +/* + * filter-grammar-test.c + * + * LTTng filter grammar test + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-bytecode.h" + +int main(int argc, char **argv) +{ + struct filter_parser_ctx *ctx; + int ret; + int print_xml = 0, generate_ir = 0, generate_bytecode = 0, + print_bytecode = 0; + int i; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-p") == 0) + print_xml = 1; + else if (strcmp(argv[i], "-i") == 0) + generate_ir = 1; + else if (strcmp(argv[i], "-b") == 0) + generate_bytecode = 1; + else if (strcmp(argv[i], "-d") == 0) + filter_parser_debug = 1; + else if (strcmp(argv[i], "-B") == 0) + print_bytecode = 1; + } + + /* + * Force generate the bytecode if the user asks to print the bytecode + * (can't print it without generating it first). + */ + if (print_bytecode) { + generate_bytecode = 1; + } + + /* + * Force generate the IR if the user asks to generate the bytecode + * (the bytecode is generated by visiting the IR). + */ + if (generate_bytecode) { + generate_ir = 1; + } + + ctx = filter_parser_ctx_alloc(stdin); + if (!ctx) { + fprintf(stderr, "Error allocating parser\n"); + goto alloc_error; + } + ret = filter_parser_ctx_append_ast(ctx); + if (ret) { + fprintf(stderr, "Parse error\n"); + goto parse_error; + } + if (print_xml) { + ret = filter_visitor_print_xml(ctx, stdout, 0); + if (ret) { + fflush(stdout); + fprintf(stderr, "XML print error\n"); + goto parse_error; + } + } + if (generate_ir) { + printf("Generating IR... "); + fflush(stdout); + ret = filter_visitor_ir_generate(ctx); + if (ret) { + fprintf(stderr, "Generate IR error\n"); + goto parse_error; + } + printf("done\n"); + + printf("Validating IR... "); + fflush(stdout); + ret = filter_visitor_ir_check_binary_op_nesting(ctx); + if (ret) { + goto parse_error; + } + printf("done\n"); + } + if (generate_bytecode) { + printf("Generating bytecode... "); + fflush(stdout); + ret = filter_visitor_bytecode_generate(ctx); + if (ret) { + fprintf(stderr, "Generate bytecode error\n"); + goto parse_error; + } + printf("done\n"); + printf("Size of bytecode generated: %u bytes.\n", + bytecode_get_len(&ctx->bytecode->b)); + } + + if (print_bytecode) { + unsigned int bytecode_len, len, i; + + len = bytecode_get_len(&ctx->bytecode->b); + bytecode_len = ctx->bytecode->b.reloc_table_offset; + printf("Bytecode:\n"); + for (i = 0; i < bytecode_len; i++) { + printf("0x%X ", + ((uint8_t *) ctx->bytecode->b.data)[i]); + } + printf("\n"); + printf("Reloc table:\n"); + for (i = bytecode_len; i < len;) { + printf("{ 0x%X, ", + *(uint16_t *) &ctx->bytecode->b.data[i]); + i += sizeof(uint16_t); + printf("%s } ", &((char *) ctx->bytecode->b.data)[i]); + i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1; + } + printf("\n"); + } + + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); + return 0; + +parse_error: + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); +alloc_error: + exit(EXIT_FAILURE); +} diff --git a/src/common/filter/filter-ir.h b/src/common/filter/filter-ir.h new file mode 100644 index 000000000..d62c0ee0c --- /dev/null +++ b/src/common/filter/filter-ir.h @@ -0,0 +1,133 @@ +#ifndef _FILTER_IR_H +#define _FILTER_IR_H + +/* + * filter-ir.h + * + * LTTng filter ir + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "filter-ast.h" + +enum ir_op_signedness { + IR_SIGN_UNKNOWN = 0, + IR_SIGNED, + IR_UNSIGNED, + IR_SIGN_DYN, /* signedness determined dynamically */ +}; + +enum ir_data_type { + IR_DATA_UNKNOWN = 0, + IR_DATA_STRING, + IR_DATA_NUMERIC, /* numeric and boolean */ + IR_DATA_FLOAT, + IR_DATA_FIELD_REF, + IR_DATA_GET_CONTEXT_REF, + IR_DATA_EXPRESSION, +}; + +enum ir_op_type { + IR_OP_UNKNOWN = 0, + IR_OP_ROOT, + IR_OP_LOAD, + IR_OP_UNARY, + IR_OP_BINARY, + IR_OP_LOGICAL, +}; + +/* left or right child */ +enum ir_side { + IR_SIDE_UNKNOWN = 0, + IR_LEFT, + IR_RIGHT, +}; + +enum ir_load_string_type { + /* Plain, no globbing at all: `hello world`. */ + IR_LOAD_STRING_TYPE_PLAIN = 0, + + /* Star at the end only: `hello *`. */ + IR_LOAD_STRING_TYPE_GLOB_STAR_END, + + /* At least one star, anywhere, but not at the end only: `he*wor*`. */ + IR_LOAD_STRING_TYPE_GLOB_STAR, +}; + +struct ir_op_root { + struct ir_op *child; +}; + +enum ir_load_expression_type { + IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT, + IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT, + IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT, + IR_LOAD_EXPRESSION_GET_SYMBOL, + IR_LOAD_EXPRESSION_GET_INDEX, + IR_LOAD_EXPRESSION_LOAD_FIELD, +}; + +struct ir_load_expression_op { + struct ir_load_expression_op *next; + enum ir_load_expression_type type; + union { + char *symbol; + uint64_t index; + } u; +}; + +struct ir_load_expression { + struct ir_load_expression_op *child; +}; + +struct ir_op_load { + union { + struct { + enum ir_load_string_type type; + char *value; + } string; + int64_t num; + double flt; + char *ref; + struct ir_load_expression *expression; + } u; +}; + +struct ir_op_unary { + enum unary_op_type type; + struct ir_op *child; +}; + +struct ir_op_binary { + enum op_type type; + struct ir_op *left; + struct ir_op *right; +}; + +struct ir_op_logical { + enum op_type type; + struct ir_op *left; + struct ir_op *right; +}; + +struct ir_op { + /* common to all ops */ + enum ir_op_type op; + enum ir_data_type data_type; + enum ir_op_signedness signedness; + enum ir_side side; + + union { + struct ir_op_root root; + struct ir_op_load load; + struct ir_op_unary unary; + struct ir_op_binary binary; + struct ir_op_logical logical; + } u; +}; + +#endif /* _FILTER_IR_H */ diff --git a/src/common/filter/filter-lexer.l b/src/common/filter/filter-lexer.l new file mode 100644 index 000000000..cdb099af7 --- /dev/null +++ b/src/common/filter/filter-lexer.l @@ -0,0 +1,129 @@ +%{ +/* + * filter-lexer.l + * + * LTTng filter lexer + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include "filter-ast.h" +#include "filter-parser.h" + +static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) + __attribute__((unused)); +static int input (yyscan_t yyscanner) __attribute__((unused)); + +%} + +%x comment_ml comment_sl string_lit char_const +%option reentrant yylineno noyywrap bison-bridge +%option extra-type="struct filter_parser_ctx *" + /* bison-locations */ + +D [0-9] +L [a-zA-Z_] +H [a-fA-F0-9] +E ([Ee][+-]?{D}+) +P ([Pp][+-]?{D}+) +FS (f|F|l|L) +IS ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U)) + +INTEGER_SUFFIX [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu) +DIGIT [0-9] +NONDIGIT [a-zA-Z_] +HEXDIGIT [0-9A-Fa-f] +OCTALDIGIT [0-7] +UCHARLOWERCASE \\u{HEXDIGIT}{4} +UCHARUPPERCASE \\U{HEXDIGIT}{8} +ID_EXTRA_CHAR (":") +ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR} +IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})* +ESCSEQ \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+) +%% + + /* + * Using start conditions to deal with comments + * and strings. + */ + +"/*" BEGIN(comment_ml); +[^*\n]* /* eat anything that's not a '*' */ +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +\n ++yylineno; +"*"+"/" BEGIN(INITIAL); + +"//" BEGIN(comment_sl); +[^\n]*\n ++yylineno; BEGIN(INITIAL); + +L\' BEGIN(char_const); return CHARACTER_CONSTANT_START; +\' BEGIN(char_const); return CHARACTER_CONSTANT_START; +\' BEGIN(INITIAL); return SQUOTE; + +L\" BEGIN(string_lit); return STRING_LITERAL_START; +\" BEGIN(string_lit); return STRING_LITERAL_START; +\" BEGIN(INITIAL); return DQUOTE; + +ESCSEQ return ESCSEQ; +\n ; /* ignore */ +. setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN; + + +0[xX]{H}+{IS}? setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT; +0[0-7]*{IS}? setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT; +[1-9]{D}*{IS}? setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT; + +{D}+{E}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; +{D}*"."{D}+{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; +{D}+"."{D}*{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; +0[xX]{H}+{P}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; +0[xX]{H}*"."{H}+{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; +0[xX]{H}+"."{H}*{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; + +"[" return LSBRAC; +"]" return RSBRAC; +"(" return LPAREN; +")" return RPAREN; +"{" return LBRAC; +"}" return RBRAC; +"->" return RARROW; + +"*" return STAR; +"+" return PLUS; +"-" return MINUS; + +"%" return MOD_OP; +"/" return DIV_OP; +">>" return RIGHT_OP; +"<<" return LEFT_OP; + +"==" return EQ_OP; +"!=" return NE_OP; +"<=" return LE_OP; +">=" return GE_OP; +"<" return LT_OP; +">" return GT_OP; +"&&" return AND_OP; +"||" return OR_OP; +"!" return NOT_OP; + +":=" return ASSIGN; +":" return COLON; +";" return SEMICOLON; +"..." return DOTDOTDOT; +"." return DOT; +"=" return EQUAL; +"," return COMMA; +"^" return XOR_BIN; +"&" return AND_BIN; +"|" return OR_BIN; +"~" return NOT_BIN; +"$"{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER; +{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER; +[ \t\n]+ ; /* ignore */ +. return ERROR; +%% diff --git a/src/common/filter/filter-parser.y b/src/common/filter/filter-parser.y new file mode 100644 index 000000000..245d83c26 --- /dev/null +++ b/src/common/filter/filter-parser.y @@ -0,0 +1,812 @@ +%{ +/* + * filter-parser.y + * + * LTTng filter expression parser + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-bytecode.h" +#include "memstream.h" + +#include + +#define WIDTH_u64_SCANF_IS_A_BROKEN_API "20" +#define WIDTH_o64_SCANF_IS_A_BROKEN_API "22" +#define WIDTH_x64_SCANF_IS_A_BROKEN_API "17" +#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096" /* Hugely optimistic approximation */ + +#ifdef DEBUG +static const int print_xml = 1; +#define dbg_printf(fmt, args...) \ + printf("[debug filter_parser] " fmt, ## args) +#else +static const int print_xml = 0; +#define dbg_printf(fmt, args...) \ +do { \ + /* do nothing but check printf format */ \ + if (0) \ + printf("[debug filter_parser] " fmt, ## args); \ +} while (0) +#endif + +LTTNG_HIDDEN +int yydebug; +LTTNG_HIDDEN +int filter_parser_debug = 0; + +LTTNG_HIDDEN +int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner); +LTTNG_HIDDEN +int yylex(union YYSTYPE *yyval, yyscan_t scanner); +LTTNG_HIDDEN +int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals); +LTTNG_HIDDEN +int yylex_destroy(yyscan_t yyparser_ctx); +LTTNG_HIDDEN +void yyrestart(FILE * in_str, yyscan_t parser_ctx); + +struct gc_string { + struct cds_list_head gc; + size_t alloclen; + char s[]; +}; + +static const char *node_type_to_str[] = { + [ NODE_UNKNOWN ] = "NODE_UNKNOWN", + [ NODE_ROOT ] = "NODE_ROOT", + [ NODE_EXPRESSION ] = "NODE_EXPRESSION", + [ NODE_OP ] = "NODE_OP", + [ NODE_UNARY_OP ] = "NODE_UNARY_OP", +}; + +LTTNG_HIDDEN +const char *node_type(struct filter_node *node) +{ + if (node->type < NR_NODE_TYPES) + return node_type_to_str[node->type]; + else + return NULL; +} + +static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx, + size_t len) +{ + struct gc_string *gstr; + size_t alloclen; + + /* TODO: could be faster with find first bit or glib Gstring */ + /* sizeof long to account for malloc header (int or long ?) */ + for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len; + alloclen *= 2); + + gstr = zmalloc(alloclen); + if (!gstr) { + goto end; + } + cds_list_add(&gstr->gc, &parser_ctx->allocated_strings); + gstr->alloclen = alloclen; +end: + return gstr; +} + +/* + * note: never use gc_string_append on a string that has external references. + * gsrc will be garbage collected immediately, and gstr might be. + * Should only be used to append characters to a string literal or constant. + */ +static +struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx, + struct gc_string *gstr, + struct gc_string *gsrc) +{ + size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1; + size_t alloclen; + + /* TODO: could be faster with find first bit or glib Gstring */ + /* sizeof long to account for malloc header (int or long ?) */ + for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen; + alloclen *= 2); + + if (alloclen > gstr->alloclen) { + struct gc_string *newgstr; + + newgstr = gc_string_alloc(parser_ctx, newlen); + strcpy(newgstr->s, gstr->s); + strcat(newgstr->s, gsrc->s); + cds_list_del(&gstr->gc); + free(gstr); + gstr = newgstr; + } else { + strcat(gstr->s, gsrc->s); + } + cds_list_del(&gsrc->gc); + free(gsrc); + return gstr; +} + +LTTNG_HIDDEN +void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src) +{ + lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1); + strcpy(lvalp->gs->s, src); +} + +static struct filter_node *make_node(struct filter_parser_ctx *scanner, + enum node_type type) +{ + struct filter_ast *ast = filter_parser_get_ast(scanner); + struct filter_node *node; + + node = zmalloc(sizeof(*node)); + if (!node) + return NULL; + memset(node, 0, sizeof(*node)); + node->type = type; + cds_list_add(&node->gc, &ast->allocated_nodes); + + switch (type) { + case NODE_ROOT: + fprintf(stderr, "[error] %s: trying to create root node\n", __func__); + break; + + case NODE_EXPRESSION: + break; + case NODE_OP: + break; + case NODE_UNARY_OP: + break; + + case NODE_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown node type %d\n", __func__, + (int) type); + break; + } + + return node; +} + +static struct filter_node *make_op_node(struct filter_parser_ctx *scanner, + enum op_type type, + struct filter_node *lchild, + struct filter_node *rchild) +{ + struct filter_ast *ast = filter_parser_get_ast(scanner); + struct filter_node *node; + + node = zmalloc(sizeof(*node)); + if (!node) + return NULL; + memset(node, 0, sizeof(*node)); + node->type = NODE_OP; + cds_list_add(&node->gc, &ast->allocated_nodes); + node->u.op.type = type; + node->u.op.lchild = lchild; + node->u.op.rchild = rchild; + return node; +} + +static +void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str) +{ + fprintf(stderr, "error %s\n", str); +} + +#define parse_error(parser_ctx, str) \ +do { \ + yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n")); \ + YYERROR; \ +} while (0) + +static void free_strings(struct cds_list_head *list) +{ + struct gc_string *gstr, *tmp; + + cds_list_for_each_entry_safe(gstr, tmp, list, gc) + free(gstr); +} + +static struct filter_ast *filter_ast_alloc(void) +{ + struct filter_ast *ast; + + ast = zmalloc(sizeof(*ast)); + if (!ast) + return NULL; + memset(ast, 0, sizeof(*ast)); + CDS_INIT_LIST_HEAD(&ast->allocated_nodes); + ast->root.type = NODE_ROOT; + return ast; +} + +static void filter_ast_free(struct filter_ast *ast) +{ + struct filter_node *node, *tmp; + + cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc) + free(node); + free(ast); +} + +LTTNG_HIDDEN +int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx) +{ + return yyparse(parser_ctx, parser_ctx->scanner); +} + +LTTNG_HIDDEN +struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input) +{ + struct filter_parser_ctx *parser_ctx; + int ret; + + yydebug = filter_parser_debug; + + parser_ctx = zmalloc(sizeof(*parser_ctx)); + if (!parser_ctx) + return NULL; + memset(parser_ctx, 0, sizeof(*parser_ctx)); + + ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner); + if (ret) { + fprintf(stderr, "yylex_init error\n"); + goto cleanup_parser_ctx; + } + /* Start processing new stream */ + yyrestart(input, parser_ctx->scanner); + + parser_ctx->ast = filter_ast_alloc(); + if (!parser_ctx->ast) + goto cleanup_lexer; + CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings); + + if (yydebug) + fprintf(stdout, "parser_ctx input is a%s.\n", + isatty(fileno(input)) ? "n interactive tty" : + " noninteractive file"); + + return parser_ctx; + +cleanup_lexer: + ret = yylex_destroy(parser_ctx->scanner); + if (!ret) + fprintf(stderr, "yylex_destroy error\n"); +cleanup_parser_ctx: + free(parser_ctx); + return NULL; +} + +LTTNG_HIDDEN +void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx) +{ + int ret; + + free_strings(&parser_ctx->allocated_strings); + filter_ast_free(parser_ctx->ast); + ret = yylex_destroy(parser_ctx->scanner); + if (ret) + fprintf(stderr, "yylex_destroy error\n"); + free(parser_ctx); +} + +LTTNG_HIDDEN +int filter_parser_ctx_create_from_filter_expression( + const char *filter_expression, struct filter_parser_ctx **ctxp) +{ + int ret; + struct filter_parser_ctx *ctx = NULL; + FILE *fmem = NULL; + + assert(filter_expression); + assert(ctxp); + + /* + * Casting const to non-const, as the underlying function will use it in + * read-only mode. + */ + fmem = lttng_fmemopen((void *) filter_expression, + strlen(filter_expression), "r"); + if (!fmem) { + fprintf(stderr, "Error opening memory as stream\n"); + ret = -LTTNG_ERR_FILTER_NOMEM; + goto error; + } + ctx = filter_parser_ctx_alloc(fmem); + if (!ctx) { + fprintf(stderr, "Error allocating parser\n"); + ret = -LTTNG_ERR_FILTER_NOMEM; + goto filter_alloc_error; + } + ret = filter_parser_ctx_append_ast(ctx); + if (ret) { + fprintf(stderr, "Parse error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + if (print_xml) { + ret = filter_visitor_print_xml(ctx, stdout, 0); + if (ret) { + fflush(stdout); + fprintf(stderr, "XML print error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + } + + dbg_printf("Generating IR... "); + fflush(stdout); + ret = filter_visitor_ir_generate(ctx); + if (ret) { + fprintf(stderr, "Generate IR error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + dbg_printf("done\n"); + + dbg_printf("Validating IR... "); + fflush(stdout); + ret = filter_visitor_ir_check_binary_op_nesting(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Normalize globbing patterns in the expression. */ + ret = filter_visitor_ir_normalize_glob_patterns(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Validate strings used as literals in the expression. */ + ret = filter_visitor_ir_validate_string(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + /* Validate globbing patterns in the expression. */ + ret = filter_visitor_ir_validate_globbing(ctx); + if (ret) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + + dbg_printf("done\n"); + + dbg_printf("Generating bytecode... "); + fflush(stdout); + ret = filter_visitor_bytecode_generate(ctx); + if (ret) { + fprintf(stderr, "Generate bytecode error\n"); + ret = -LTTNG_ERR_FILTER_INVAL; + goto parse_error; + } + dbg_printf("done\n"); + dbg_printf("Size of bytecode generated: %u bytes.\n", + bytecode_get_len(&ctx->bytecode->b)); + + /* No need to keep the memory stream. */ + if (fclose(fmem) != 0) { + fprintf(stderr, "fclose (%d) \n", errno); + ret = -LTTNG_ERR_FILTER_INVAL; + } + + *ctxp = ctx; + return 0; + +parse_error: + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); +filter_alloc_error: + if (fclose(fmem) != 0) { + fprintf(stderr, "fclose (%d) \n", errno); + } +error: + return ret; +} + +%} + +%code provides +{ +#include "common/macros.h" + +LTTNG_HIDDEN +void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src); +} + +%define api.pure + /* %locations */ +%parse-param {struct filter_parser_ctx *parser_ctx} +%parse-param {yyscan_t scanner} +%lex-param {yyscan_t scanner} +%start translation_unit +%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE +%token ESCSEQ CHAR_STRING_TOKEN +%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT +%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW +%token STAR PLUS MINUS +%token MOD_OP DIV_OP RIGHT_OP LEFT_OP +%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP +%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA +%token XOR_BIN AND_BIN OR_BIN NOT_BIN + +%token IDENTIFIER GLOBAL_IDENTIFIER +%token ERROR +%union +{ + long long ll; + char c; + struct gc_string *gs; + struct filter_node *n; +} + +%type s_char s_char_sequence c_char c_char_sequence + +%type primary_expression +%type prefix_expression +%type prefix_expression_rec +%type postfix_expression +%type unary_expression +%type unary_operator +%type multiplicative_expression +%type additive_expression +%type shift_expression +%type relational_expression +%type equality_expression +%type and_expression +%type exclusive_or_expression +%type inclusive_or_expression +%type logical_and_expression +%type logical_or_expression +%type expression +%type identifiers + +%% + + +/* 1.5 Constants */ + +c_char_sequence: + c_char + { $$ = $1; } + | c_char_sequence c_char + { $$ = gc_string_append(parser_ctx, $1, $2); } + ; + +c_char: + CHAR_STRING_TOKEN + { $$ = yylval.gs; } + | ESCSEQ + { + parse_error(parser_ctx, "escape sequences not supported yet"); + } + ; + +/* 1.6 String literals */ + +s_char_sequence: + s_char + { $$ = $1; } + | s_char_sequence s_char + { $$ = gc_string_append(parser_ctx, $1, $2); } + ; + +s_char: + CHAR_STRING_TOKEN + { $$ = yylval.gs; } + | ESCSEQ + { + parse_error(parser_ctx, "escape sequences not supported yet"); + } + ; + +primary_expression: + DECIMAL_CONSTANT + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_CONSTANT; + if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64, + &$$->u.expression.u.constant) != 1) { + parse_error(parser_ctx, "cannot scanf decimal constant"); + } + } + | OCTAL_CONSTANT + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_CONSTANT; + if (!strcmp(yylval.gs->s, "0")) { + $$->u.expression.u.constant = 0; + } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64, + &$$->u.expression.u.constant) != 1) { + parse_error(parser_ctx, "cannot scanf octal constant"); + } + } + | HEXADECIMAL_CONSTANT + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_CONSTANT; + if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64, + &$$->u.expression.u.constant) != 1) { + parse_error(parser_ctx, "cannot scanf hexadecimal constant"); + } + } + | FLOAT_CONSTANT + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_FLOAT_CONSTANT; + if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg", + &$$->u.expression.u.float_constant) != 1) { + parse_error(parser_ctx, "cannot scanf float constant"); + } + } + | STRING_LITERAL_START DQUOTE + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_STRING; + $$->u.expression.u.string = ""; + } + | STRING_LITERAL_START s_char_sequence DQUOTE + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_STRING; + $$->u.expression.u.string = $2->s; + } + | CHARACTER_CONSTANT_START c_char_sequence SQUOTE + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_STRING; + $$->u.expression.u.string = $2->s; + } + | LPAREN expression RPAREN + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_NESTED; + $$->u.expression.u.child = $2; + } + ; + +identifiers + : IDENTIFIER + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_IDENTIFIER; + $$->u.expression.u.identifier = yylval.gs->s; + } + | GLOBAL_IDENTIFIER + { + $$ = make_node(parser_ctx, NODE_EXPRESSION); + $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER; + $$->u.expression.u.identifier = yylval.gs->s; + } + ; + +prefix_expression_rec + : LSBRAC unary_expression RSBRAC + { + $$ = $2; + } + | LSBRAC unary_expression RSBRAC prefix_expression_rec + { + $$ = $2; + $$->u.expression.pre_op = AST_LINK_BRACKET; + $$->u.expression.prev = $4; + } + ; + +prefix_expression + : identifiers + { + $$ = $1; + } + | identifiers prefix_expression_rec + { + $$ = $1; + $$->u.expression.pre_op = AST_LINK_BRACKET; + $$->u.expression.next_bracket = $2; + } + ; + +postfix_expression + : prefix_expression + { + $$ = $1; + } + | postfix_expression DOT prefix_expression + { + $$ = $3; + $$->u.expression.post_op = AST_LINK_DOT; + $$->u.expression.prev = $1; + } + | postfix_expression RARROW prefix_expression + { + $$ = $3; + $$->u.expression.post_op = AST_LINK_RARROW; + $$->u.expression.prev = $1; + } + ; + +unary_expression + : postfix_expression + { $$ = $1; } + | primary_expression + { $$ = $1; } + | unary_operator unary_expression + { + $$ = $1; + $$->u.unary_op.child = $2; + } + ; + +unary_operator + : PLUS + { + $$ = make_node(parser_ctx, NODE_UNARY_OP); + $$->u.unary_op.type = AST_UNARY_PLUS; + } + | MINUS + { + $$ = make_node(parser_ctx, NODE_UNARY_OP); + $$->u.unary_op.type = AST_UNARY_MINUS; + } + | NOT_OP + { + $$ = make_node(parser_ctx, NODE_UNARY_OP); + $$->u.unary_op.type = AST_UNARY_NOT; + } + | NOT_BIN + { + $$ = make_node(parser_ctx, NODE_UNARY_OP); + $$->u.unary_op.type = AST_UNARY_BIT_NOT; + } + ; + +multiplicative_expression + : unary_expression + { $$ = $1; } + | multiplicative_expression STAR unary_expression + { + $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3); + } + | multiplicative_expression DIV_OP unary_expression + { + $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3); + } + | multiplicative_expression MOD_OP unary_expression + { + $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3); + } + ; + +additive_expression + : multiplicative_expression + { $$ = $1; } + | additive_expression PLUS multiplicative_expression + { + $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3); + } + | additive_expression MINUS multiplicative_expression + { + $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3); + } + ; + +shift_expression + : additive_expression + { $$ = $1; } + | shift_expression LEFT_OP additive_expression + { + $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3); + } + | shift_expression RIGHT_OP additive_expression + { + $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3); + } + ; + +and_expression + : shift_expression + { $$ = $1; } + | and_expression AND_BIN shift_expression + { + $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3); + } + ; + +exclusive_or_expression + : and_expression + { $$ = $1; } + | exclusive_or_expression XOR_BIN and_expression + { + $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3); + } + ; + +inclusive_or_expression + : exclusive_or_expression + { $$ = $1; } + | inclusive_or_expression OR_BIN exclusive_or_expression + { + $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3); + } + ; + +relational_expression + : inclusive_or_expression + { $$ = $1; } + | relational_expression LT_OP inclusive_or_expression + { + $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3); + } + | relational_expression GT_OP inclusive_or_expression + { + $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3); + } + | relational_expression LE_OP inclusive_or_expression + { + $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3); + } + | relational_expression GE_OP inclusive_or_expression + { + $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3); + } + ; + +equality_expression + : relational_expression + { $$ = $1; } + | equality_expression EQ_OP relational_expression + { + $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3); + } + | equality_expression NE_OP relational_expression + { + $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3); + } + ; + +logical_and_expression + : equality_expression + { $$ = $1; } + | logical_and_expression AND_OP equality_expression + { + $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3); + } + ; + +logical_or_expression + : logical_and_expression + { $$ = $1; } + | logical_or_expression OR_OP logical_and_expression + { + $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3); + } + ; + +expression + : logical_or_expression + { $$ = $1; } + ; + +translation_unit + : expression + { + parser_ctx->ast->root.u.root.child = $1; + } + ; diff --git a/src/common/filter/filter-symbols.h b/src/common/filter/filter-symbols.h new file mode 100644 index 000000000..031776da0 --- /dev/null +++ b/src/common/filter/filter-symbols.h @@ -0,0 +1,45 @@ +#ifndef _FILTER_SYMBOLS_H +#define _FILTER_SYMBOLS_H + +/* + * filter-symbols.h + * + * LTTng filter flex/bison symbol prefixes + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define yy_create_buffer lttng_yy_create_buffer +#define yy_delete_buffer lttng_yy_delete_buffer +#define yy_flush_buffer lttng_yy_flush_buffer +#define yy_scan_buffer lttng_yy_scan_buffer +#define yy_scan_bytes lttng_yy_scan_bytes +#define yy_scan_string lttng_yy_scan_string +#define yy_switch_to_buffer lttng_yy_switch_to_buffer +#define yyalloc lttng_yyalloc +#define yyfree lttng_yyfree +#define yyget_column lttng_yyget_column +#define yyget_debug lttng_yyget_debug +#define yyget_extra lttng_yyget_extra +#define yyget_in lttng_yyget_in +#define yyget_leng lttng_yyget_leng +#define yyget_lineno lttng_yyget_lineno +#define yyget_lval lttng_yyget_lval +#define yyget_out lttng_yyget_out +#define yyget_text lttng_yyget_text +#define yylex_init lttng_yylex_init +#define yypop_buffer_state lttng_yypop_buffer_state +#define yypush_buffer_state lttng_yypush_buffer_state +#define yyrealloc lttng_yyrealloc +#define yyset_column lttng_yyset_column +#define yyset_debug lttng_yyset_debug +#define yyset_extra lttng_yyset_extra +#define yyset_in lttng_yyset_in +#define yyset_lineno lttng_yyset_lineno +#define yyset_lval lttng_yyset_lval +#define yyset_out lttng_yyset_out + +#endif /* _FILTER_SYMBOLS_H */ diff --git a/src/common/filter/filter-visitor-generate-bytecode.c b/src/common/filter/filter-visitor-generate-bytecode.c new file mode 100644 index 000000000..699273c3d --- /dev/null +++ b/src/common/filter/filter-visitor-generate-bytecode.c @@ -0,0 +1,830 @@ +/* + * filter-visitor-generate-bytecode.c + * + * LTTng filter bytecode generation + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include + +#include "filter-bytecode.h" +#include "filter-ir.h" +#include "filter-ast.h" + +#include + +#ifndef max_t +#define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b))) +#endif + +#define INIT_ALLOC_SIZE 4 + +static +int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx, + struct ir_op *node); + +static inline int get_count_order(unsigned int count) +{ + int order; + + order = lttng_fls(count) - 1; + if (count & (count - 1)) + order++; + return order; +} + +static +int bytecode_init(struct lttng_filter_bytecode_alloc **fb) +{ + uint32_t alloc_len; + + alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE; + *fb = calloc(alloc_len, 1); + if (!*fb) { + return -ENOMEM; + } else { + (*fb)->alloc_len = alloc_len; + return 0; + } +} + +static +int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len) +{ + int32_t ret; + uint32_t padding = offset_align((*fb)->b.len, align); + uint32_t new_len = (*fb)->b.len + padding + len; + uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len; + uint32_t old_alloc_len = (*fb)->alloc_len; + + if (new_len > LTTNG_FILTER_MAX_LEN) + return -EINVAL; + + if (new_alloc_len > old_alloc_len) { + struct lttng_filter_bytecode_alloc *newptr; + + new_alloc_len = + max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); + newptr = realloc(*fb, new_alloc_len); + if (!newptr) + return -ENOMEM; + *fb = newptr; + /* We zero directly the memory from start of allocation. */ + memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len); + (*fb)->alloc_len = new_alloc_len; + } + (*fb)->b.len += padding; + ret = (*fb)->b.len; + (*fb)->b.len += len; + return ret; +} + +static +int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data, + uint32_t align, uint32_t len) +{ + int32_t offset; + + offset = bytecode_reserve(fb, align, len); + if (offset < 0) + return offset; + memcpy(&(*fb)->b.data[offset], data, len); + return 0; +} + +static +int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb, + struct logical_op *data, + uint32_t align, uint32_t len, + uint16_t *skip_offset) +{ + int32_t offset; + + offset = bytecode_reserve(fb, align, len); + if (offset < 0) + return offset; + memcpy(&(*fb)->b.data[offset], data, len); + *skip_offset = + (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset + - (void *) &(*fb)->b.data[0]; + return 0; +} + +static +int bytecode_patch(struct lttng_filter_bytecode_alloc **fb, + const void *data, + uint16_t offset, + uint32_t len) +{ + if (offset >= (*fb)->b.len) { + return -EINVAL; + } + memcpy(&(*fb)->b.data[offset], data, len); + return 0; +} + +static +int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node) +{ + int ret; + struct return_op insn; + + /* Visit child */ + ret = recursive_visit_gen_bytecode(ctx, node->u.root.child); + if (ret) + return ret; + + /* Generate end of bytecode instruction */ + insn.op = FILTER_OP_RETURN; + 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) +{ + int ret; + + switch (node->data_type) { + case IR_DATA_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown data type in %s\n", + __func__); + return -EINVAL; + + case IR_DATA_STRING: + { + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op) + + strlen(node->u.load.u.string.value) + 1; + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + + 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; + } + case IR_DATA_NUMERIC: + { + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op) + + sizeof(struct literal_numeric); + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + insn->op = FILTER_OP_LOAD_S64; + memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t)); + ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); + free(insn); + return ret; + } + case IR_DATA_FLOAT: + { + struct load_op *insn; + uint32_t insn_len = sizeof(struct load_op) + + sizeof(struct literal_double); + + insn = calloc(insn_len, 1); + if (!insn) + return -ENOMEM; + insn->op = FILTER_OP_LOAD_DOUBLE; + memcpy(insn->data, &node->u.load.u.flt, sizeof(double)); + ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); + free(insn); + return ret; + } + case IR_DATA_EXPRESSION: + return visit_node_load_expression(ctx, node); + } +} + +static +int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node) +{ + int ret; + struct unary_op insn; + + /* Visit child */ + ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child); + if (ret) + return ret; + + /* Generate end of bytecode instruction */ + switch (node->u.unary.type) { + case AST_UNARY_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown unary node type in %s\n", + __func__); + return -EINVAL; + case AST_UNARY_PLUS: + /* Nothing to do. */ + return 0; + case AST_UNARY_MINUS: + insn.op = FILTER_OP_UNARY_MINUS; + return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); + 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)); + } +} + +/* + * Binary comparator nesting is disallowed. This allows fitting into + * only 2 registers. + */ +static +int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node) +{ + int ret; + struct binary_op insn; + + /* Visit child */ + ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left); + if (ret) + return ret; + ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right); + if (ret) + return ret; + + switch (node->u.binary.type) { + case AST_OP_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown unary node type in %s\n", + __func__); + return -EINVAL; + + case AST_OP_AND: + case AST_OP_OR: + fprintf(stderr, "[error] Unexpected logical node type in %s\n", + __func__); + return -EINVAL; + + case AST_OP_MUL: + insn.op = FILTER_OP_MUL; + break; + case AST_OP_DIV: + insn.op = FILTER_OP_DIV; + break; + case AST_OP_MOD: + insn.op = FILTER_OP_MOD; + break; + case AST_OP_PLUS: + insn.op = FILTER_OP_PLUS; + break; + case AST_OP_MINUS: + insn.op = FILTER_OP_MINUS; + break; + case AST_OP_BIT_RSHIFT: + insn.op = FILTER_OP_BIT_RSHIFT; + break; + case AST_OP_BIT_LSHIFT: + insn.op = FILTER_OP_BIT_LSHIFT; + break; + case AST_OP_BIT_AND: + insn.op = FILTER_OP_BIT_AND; + break; + case AST_OP_BIT_OR: + insn.op = FILTER_OP_BIT_OR; + break; + case AST_OP_BIT_XOR: + insn.op = FILTER_OP_BIT_XOR; + break; + + case AST_OP_EQ: + insn.op = FILTER_OP_EQ; + break; + case AST_OP_NE: + insn.op = FILTER_OP_NE; + break; + case AST_OP_GT: + insn.op = FILTER_OP_GT; + break; + case AST_OP_LT: + insn.op = FILTER_OP_LT; + break; + case AST_OP_GE: + insn.op = FILTER_OP_GE; + break; + case AST_OP_LE: + insn.op = FILTER_OP_LE; + break; + } + return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); +} + +/* + * A logical op always return a s64 (1 or 0). + */ +static +int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node) +{ + int ret; + struct logical_op insn; + uint16_t skip_offset_loc; + uint16_t target_loc; + + /* Visit left child */ + ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left); + if (ret) + 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_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_EXPRESSION) { + cast_insn.op = FILTER_OP_CAST_TO_S64; + } else { + cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; + } + ret = bytecode_push(&ctx->bytecode, &cast_insn, + 1, sizeof(cast_insn)); + if (ret) + return ret; + } + switch (node->u.logical.type) { + default: + fprintf(stderr, "[error] Unknown node type in %s\n", + __func__); + return -EINVAL; + + case AST_OP_AND: + insn.op = FILTER_OP_AND; + break; + case AST_OP_OR: + insn.op = FILTER_OP_OR; + break; + } + insn.skip_offset = (uint16_t) -1UL; /* Temporary */ + ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn), + &skip_offset_loc); + if (ret) + return ret; + /* Visit right child */ + ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right); + if (ret) + 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_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_EXPRESSION) { + cast_insn.op = FILTER_OP_CAST_TO_S64; + } else { + cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; + } + ret = bytecode_push(&ctx->bytecode, &cast_insn, + 1, sizeof(cast_insn)); + if (ret) + return ret; + } + /* We now know where the logical op can skip. */ + target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b); + ret = bytecode_patch(&ctx->bytecode, + &target_loc, /* Offset to jump to */ + skip_offset_loc, /* Where to patch */ + sizeof(uint16_t)); + return ret; +} + +/* + * Postorder traversal of the tree. We need the children result before + * we can evaluate the parent. + */ +static +int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx, + struct ir_op *node) +{ + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown node type in %s\n", + __func__); + return -EINVAL; + + case IR_OP_ROOT: + return visit_node_root(ctx, node); + case IR_OP_LOAD: + return visit_node_load(ctx, node); + case IR_OP_UNARY: + return visit_node_unary(ctx, node); + case IR_OP_BINARY: + return visit_node_binary(ctx, node); + case IR_OP_LOGICAL: + return visit_node_logical(ctx, node); + } +} + +LTTNG_HIDDEN +void filter_bytecode_free(struct filter_parser_ctx *ctx) +{ + if (!ctx) { + return; + } + + if (ctx->bytecode) { + free(ctx->bytecode); + ctx->bytecode = NULL; + } + + if (ctx->bytecode_reloc) { + free(ctx->bytecode_reloc); + ctx->bytecode_reloc = NULL; + } +} + +LTTNG_HIDDEN +int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx) +{ + int ret; + + ret = bytecode_init(&ctx->bytecode); + if (ret) + return ret; + ret = bytecode_init(&ctx->bytecode_reloc); + if (ret) + goto error; + ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root); + if (ret) + goto error; + + /* Finally, append symbol table to bytecode */ + ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b); + return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data, + 1, bytecode_get_len(&ctx->bytecode_reloc->b)); + +error: + filter_bytecode_free(ctx); + return ret; +} diff --git a/src/common/filter/filter-visitor-generate-ir.c b/src/common/filter/filter-visitor-generate-ir.c new file mode 100644 index 000000000..11c6b610c --- /dev/null +++ b/src/common/filter/filter-visitor-generate-ir.c @@ -0,0 +1,909 @@ +/* + * filter-visitor-generate-ir.c + * + * LTTng filter generate intermediate representation + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +#include +#include + +static +struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx, + struct filter_node *node, enum ir_side side); + +static +struct ir_op *make_op_root(struct ir_op *child, enum ir_side side) +{ + struct ir_op *op; + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + switch (child->data_type) { + case IR_DATA_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown root child data type\n"); + free(op); + return NULL; + case IR_DATA_STRING: + fprintf(stderr, "[error] String cannot be root data type\n"); + free(op); + return NULL; + case IR_DATA_NUMERIC: + case IR_DATA_FIELD_REF: + case IR_DATA_GET_CONTEXT_REF: + case IR_DATA_EXPRESSION: + /* ok */ + break; + } + op->op = IR_OP_ROOT; + op->side = side; + op->data_type = child->data_type; + op->signedness = child->signedness; + op->u.root.child = child; + return op; +} + +static +enum ir_load_string_type get_literal_string_type(const char *string) +{ + assert(string); + + if (strutils_is_star_glob_pattern(string)) { + if (strutils_is_star_at_the_end_only_glob_pattern(string)) { + return IR_LOAD_STRING_TYPE_GLOB_STAR_END; + } + + return IR_LOAD_STRING_TYPE_GLOB_STAR; + } + + return IR_LOAD_STRING_TYPE_PLAIN; +} + +static +struct ir_op *make_op_load_string(const char *string, enum ir_side side) +{ + struct ir_op *op; + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_LOAD; + op->data_type = IR_DATA_STRING; + op->signedness = IR_SIGN_UNKNOWN; + op->side = side; + op->u.load.u.string.type = get_literal_string_type(string); + op->u.load.u.string.value = strdup(string); + if (!op->u.load.u.string.value) { + free(op); + return NULL; + } + return op; +} + +static +struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side) +{ + struct ir_op *op; + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_LOAD; + op->data_type = IR_DATA_NUMERIC; + /* TODO: for now, all numeric values are signed */ + op->signedness = IR_SIGNED; + op->side = side; + op->u.load.u.num = v; + return op; +} + +static +struct ir_op *make_op_load_float(double v, enum ir_side side) +{ + struct ir_op *op; + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_LOAD; + op->data_type = IR_DATA_FLOAT; + op->signedness = IR_SIGN_UNKNOWN; + op->side = side; + op->u.load.u.flt = v; + return op; +} + +static +void free_load_expression(struct ir_load_expression *load_expression) +{ + struct ir_load_expression_op *exp_op; + + if (!load_expression) + return; + exp_op = load_expression->child; + for (;;) { + struct ir_load_expression_op *prev_exp_op; + + if (!exp_op) + break; + switch (exp_op->type) { + case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: + case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: + case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: + case IR_LOAD_EXPRESSION_GET_INDEX: + case IR_LOAD_EXPRESSION_LOAD_FIELD: + break; + case IR_LOAD_EXPRESSION_GET_SYMBOL: + free(exp_op->u.symbol); + break; + } + prev_exp_op = exp_op; + exp_op = exp_op->next; + free(prev_exp_op); + } + free(load_expression); +} + +/* + * Returns the first node of the chain, after initializing the next + * pointers. + */ +static +struct filter_node *load_expression_get_forward_chain(struct filter_node *node) +{ + struct filter_node *prev_node; + + for (;;) { + assert(node->type == NODE_EXPRESSION); + prev_node = node; + node = node->u.expression.prev; + if (!node) { + break; + } + node->u.expression.next = prev_node; + } + return prev_node; +} + +static +struct ir_load_expression *create_load_expression(struct filter_node *node) +{ + struct ir_load_expression *load_exp; + struct ir_load_expression_op *load_exp_op, *prev_op; + const char *str; + + /* Get forward chain. */ + node = load_expression_get_forward_chain(node); + if (!node) + return NULL; + load_exp = calloc(sizeof(struct ir_load_expression), 1); + if (!load_exp) + return NULL; + + /* Root */ + load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); + if (!load_exp_op) + goto error; + load_exp->child = load_exp_op; + str = node->u.expression.u.string; + if (!strcmp(str, "$ctx")) { + load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT; + node = node->u.expression.next; + if (!node) { + fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str); + goto error; + } + str = node->u.expression.u.string; + } else if (!strcmp(str, "$app")) { + load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT; + node = node->u.expression.next; + if (!node) { + fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str); + goto error; + } + str = node->u.expression.u.string; + } else if (str[0] == '$') { + fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str); + goto error; + } else { + load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT; + } + + for (;;) { + struct filter_node *bracket_node; + + prev_op = load_exp_op; + load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); + if (!load_exp_op) + goto error; + prev_op->next = load_exp_op; + load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL; + load_exp_op->u.symbol = strdup(str); + if (!load_exp_op->u.symbol) + goto error; + + /* Explore brackets from current node. */ + for (bracket_node = node->u.expression.next_bracket; + bracket_node != NULL; + bracket_node = bracket_node->u.expression.next_bracket) { + prev_op = load_exp_op; + load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); + if (!load_exp_op) + goto error; + prev_op->next = load_exp_op; + load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX; + load_exp_op->u.index = bracket_node->u.expression.u.constant; + } + /* Go to next chain element. */ + node = node->u.expression.next; + if (!node) + break; + str = node->u.expression.u.string; + } + /* Add final load field */ + prev_op = load_exp_op; + load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); + if (!load_exp_op) + goto error; + prev_op->next = load_exp_op; + load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD; + return load_exp; + +error: + free_load_expression(load_exp); + return NULL; +} + +static +struct ir_op *make_op_load_expression(struct filter_node *node, + enum ir_side side) +{ + struct ir_op *op; + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_LOAD; + op->data_type = IR_DATA_EXPRESSION; + op->signedness = IR_SIGN_DYN; + op->side = side; + op->u.load.u.expression = create_load_expression(node); + if (!op->u.load.u.expression) { + goto error; + } + return op; + +error: + free_load_expression(op->u.load.u.expression); + free(op); + return NULL; +} + +static +struct ir_op *make_op_unary(enum unary_op_type unary_op_type, + const char *op_str, enum ir_op_signedness signedness, + struct ir_op *child, enum ir_side side) +{ + struct ir_op *op = NULL; + + if (child->data_type == IR_DATA_STRING) { + fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str); + goto error; + } + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_UNARY; + op->data_type = child->data_type; + op->signedness = signedness; + op->side = side; + op->u.unary.type = unary_op_type; + op->u.unary.child = child; + return op; + +error: + free(op); + return NULL; +} + +/* + * unary + is pretty much useless. + */ +static +struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side) +{ + return make_op_unary(AST_UNARY_PLUS, "+", child->signedness, + child, side); +} + +static +struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side) +{ + return make_op_unary(AST_UNARY_MINUS, "-", child->signedness, + child, side); +} + +static +struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side) +{ + return make_op_unary(AST_UNARY_NOT, "!", child->signedness, + child, side); +} + +static +struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side) +{ + return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness, + child, side); +} + +static +struct ir_op *make_op_binary_compare(enum op_type bin_op_type, + const char *op_str, struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + struct ir_op *op = NULL; + + if (left->data_type == IR_DATA_UNKNOWN + || right->data_type == IR_DATA_UNKNOWN) { + fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str); + goto error; + + } + if ((left->data_type == IR_DATA_STRING + && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT)) + || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) && + right->data_type == IR_DATA_STRING)) { + fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str); + goto error; + } + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_BINARY; + op->u.binary.type = bin_op_type; + op->u.binary.left = left; + op->u.binary.right = right; + + /* we return a boolean, represented as signed numeric */ + op->data_type = IR_DATA_NUMERIC; + op->signedness = IR_SIGNED; + op->side = side; + + return op; + +error: + free(op); + return NULL; +} + +static +struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_EQ, "==", left, right, side); +} + +static +struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_NE, "!=", left, right, side); +} + +static +struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_GT, ">", left, right, side); +} + +static +struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_LT, "<", left, right, side); +} + +static +struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_GE, ">=", left, right, side); +} + +static +struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_compare(AST_OP_LE, "<=", left, right, side); +} + +static +struct ir_op *make_op_binary_logical(enum op_type bin_op_type, + const char *op_str, struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + struct ir_op *op = NULL; + + if (left->data_type == IR_DATA_UNKNOWN + || right->data_type == IR_DATA_UNKNOWN) { + fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str); + goto error; + + } + if (left->data_type == IR_DATA_STRING + || right->data_type == IR_DATA_STRING) { + fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str); + goto error; + } + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_LOGICAL; + op->u.binary.type = bin_op_type; + op->u.binary.left = left; + op->u.binary.right = right; + + /* we return a boolean, represented as signed numeric */ + op->data_type = IR_DATA_NUMERIC; + op->signedness = IR_SIGNED; + op->side = side; + + return op; + +error: + free(op); + return NULL; +} + +static +struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type, + const char *op_str, struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + struct ir_op *op = NULL; + + if (left->data_type == IR_DATA_UNKNOWN + || right->data_type == IR_DATA_UNKNOWN) { + fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str); + goto error; + + } + if (left->data_type == IR_DATA_STRING + || right->data_type == IR_DATA_STRING) { + fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str); + goto error; + } + if (left->data_type == IR_DATA_FLOAT + || right->data_type == IR_DATA_FLOAT) { + fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str); + goto error; + } + + op = calloc(sizeof(struct ir_op), 1); + if (!op) + return NULL; + op->op = IR_OP_BINARY; + op->u.binary.type = bin_op_type; + op->u.binary.left = left; + op->u.binary.right = right; + + /* we return a signed numeric */ + op->data_type = IR_DATA_NUMERIC; + op->signedness = IR_SIGNED; + op->side = side; + + return op; + +error: + free(op); + return NULL; +} + +static +struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_logical(AST_OP_AND, "&&", left, right, side); +} + +static +struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_logical(AST_OP_OR, "||", left, right, side); +} + +static +struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side); +} + +static +struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side); +} + +static +struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side); +} + +static +struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side); +} + +static +struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right, + enum ir_side side) +{ + return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side); +} + +static +void filter_free_ir_recursive(struct ir_op *op) +{ + if (!op) + return; + switch (op->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] Unknown op type in %s\n", + __func__); + break; + case IR_OP_ROOT: + filter_free_ir_recursive(op->u.root.child); + break; + case IR_OP_LOAD: + switch (op->data_type) { + case IR_DATA_STRING: + free(op->u.load.u.string.value); + break; + case IR_DATA_FIELD_REF: /* fall-through */ + case IR_DATA_GET_CONTEXT_REF: + free(op->u.load.u.ref); + break; + case IR_DATA_EXPRESSION: + free_load_expression(op->u.load.u.expression); + default: + break; + } + break; + case IR_OP_UNARY: + filter_free_ir_recursive(op->u.unary.child); + break; + case IR_OP_BINARY: + filter_free_ir_recursive(op->u.binary.left); + filter_free_ir_recursive(op->u.binary.right); + break; + case IR_OP_LOGICAL: + filter_free_ir_recursive(op->u.logical.left); + filter_free_ir_recursive(op->u.logical.right); + break; + } + free(op); +} + +static +struct ir_op *make_expression(struct filter_parser_ctx *ctx, + struct filter_node *node, enum ir_side side) +{ + switch (node->u.expression.type) { + case AST_EXP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown expression type\n", __func__); + return NULL; + + case AST_EXP_STRING: + return make_op_load_string(node->u.expression.u.string, side); + case AST_EXP_CONSTANT: + return make_op_load_numeric(node->u.expression.u.constant, + side); + case AST_EXP_FLOAT_CONSTANT: + return make_op_load_float(node->u.expression.u.float_constant, + side); + case AST_EXP_IDENTIFIER: + case AST_EXP_GLOBAL_IDENTIFIER: + return make_op_load_expression(node, side); + case AST_EXP_NESTED: + return generate_ir_recursive(ctx, node->u.expression.u.child, + side); + } +} + +static +struct ir_op *make_op(struct filter_parser_ctx *ctx, + struct filter_node *node, enum ir_side side) +{ + struct ir_op *op = NULL, *lchild, *rchild; + const char *op_str = "?"; + + switch (node->u.op.type) { + case AST_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown binary op type\n", __func__); + return NULL; + + /* + * The following binary operators other than comparators and + * logical and/or are not supported yet. + */ + case AST_OP_MUL: + op_str = "*"; + goto error_not_supported; + case AST_OP_DIV: + op_str = "/"; + goto error_not_supported; + case AST_OP_MOD: + op_str = "%"; + goto error_not_supported; + case AST_OP_PLUS: + op_str = "+"; + goto error_not_supported; + case AST_OP_MINUS: + op_str = "-"; + goto error_not_supported; + + case AST_OP_BIT_RSHIFT: + case AST_OP_BIT_LSHIFT: + case AST_OP_BIT_AND: + case AST_OP_BIT_OR: + case AST_OP_BIT_XOR: + lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); + if (!lchild) + return NULL; + rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT); + if (!rchild) { + filter_free_ir_recursive(lchild); + return NULL; + } + break; + + case AST_OP_EQ: + case AST_OP_NE: + case AST_OP_GT: + case AST_OP_LT: + case AST_OP_GE: + case AST_OP_LE: + lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); + if (!lchild) + return NULL; + rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT); + if (!rchild) { + filter_free_ir_recursive(lchild); + return NULL; + } + break; + + case AST_OP_AND: + case AST_OP_OR: + /* + * Both children considered as left, since we need to + * populate R0. + */ + lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); + if (!lchild) + return NULL; + rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT); + if (!rchild) { + filter_free_ir_recursive(lchild); + return NULL; + } + break; + } + + switch (node->u.op.type) { + case AST_OP_AND: + op = make_op_binary_logical_and(lchild, rchild, side); + break; + case AST_OP_OR: + op = make_op_binary_logical_or(lchild, rchild, side); + break; + case AST_OP_EQ: + op = make_op_binary_eq(lchild, rchild, side); + break; + case AST_OP_NE: + op = make_op_binary_ne(lchild, rchild, side); + break; + case AST_OP_GT: + op = make_op_binary_gt(lchild, rchild, side); + break; + case AST_OP_LT: + op = make_op_binary_lt(lchild, rchild, side); + break; + case AST_OP_GE: + op = make_op_binary_ge(lchild, rchild, side); + break; + case AST_OP_LE: + op = make_op_binary_le(lchild, rchild, side); + break; + case AST_OP_BIT_RSHIFT: + op = make_op_binary_bitwise_rshift(lchild, rchild, side); + break; + case AST_OP_BIT_LSHIFT: + op = make_op_binary_bitwise_lshift(lchild, rchild, side); + break; + case AST_OP_BIT_AND: + op = make_op_binary_bitwise_and(lchild, rchild, side); + break; + case AST_OP_BIT_OR: + op = make_op_binary_bitwise_or(lchild, rchild, side); + break; + case AST_OP_BIT_XOR: + op = make_op_binary_bitwise_xor(lchild, rchild, side); + break; + default: + break; + } + + if (!op) { + filter_free_ir_recursive(rchild); + filter_free_ir_recursive(lchild); + } + return op; + +error_not_supported: + fprintf(stderr, "[error] %s: binary operation '%s' not supported\n", + __func__, op_str); + return NULL; +} + +static +struct ir_op *make_unary_op(struct filter_parser_ctx *ctx, + struct filter_node *node, enum ir_side side) +{ + switch (node->u.unary_op.type) { + case AST_UNARY_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown unary op type\n", __func__); + return NULL; + + case AST_UNARY_PLUS: + { + struct ir_op *op, *child; + + child = generate_ir_recursive(ctx, node->u.unary_op.child, + side); + if (!child) + return NULL; + op = make_op_unary_plus(child, side); + if (!op) { + filter_free_ir_recursive(child); + return NULL; + } + return op; + } + case AST_UNARY_MINUS: + { + struct ir_op *op, *child; + + child = generate_ir_recursive(ctx, node->u.unary_op.child, + side); + if (!child) + return NULL; + op = make_op_unary_minus(child, side); + if (!op) { + filter_free_ir_recursive(child); + return NULL; + } + return op; + } + case AST_UNARY_NOT: + { + struct ir_op *op, *child; + + child = generate_ir_recursive(ctx, node->u.unary_op.child, + side); + if (!child) + return NULL; + op = make_op_unary_not(child, side); + if (!op) { + filter_free_ir_recursive(child); + return NULL; + } + return op; + } + case AST_UNARY_BIT_NOT: + { + struct ir_op *op, *child; + + child = generate_ir_recursive(ctx, node->u.unary_op.child, + side); + if (!child) + return NULL; + op = make_op_unary_bit_not(child, side); + if (!op) { + filter_free_ir_recursive(child); + return NULL; + } + return op; + } + } + + return NULL; +} + +static +struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx, + struct filter_node *node, enum ir_side side) +{ + switch (node->type) { + case NODE_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown node type\n", __func__); + return NULL; + + case NODE_ROOT: + { + struct ir_op *op, *child; + + child = generate_ir_recursive(ctx, node->u.root.child, + side); + if (!child) + return NULL; + op = make_op_root(child, side); + if (!op) { + filter_free_ir_recursive(child); + return NULL; + } + return op; + } + case NODE_EXPRESSION: + return make_expression(ctx, node, side); + case NODE_OP: + return make_op(ctx, node, side); + case NODE_UNARY_OP: + return make_unary_op(ctx, node, side); + } + return 0; +} + +LTTNG_HIDDEN +void filter_ir_free(struct filter_parser_ctx *ctx) +{ + filter_free_ir_recursive(ctx->ir_root); + ctx->ir_root = NULL; +} + +LTTNG_HIDDEN +int filter_visitor_ir_generate(struct filter_parser_ctx *ctx) +{ + struct ir_op *op; + + op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT); + if (!op) { + return -EINVAL; + } + ctx->ir_root = op; + return 0; +} diff --git a/src/common/filter/filter-visitor-ir-check-binary-comparator.c b/src/common/filter/filter-visitor-ir-check-binary-comparator.c new file mode 100644 index 000000000..bcbb69e3f --- /dev/null +++ b/src/common/filter/filter-visitor-ir-check-binary-comparator.c @@ -0,0 +1,72 @@ +/* + * filter-visitor-ir-check-binary-comparator.c + * + * LTTng filter IR check binary comparator + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +static +int check_bin_comparator(struct ir_op *node) +{ + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown op type\n", __func__); + return -EINVAL; + + case IR_OP_ROOT: + return check_bin_comparator(node->u.root.child); + case IR_OP_LOAD: + return 0; + case IR_OP_UNARY: + return check_bin_comparator(node->u.unary.child); + case IR_OP_BINARY: + { + int ret; + + if (node->u.binary.left->data_type == IR_DATA_STRING + || node->u.binary.right->data_type + == IR_DATA_STRING) { + if (node->u.binary.type != AST_OP_EQ + && node->u.binary.type != AST_OP_NE) { + fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n"); + return -EINVAL; + } + } + + ret = check_bin_comparator(node->u.binary.left); + if (ret) + return ret; + return check_bin_comparator(node->u.binary.right); + } + case IR_OP_LOGICAL: + { + int ret; + + ret = check_bin_comparator(node->u.logical.left); + if (ret) + return ret; + return check_bin_comparator(node->u.logical.right); + } + } +} + +int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx) +{ + return check_bin_comparator(ctx->ir_root); +} diff --git a/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c b/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c new file mode 100644 index 000000000..04a5fbdd6 --- /dev/null +++ b/src/common/filter/filter-visitor-ir-check-binary-op-nesting.c @@ -0,0 +1,71 @@ +/* + * filter-visitor-ir-check-binary-op-nesting.c + * + * LTTng filter IR check binary op nesting + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +#include + +static +int check_bin_op_nesting_recursive(struct ir_op *node, int nesting) +{ + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown op type\n", __func__); + return -EINVAL; + + case IR_OP_ROOT: + return check_bin_op_nesting_recursive(node->u.root.child, + nesting); + case IR_OP_LOAD: + return 0; + case IR_OP_UNARY: + return check_bin_op_nesting_recursive(node->u.unary.child, + nesting); + case IR_OP_BINARY: + { + int ret; + + ret = check_bin_op_nesting_recursive(node->u.binary.left, + nesting + 1); + if (ret) + return ret; + return check_bin_op_nesting_recursive(node->u.binary.right, + nesting + 1); + } + case IR_OP_LOGICAL: + { + int ret; + + ret = check_bin_op_nesting_recursive(node->u.logical.left, + nesting); + if (ret) + return ret; + return check_bin_op_nesting_recursive(node->u.logical.right, + nesting); + } + } +} + +LTTNG_HIDDEN +int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx) +{ + return check_bin_op_nesting_recursive(ctx->ir_root, 0); +} diff --git a/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c b/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c new file mode 100644 index 000000000..5e2778e09 --- /dev/null +++ b/src/common/filter/filter-visitor-ir-normalize-glob-patterns.c @@ -0,0 +1,84 @@ +/* + * filter-visitor-ir-normalize-glob-patterns.c + * + * LTTng filter IR normalize string + * + * Copyright 2017 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +static +int normalize_glob_patterns(struct ir_op *node) +{ + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown op type\n", __func__); + return -EINVAL; + + case IR_OP_ROOT: + return normalize_glob_patterns(node->u.root.child); + case IR_OP_LOAD: + { + if (node->data_type == IR_DATA_STRING) { + enum ir_load_string_type type = + node->u.load.u.string.type; + if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END || + type == IR_LOAD_STRING_TYPE_GLOB_STAR) { + assert(node->u.load.u.string.value); + strutils_normalize_star_glob_pattern( + node->u.load.u.string.value); + } + } + + return 0; + } + case IR_OP_UNARY: + return normalize_glob_patterns(node->u.unary.child); + case IR_OP_BINARY: + { + int ret = normalize_glob_patterns(node->u.binary.left); + + if (ret) + return ret; + return normalize_glob_patterns(node->u.binary.right); + } + case IR_OP_LOGICAL: + { + int ret; + + ret = normalize_glob_patterns(node->u.logical.left); + if (ret) + return ret; + return normalize_glob_patterns(node->u.logical.right); + } + } +} + +/* + * This function normalizes all the globbing literal strings with + * utils_normalize_glob_pattern(). See the documentation of + * utils_normalize_glob_pattern() for more details. + */ +LTTNG_HIDDEN +int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx) +{ + return normalize_glob_patterns(ctx->ir_root); +} diff --git a/src/common/filter/filter-visitor-ir-validate-globbing.c b/src/common/filter/filter-visitor-ir-validate-globbing.c new file mode 100644 index 000000000..3e749a4da --- /dev/null +++ b/src/common/filter/filter-visitor-ir-validate-globbing.c @@ -0,0 +1,112 @@ +/* + * filter-visitor-ir-validate-globbing.c + * + * LTTng filter IR validate globbing + * + * Copyright 2017 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +static +int validate_globbing(struct ir_op *node) +{ + int ret; + + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown op type\n", __func__); + return -EINVAL; + + case IR_OP_ROOT: + return validate_globbing(node->u.root.child); + case IR_OP_LOAD: + return 0; + case IR_OP_UNARY: + return validate_globbing(node->u.unary.child); + case IR_OP_BINARY: + { + struct ir_op *left = node->u.binary.left; + struct ir_op *right = node->u.binary.right; + + if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD && + left->data_type == IR_DATA_STRING && + right->data_type == IR_DATA_STRING) { + /* Test 1. */ + if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR && + right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) { + fprintf(stderr, "[error] Cannot compare two globbing patterns\n"); + return -1; + } + + if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR && + left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) { + fprintf(stderr, "[error] Cannot compare two globbing patterns\n"); + return -1; + } + } + + if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) || + (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) { + if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) || + (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) { + /* Test 2. */ + if (node->u.binary.type != AST_OP_EQ && + node->u.binary.type != AST_OP_NE) { + fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n"); + return -1; + } + } + } + + ret = validate_globbing(left); + if (ret) { + return ret; + } + + return validate_globbing(right); + } + case IR_OP_LOGICAL: + ret = validate_globbing(node->u.logical.left); + if (ret) + return ret; + return validate_globbing(node->u.logical.right); + } +} + +/* + * This function recursively validates that: + * + * 1. When there's a binary operation between two literal strings, + * if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type, + * the other one has the IR_LOAD_STRING_TYPE_PLAIN type. + * + * In other words, you cannot compare two globbing patterns, except + * for two globbing patterns with only a star at the end for backward + * compatibility reasons. + * + * 2. When there's a binary operation between two literal strings, if + * one of them is a (full) star globbing pattern, the binary + * operation is either == or !=. + */ +LTTNG_HIDDEN +int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx) +{ + return validate_globbing(ctx->ir_root); +} diff --git a/src/common/filter/filter-visitor-ir-validate-string.c b/src/common/filter/filter-visitor-ir-validate-string.c new file mode 100644 index 000000000..1df83b03f --- /dev/null +++ b/src/common/filter/filter-visitor-ir-validate-string.c @@ -0,0 +1,124 @@ +/* + * filter-visitor-ir-validate-string.c + * + * LTTng filter IR validate string + * + * Copyright 2014 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "filter-ast.h" +#include "filter-parser.h" +#include "filter-ir.h" + +enum parse_char_result { + PARSE_CHAR_UNKNOWN = -2, + PARSE_CHAR_WILDCARD = -1, + PARSE_CHAR_NORMAL = 0, +}; + +static +enum parse_char_result parse_char(const char **p) +{ + switch (**p) { + case '\\': + (*p)++; + switch (**p) { + case '\\': + case '*': + return PARSE_CHAR_NORMAL; + default: + return PARSE_CHAR_UNKNOWN; + } + case '*': + return PARSE_CHAR_WILDCARD; + default: + return PARSE_CHAR_NORMAL; + } +} + +static +int validate_string(struct ir_op *node) +{ + switch (node->op) { + case IR_OP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown op type\n", __func__); + return -EINVAL; + + case IR_OP_ROOT: + return validate_string(node->u.root.child); + case IR_OP_LOAD: + { + int ret = 0; + + if (node->data_type == IR_DATA_STRING) { + const char *str; + + assert(node->u.load.u.string.value); + str = node->u.load.u.string.value; + + for (;;) { + enum parse_char_result res; + + if (!(*str)) { + break; + } + + res = parse_char(&str); + str++; + + switch (res) { + case PARSE_CHAR_UNKNOWN: + ret = -EINVAL; + fprintf(stderr, + "Unsupported escape character detected.\n"); + goto end_load; + case PARSE_CHAR_NORMAL: + default: + break; + } + } + } +end_load: + return ret; + } + case IR_OP_UNARY: + return validate_string(node->u.unary.child); + case IR_OP_BINARY: + { + int ret = validate_string(node->u.binary.left); + + if (ret) + return ret; + return validate_string(node->u.binary.right); + } + case IR_OP_LOGICAL: + { + int ret; + + ret = validate_string(node->u.logical.left); + if (ret) + return ret; + return validate_string(node->u.logical.right); + } + } +} + +LTTNG_HIDDEN +int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx) +{ + return validate_string(ctx->ir_root); +} diff --git a/src/common/filter/filter-visitor-xml.c b/src/common/filter/filter-visitor-xml.c new file mode 100644 index 000000000..d5ff0c187 --- /dev/null +++ b/src/common/filter/filter-visitor-xml.c @@ -0,0 +1,239 @@ +/* + * filter-visitor-xml.c + * + * LTTng filter XML pretty printer visitor + * + * Copyright 2012 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "filter-ast.h" +#include "filter-parser.h" + +#include + +#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) + +static +int recursive_visit_print(struct filter_node *node, FILE *stream, int indent); + +static +void print_tabs(FILE *fd, int depth) +{ + int i; + + for (i = 0; i < depth; i++) + fprintf(fd, "\t"); +} + +static +int recursive_visit_print_expression(struct filter_node *node, + FILE *stream, int indent) +{ + struct filter_node *iter_node; + + if (!node) { + fprintf(stderr, "[error] %s: NULL child\n", __func__); + return -EINVAL; + } + switch (node->u.expression.type) { + case AST_EXP_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown expression\n", __func__); + return -EINVAL; + case AST_EXP_STRING: + print_tabs(stream, indent); + fprintf(stream, "\n", + node->u.expression.u.string); + break; + case AST_EXP_CONSTANT: + print_tabs(stream, indent); + fprintf(stream, "\n", + node->u.expression.u.constant); + break; + case AST_EXP_FLOAT_CONSTANT: + print_tabs(stream, indent); + fprintf(stream, "\n", + node->u.expression.u.float_constant); + break; + case AST_EXP_IDENTIFIER: /* fall-through */ + case AST_EXP_GLOBAL_IDENTIFIER: + print_tabs(stream, indent); + fprintf(stream, "<%s value=\"%s\"/>\n", + node->u.expression.type == AST_EXP_IDENTIFIER ? + "identifier" : "global_identifier", + node->u.expression.u.identifier); + iter_node = node->u.expression.next; + while (iter_node) { + print_tabs(stream, indent); + fprintf(stream, "\n"); + if (recursive_visit_print_expression(iter_node, + stream, indent + 1)) { + return -EINVAL; + } + print_tabs(stream, indent); + fprintf(stream, "\n"); + iter_node = iter_node->u.expression.next; + + } + break; + case AST_EXP_NESTED: + return recursive_visit_print(node->u.expression.u.child, + stream, indent + 1); + } + return 0; +} + + +static +int recursive_visit_print(struct filter_node *node, FILE *stream, int indent) +{ + int ret; + + if (!node) { + fprintf(stderr, "[error] %s: NULL child\n", __func__); + return -EINVAL; + } + switch (node->type) { + case NODE_UNKNOWN: + default: + fprintf(stderr, "[error] %s: unknown node type\n", __func__); + return -EINVAL; + case NODE_ROOT: + print_tabs(stream, indent); + fprintf(stream, "\n"); + ret = recursive_visit_print(node->u.root.child, stream, + indent + 1); + print_tabs(stream, indent); + fprintf(stream, "\n"); + return ret; + case NODE_EXPRESSION: + print_tabs(stream, indent); + fprintf(stream, "\n"); + ret = recursive_visit_print_expression(node, stream, + indent + 1); + print_tabs(stream, indent); + fprintf(stream, "\n"); + return ret; + case NODE_OP: + print_tabs(stream, indent); + fprintf(stream, ">\""); + break; + case AST_OP_BIT_LSHIFT: + fprintf(stream, "\"<<\""); + break; + case AST_OP_AND: + fprintf(stream, "\"&&\""); + break; + case AST_OP_OR: + fprintf(stream, "\"||\""); + break; + case AST_OP_BIT_AND: + fprintf(stream, "\"&\""); + break; + case AST_OP_BIT_OR: + fprintf(stream, "\"|\""); + break; + case AST_OP_BIT_XOR: + fprintf(stream, "\"^\""); + break; + + case AST_OP_EQ: + fprintf(stream, "\"==\""); + break; + case AST_OP_NE: + fprintf(stream, "\"!=\""); + break; + case AST_OP_GT: + fprintf(stream, "\">\""); + break; + case AST_OP_LT: + fprintf(stream, "\"<\""); + break; + case AST_OP_GE: + fprintf(stream, "\">=\""); + break; + case AST_OP_LE: + fprintf(stream, "\"<=\""); + break; + } + fprintf(stream, ">\n"); + ret = recursive_visit_print(node->u.op.lchild, + stream, indent + 1); + if (ret) + return ret; + ret = recursive_visit_print(node->u.op.rchild, + stream, indent + 1); + if (ret) + return ret; + print_tabs(stream, indent); + fprintf(stream, "\n"); + return ret; + case NODE_UNARY_OP: + print_tabs(stream, indent); + fprintf(stream, "\n"); + ret = recursive_visit_print(node->u.unary_op.child, + stream, indent + 1); + print_tabs(stream, indent); + fprintf(stream, "\n"); + return ret; + } + return 0; +} + +LTTNG_HIDDEN +int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream, + int indent) +{ + return recursive_visit_print(&ctx->ast->root, stream, indent); +} diff --git a/src/common/filter/memstream.h b/src/common/filter/memstream.h new file mode 100644 index 000000000..cd246eed2 --- /dev/null +++ b/src/common/filter/memstream.h @@ -0,0 +1,87 @@ +/* + * Copyright 2012 (C) Mathieu Desnoyers + * + * SPDX-License-Identifier: MIT + * + */ + +#ifndef _LTTNG_CTL_MEMSTREAM_H +#define _LTTNG_CTL_MEMSTREAM_H + +#ifdef LTTNG_HAVE_FMEMOPEN +#include + +static inline +FILE *lttng_fmemopen(void *buf, size_t size, const char *mode) +{ + return fmemopen(buf, size, mode); +} + +#else /* LTTNG_HAVE_FMEMOPEN */ + +#include +#include + +/* + * Fallback for systems which don't have fmemopen. Copy buffer to a + * temporary file, and use that file as FILE * input. + */ +static inline +FILE *lttng_fmemopen(void *buf, size_t size, const char *mode) +{ + char tmpname[PATH_MAX]; + size_t len; + FILE *fp; + int ret; + + /* + * Support reading only. + */ + if (strcmp(mode, "rb") != 0) { + return NULL; + } + strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX); + ret = mkstemp(tmpname); + if (ret < 0) { + return NULL; + } + /* + * We need to write to the file. + */ + fp = fdopen(ret, "w+"); + if (!fp) { + goto error_unlink; + } + /* Copy the entire buffer to the file */ + len = fwrite(buf, sizeof(char), size, fp); + if (len != size) { + goto error_close; + } + ret = fseek(fp, 0L, SEEK_SET); + if (ret < 0) { + PERROR("fseek"); + goto error_close; + } + /* We keep the handle open, but can unlink the file on the VFS. */ + ret = unlink(tmpname); + if (ret < 0) { + PERROR("unlink"); + } + return fp; + +error_close: + ret = fclose(fp); + if (ret < 0) { + PERROR("close"); + } +error_unlink: + ret = unlink(tmpname); + if (ret < 0) { + PERROR("unlink"); + } + return NULL; +} + +#endif /* LTTNG_HAVE_FMEMOPEN */ + +#endif /* _LTTNG_CTL_MEMSTREAM_H */ diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index 1bee05f0c..8c271f5ef 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -SUBDIRS = filter +SUBDIRS = AM_CPPFLAGS += -I$(srcdir) -I$(builddir) @@ -16,8 +16,7 @@ liblttng_ctl_la_LDFLAGS = \ liblttng_ctl_la_LIBADD = \ $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ - $(top_builddir)/src/common/libcommon.la \ - $(top_builddir)/src/lib/lttng-ctl/filter/libfilter.la + $(top_builddir)/src/common/libcommon.la pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = lttng-ctl.pc diff --git a/src/lib/lttng-ctl/filter/Makefile.am b/src/lib/lttng-ctl/filter/Makefile.am deleted file mode 100644 index cac4cb175..000000000 --- a/src/lib/lttng-ctl/filter/Makefile.am +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -AM_CPPFLAGS += -I$(srcdir) -I$(builddir) - -noinst_PROGRAMS = filter-grammar-test -noinst_LTLIBRARIES = libfilter.la -noinst_HEADERS = filter-ast.h \ - filter-symbols.h - -BUILT_SOURCES = filter-parser.h - -libfilter_la_SOURCES = \ - filter-parser.y filter-lexer.l \ - filter-visitor-xml.c \ - filter-visitor-generate-ir.c \ - filter-visitor-ir-check-binary-op-nesting.c \ - filter-visitor-ir-validate-string.c \ - filter-visitor-ir-validate-globbing.c \ - filter-visitor-ir-normalize-glob-patterns.c \ - filter-visitor-generate-bytecode.c \ - filter-ast.h \ - filter-bytecode.h \ - filter-ir.h \ - memstream.h -libfilter_la_CFLAGS = -include filter-symbols.h $(AM_CFLAGS) -libfilter_la_LIBADD = $(top_builddir)/src/common/string-utils/libstring-utils.la - -AM_YFLAGS = -t -d -v -Wno-yacc - -# start with empty files to clean -CLEANFILES = - -if HAVE_BISON -# we have bison: we can clean the generated parser files -CLEANFILES += filter-parser.c filter-parser.h filter-parser.output -else # HAVE_BISON -# create target used to stop the build if we want to build the parser, -# but we don't have the necessary tool to do so -ERR_MSG = "Error: Cannot build target because bison is missing." -ERR_MSG += "Make sure bison is installed and run the configure script again." - -filter-parser.c filter-parser.h: filter-parser.y - @echo $(ERR_MSG) - @false - -all-local: filter-parser.c filter-parser.h -endif # HAVE_BISON - -if HAVE_FLEX -# we have flex: we can clean the generated lexer files -CLEANFILES += filter-lexer.c -else # HAVE_FLEX -# create target used to stop the build if we want to build the lexer, -# but we don't have the necessary tool to do so -ERR_MSG = "Error: Cannot build target because flex is missing." -ERR_MSG += "Make sure flex is installed and run the configure script again." - -filter-lexer.c: filter-lexer.l - @echo $(ERR_MSG) - @false - -all-local: filter-lexer.c -endif # HAVE_FLEX - -filter_grammar_test_SOURCES = filter-grammar-test.c -filter_grammar_test_LDADD = libfilter.la diff --git a/src/lib/lttng-ctl/filter/filter-ast.h b/src/lib/lttng-ctl/filter/filter-ast.h deleted file mode 100644 index 926d8dd64..000000000 --- a/src/lib/lttng-ctl/filter/filter-ast.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef _FILTER_AST_H -#define _FILTER_AST_H - -/* - * filter-ast.h - * - * LTTng filter AST - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -/* - * Note: filter-ast.h should be included before filter-parser.h. - */ - -#include -#include - -#define printf_debug(fmt, args...) \ - do { \ - if (filter_parser_debug) \ - fprintf(stdout, "[debug] " fmt, ## args); \ - } while (0) - -// the parameter name (of the reentrant 'yyparse' function) -// data is a pointer to a 'SParserParam' structure -//#define YYPARSE_PARAM parser_ctx - -#ifndef YY_TYPEDEF_YY_SCANNER_T -#define YY_TYPEDEF_YY_SCANNER_T -typedef void* yyscan_t; -#endif - -extern int filter_parser_debug; - -struct filter_node; -struct filter_parser; - -enum node_type { - NODE_UNKNOWN = 0, - NODE_ROOT, - - NODE_EXPRESSION, - NODE_OP, - NODE_UNARY_OP, - - NR_NODE_TYPES, -}; - -enum op_type { - AST_OP_UNKNOWN = 0, - AST_OP_MUL, - AST_OP_DIV, - AST_OP_MOD, - AST_OP_PLUS, - AST_OP_MINUS, - AST_OP_BIT_RSHIFT, - AST_OP_BIT_LSHIFT, - AST_OP_AND, - AST_OP_OR, - AST_OP_BIT_AND, - AST_OP_BIT_OR, - AST_OP_BIT_XOR, - - AST_OP_EQ, - AST_OP_NE, - AST_OP_GT, - AST_OP_LT, - AST_OP_GE, - AST_OP_LE, -}; - -enum unary_op_type { - AST_UNARY_UNKNOWN = 0, - AST_UNARY_PLUS, - AST_UNARY_MINUS, - AST_UNARY_NOT, - AST_UNARY_BIT_NOT, -}; - -enum ast_link_type { - AST_LINK_UNKNOWN = 0, - AST_LINK_DOT, - AST_LINK_RARROW, - AST_LINK_BRACKET, -}; - -struct filter_node { - /* - * Parent node is only set on demand by specific visitor. - */ - struct filter_node *parent; - struct cds_list_head gc; - - enum node_type type; - union { - struct { - } unknown; - struct { - struct filter_node *child; - } root; - struct { - enum { - AST_EXP_UNKNOWN = 0, - AST_EXP_STRING, - AST_EXP_CONSTANT, - AST_EXP_FLOAT_CONSTANT, - AST_EXP_IDENTIFIER, - AST_EXP_GLOBAL_IDENTIFIER, - AST_EXP_NESTED, - } type; - enum ast_link_type post_op; /* reverse */ - enum ast_link_type pre_op; /* forward */ - union { - const char *string; - uint64_t constant; - double float_constant; - const char *identifier; - /* - * child can be nested. - */ - struct filter_node *child; - } u; - /* prev: backward dot/arrow chain (postfix expression) */ - struct filter_node *prev; - /* next: forward dot/arrow chain, generated by a visitor. */ - struct filter_node *next; - /* next_bracket: linked bracket chain (prefix expression) */ - struct filter_node *next_bracket; - } expression; - struct { - enum op_type type; - struct filter_node *lchild; - struct filter_node *rchild; - } op; - struct { - enum unary_op_type type; - struct filter_node *child; - } unary_op; - } u; -}; - -struct filter_ast { - struct filter_node root; - struct cds_list_head allocated_nodes; -}; - -const char *node_type(struct filter_node *node); - -struct ir_op; - -struct filter_parser_ctx { - yyscan_t scanner; - struct filter_ast *ast; - struct cds_list_head allocated_strings; - struct ir_op *ir_root; - struct lttng_filter_bytecode_alloc *bytecode; - struct lttng_filter_bytecode_alloc *bytecode_reloc; -}; - -struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input); -void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx); -int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx); - -static inline -struct filter_ast *filter_parser_get_ast(struct filter_parser_ctx *parser_ctx) -{ - return parser_ctx->ast; -} - -int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream, - int indent); -int filter_visitor_ir_generate(struct filter_parser_ctx *ctx); -void filter_ir_free(struct filter_parser_ctx *ctx); -int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx); -void filter_bytecode_free(struct filter_parser_ctx *ctx); -int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx); -int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx); -int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx); -int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx); -int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx); - -#endif /* _FILTER_AST_H */ diff --git a/src/lib/lttng-ctl/filter/filter-bytecode.h b/src/lib/lttng-ctl/filter/filter-bytecode.h deleted file mode 100644 index 053bb08bb..000000000 --- a/src/lib/lttng-ctl/filter/filter-bytecode.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef _FILTER_BYTECODE_H -#define _FILTER_BYTECODE_H - -/* - * filter-bytecode.h - * - * LTTng filter bytecode - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include - -#include "filter-ast.h" - -/* - * offsets are absolute from start of bytecode. - */ - -struct field_ref { - /* Initially, symbol offset. After link, field offset. */ - uint16_t offset; -} LTTNG_PACKED; - -struct get_symbol { - /* Symbol offset. */ - uint16_t offset; -} LTTNG_PACKED; - -struct get_index_u16 { - uint16_t index; -} LTTNG_PACKED; - -struct get_index_u64 { - uint64_t index; -} LTTNG_PACKED; - -struct literal_numeric { - int64_t v; -} LTTNG_PACKED; - -struct literal_double { - double v; -} LTTNG_PACKED; - -struct literal_string { - char string[0]; -} LTTNG_PACKED; - -enum filter_op { - FILTER_OP_UNKNOWN = 0, - - FILTER_OP_RETURN = 1, - - /* binary */ - FILTER_OP_MUL = 2, - FILTER_OP_DIV = 3, - FILTER_OP_MOD = 4, - FILTER_OP_PLUS = 5, - FILTER_OP_MINUS = 6, - FILTER_OP_BIT_RSHIFT = 7, - FILTER_OP_BIT_LSHIFT = 8, - FILTER_OP_BIT_AND = 9, - FILTER_OP_BIT_OR = 10, - FILTER_OP_BIT_XOR = 11, - - /* binary comparators */ - FILTER_OP_EQ = 12, - FILTER_OP_NE = 13, - FILTER_OP_GT = 14, - FILTER_OP_LT = 15, - FILTER_OP_GE = 16, - FILTER_OP_LE = 17, - - /* string binary comparator: apply to */ - FILTER_OP_EQ_STRING = 18, - FILTER_OP_NE_STRING = 19, - FILTER_OP_GT_STRING = 20, - FILTER_OP_LT_STRING = 21, - FILTER_OP_GE_STRING = 22, - FILTER_OP_LE_STRING = 23, - - /* s64 binary comparator */ - FILTER_OP_EQ_S64 = 24, - FILTER_OP_NE_S64 = 25, - FILTER_OP_GT_S64 = 26, - FILTER_OP_LT_S64 = 27, - FILTER_OP_GE_S64 = 28, - FILTER_OP_LE_S64 = 29, - - /* double binary comparator */ - FILTER_OP_EQ_DOUBLE = 30, - FILTER_OP_NE_DOUBLE = 31, - FILTER_OP_GT_DOUBLE = 32, - FILTER_OP_LT_DOUBLE = 33, - FILTER_OP_GE_DOUBLE = 34, - FILTER_OP_LE_DOUBLE = 35, - - /* Mixed S64-double binary comparators */ - FILTER_OP_EQ_DOUBLE_S64 = 36, - FILTER_OP_NE_DOUBLE_S64 = 37, - FILTER_OP_GT_DOUBLE_S64 = 38, - FILTER_OP_LT_DOUBLE_S64 = 39, - FILTER_OP_GE_DOUBLE_S64 = 40, - FILTER_OP_LE_DOUBLE_S64 = 41, - - FILTER_OP_EQ_S64_DOUBLE = 42, - FILTER_OP_NE_S64_DOUBLE = 43, - FILTER_OP_GT_S64_DOUBLE = 44, - FILTER_OP_LT_S64_DOUBLE = 45, - FILTER_OP_GE_S64_DOUBLE = 46, - FILTER_OP_LE_S64_DOUBLE = 47, - - /* unary */ - FILTER_OP_UNARY_PLUS = 48, - FILTER_OP_UNARY_MINUS = 49, - FILTER_OP_UNARY_NOT = 50, - FILTER_OP_UNARY_PLUS_S64 = 51, - FILTER_OP_UNARY_MINUS_S64 = 52, - FILTER_OP_UNARY_NOT_S64 = 53, - FILTER_OP_UNARY_PLUS_DOUBLE = 54, - FILTER_OP_UNARY_MINUS_DOUBLE = 55, - FILTER_OP_UNARY_NOT_DOUBLE = 56, - - /* logical */ - FILTER_OP_AND = 57, - FILTER_OP_OR = 58, - - /* load field ref */ - FILTER_OP_LOAD_FIELD_REF = 59, - FILTER_OP_LOAD_FIELD_REF_STRING = 60, - FILTER_OP_LOAD_FIELD_REF_SEQUENCE = 61, - FILTER_OP_LOAD_FIELD_REF_S64 = 62, - FILTER_OP_LOAD_FIELD_REF_DOUBLE = 63, - - /* load immediate from operand */ - FILTER_OP_LOAD_STRING = 64, - FILTER_OP_LOAD_S64 = 65, - FILTER_OP_LOAD_DOUBLE = 66, - - /* cast */ - FILTER_OP_CAST_TO_S64 = 67, - FILTER_OP_CAST_DOUBLE_TO_S64 = 68, - FILTER_OP_CAST_NOP = 69, - - /* get context ref */ - FILTER_OP_GET_CONTEXT_REF = 70, - FILTER_OP_GET_CONTEXT_REF_STRING = 71, - FILTER_OP_GET_CONTEXT_REF_S64 = 72, - FILTER_OP_GET_CONTEXT_REF_DOUBLE = 73, - - /* load userspace field ref */ - FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74, - FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75, - - /* - * load immediate star globbing pattern (literal string) - * from immediate - */ - FILTER_OP_LOAD_STAR_GLOB_STRING = 76, - - /* globbing pattern binary operator: apply to */ - FILTER_OP_EQ_STAR_GLOB_STRING = 77, - FILTER_OP_NE_STAR_GLOB_STRING = 78, - - /* - * Instructions for recursive traversal through composed types. - */ - FILTER_OP_GET_CONTEXT_ROOT = 79, - FILTER_OP_GET_APP_CONTEXT_ROOT = 80, - FILTER_OP_GET_PAYLOAD_ROOT = 81, - - FILTER_OP_GET_SYMBOL = 82, - FILTER_OP_GET_SYMBOL_FIELD = 83, - FILTER_OP_GET_INDEX_U16 = 84, - FILTER_OP_GET_INDEX_U64 = 85, - - FILTER_OP_LOAD_FIELD = 86, - FILTER_OP_LOAD_FIELD_S8 = 87, - FILTER_OP_LOAD_FIELD_S16 = 88, - FILTER_OP_LOAD_FIELD_S32 = 89, - FILTER_OP_LOAD_FIELD_S64 = 90, - FILTER_OP_LOAD_FIELD_U8 = 91, - FILTER_OP_LOAD_FIELD_U16 = 92, - FILTER_OP_LOAD_FIELD_U32 = 93, - FILTER_OP_LOAD_FIELD_U64 = 94, - FILTER_OP_LOAD_FIELD_STRING = 95, - FILTER_OP_LOAD_FIELD_SEQUENCE = 96, - FILTER_OP_LOAD_FIELD_DOUBLE = 97, - - FILTER_OP_UNARY_BIT_NOT = 98, - - FILTER_OP_RETURN_S64 = 99, - - 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. */ -} LTTNG_PACKED; - -struct binary_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct unary_op { - filter_opcode_t op; -} LTTNG_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 */ -} LTTNG_PACKED; - -struct cast_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct return_op { - filter_opcode_t op; -} LTTNG_PACKED; - -struct lttng_filter_bytecode_alloc { - uint32_t alloc_len; - struct lttng_filter_bytecode b; -}; - -static inline -unsigned int bytecode_get_len(struct lttng_filter_bytecode *bytecode) -{ - return bytecode->len; -} - -#endif /* _FILTER_BYTECODE_H */ diff --git a/src/lib/lttng-ctl/filter/filter-grammar-test.c b/src/lib/lttng-ctl/filter/filter-grammar-test.c deleted file mode 100644 index 70656e1a8..000000000 --- a/src/lib/lttng-ctl/filter/filter-grammar-test.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * filter-grammar-test.c - * - * LTTng filter grammar test - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-bytecode.h" - -int main(int argc, char **argv) -{ - struct filter_parser_ctx *ctx; - int ret; - int print_xml = 0, generate_ir = 0, generate_bytecode = 0, - print_bytecode = 0; - int i; - - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-p") == 0) - print_xml = 1; - else if (strcmp(argv[i], "-i") == 0) - generate_ir = 1; - else if (strcmp(argv[i], "-b") == 0) - generate_bytecode = 1; - else if (strcmp(argv[i], "-d") == 0) - filter_parser_debug = 1; - else if (strcmp(argv[i], "-B") == 0) - print_bytecode = 1; - } - - /* - * Force generate the bytecode if the user asks to print the bytecode - * (can't print it without generating it first). - */ - if (print_bytecode) { - generate_bytecode = 1; - } - - /* - * Force generate the IR if the user asks to generate the bytecode - * (the bytecode is generated by visiting the IR). - */ - if (generate_bytecode) { - generate_ir = 1; - } - - ctx = filter_parser_ctx_alloc(stdin); - if (!ctx) { - fprintf(stderr, "Error allocating parser\n"); - goto alloc_error; - } - ret = filter_parser_ctx_append_ast(ctx); - if (ret) { - fprintf(stderr, "Parse error\n"); - goto parse_error; - } - if (print_xml) { - ret = filter_visitor_print_xml(ctx, stdout, 0); - if (ret) { - fflush(stdout); - fprintf(stderr, "XML print error\n"); - goto parse_error; - } - } - if (generate_ir) { - printf("Generating IR... "); - fflush(stdout); - ret = filter_visitor_ir_generate(ctx); - if (ret) { - fprintf(stderr, "Generate IR error\n"); - goto parse_error; - } - printf("done\n"); - - printf("Validating IR... "); - fflush(stdout); - ret = filter_visitor_ir_check_binary_op_nesting(ctx); - if (ret) { - goto parse_error; - } - printf("done\n"); - } - if (generate_bytecode) { - printf("Generating bytecode... "); - fflush(stdout); - ret = filter_visitor_bytecode_generate(ctx); - if (ret) { - fprintf(stderr, "Generate bytecode error\n"); - goto parse_error; - } - printf("done\n"); - printf("Size of bytecode generated: %u bytes.\n", - bytecode_get_len(&ctx->bytecode->b)); - } - - if (print_bytecode) { - unsigned int bytecode_len, len, i; - - len = bytecode_get_len(&ctx->bytecode->b); - bytecode_len = ctx->bytecode->b.reloc_table_offset; - printf("Bytecode:\n"); - for (i = 0; i < bytecode_len; i++) { - printf("0x%X ", - ((uint8_t *) ctx->bytecode->b.data)[i]); - } - printf("\n"); - printf("Reloc table:\n"); - for (i = bytecode_len; i < len;) { - printf("{ 0x%X, ", - *(uint16_t *) &ctx->bytecode->b.data[i]); - i += sizeof(uint16_t); - printf("%s } ", &((char *) ctx->bytecode->b.data)[i]); - i += strlen(&((char *) ctx->bytecode->b.data)[i]) + 1; - } - printf("\n"); - } - - filter_bytecode_free(ctx); - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); - return 0; - -parse_error: - filter_bytecode_free(ctx); - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); -alloc_error: - exit(EXIT_FAILURE); -} diff --git a/src/lib/lttng-ctl/filter/filter-ir.h b/src/lib/lttng-ctl/filter/filter-ir.h deleted file mode 100644 index d62c0ee0c..000000000 --- a/src/lib/lttng-ctl/filter/filter-ir.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef _FILTER_IR_H -#define _FILTER_IR_H - -/* - * filter-ir.h - * - * LTTng filter ir - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "filter-ast.h" - -enum ir_op_signedness { - IR_SIGN_UNKNOWN = 0, - IR_SIGNED, - IR_UNSIGNED, - IR_SIGN_DYN, /* signedness determined dynamically */ -}; - -enum ir_data_type { - IR_DATA_UNKNOWN = 0, - IR_DATA_STRING, - IR_DATA_NUMERIC, /* numeric and boolean */ - IR_DATA_FLOAT, - IR_DATA_FIELD_REF, - IR_DATA_GET_CONTEXT_REF, - IR_DATA_EXPRESSION, -}; - -enum ir_op_type { - IR_OP_UNKNOWN = 0, - IR_OP_ROOT, - IR_OP_LOAD, - IR_OP_UNARY, - IR_OP_BINARY, - IR_OP_LOGICAL, -}; - -/* left or right child */ -enum ir_side { - IR_SIDE_UNKNOWN = 0, - IR_LEFT, - IR_RIGHT, -}; - -enum ir_load_string_type { - /* Plain, no globbing at all: `hello world`. */ - IR_LOAD_STRING_TYPE_PLAIN = 0, - - /* Star at the end only: `hello *`. */ - IR_LOAD_STRING_TYPE_GLOB_STAR_END, - - /* At least one star, anywhere, but not at the end only: `he*wor*`. */ - IR_LOAD_STRING_TYPE_GLOB_STAR, -}; - -struct ir_op_root { - struct ir_op *child; -}; - -enum ir_load_expression_type { - IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT, - IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT, - IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT, - IR_LOAD_EXPRESSION_GET_SYMBOL, - IR_LOAD_EXPRESSION_GET_INDEX, - IR_LOAD_EXPRESSION_LOAD_FIELD, -}; - -struct ir_load_expression_op { - struct ir_load_expression_op *next; - enum ir_load_expression_type type; - union { - char *symbol; - uint64_t index; - } u; -}; - -struct ir_load_expression { - struct ir_load_expression_op *child; -}; - -struct ir_op_load { - union { - struct { - enum ir_load_string_type type; - char *value; - } string; - int64_t num; - double flt; - char *ref; - struct ir_load_expression *expression; - } u; -}; - -struct ir_op_unary { - enum unary_op_type type; - struct ir_op *child; -}; - -struct ir_op_binary { - enum op_type type; - struct ir_op *left; - struct ir_op *right; -}; - -struct ir_op_logical { - enum op_type type; - struct ir_op *left; - struct ir_op *right; -}; - -struct ir_op { - /* common to all ops */ - enum ir_op_type op; - enum ir_data_type data_type; - enum ir_op_signedness signedness; - enum ir_side side; - - union { - struct ir_op_root root; - struct ir_op_load load; - struct ir_op_unary unary; - struct ir_op_binary binary; - struct ir_op_logical logical; - } u; -}; - -#endif /* _FILTER_IR_H */ diff --git a/src/lib/lttng-ctl/filter/filter-lexer.l b/src/lib/lttng-ctl/filter/filter-lexer.l deleted file mode 100644 index cdb099af7..000000000 --- a/src/lib/lttng-ctl/filter/filter-lexer.l +++ /dev/null @@ -1,129 +0,0 @@ -%{ -/* - * filter-lexer.l - * - * LTTng filter lexer - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include "filter-ast.h" -#include "filter-parser.h" - -static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) - __attribute__((unused)); -static int input (yyscan_t yyscanner) __attribute__((unused)); - -%} - -%x comment_ml comment_sl string_lit char_const -%option reentrant yylineno noyywrap bison-bridge -%option extra-type="struct filter_parser_ctx *" - /* bison-locations */ - -D [0-9] -L [a-zA-Z_] -H [a-fA-F0-9] -E ([Ee][+-]?{D}+) -P ([Pp][+-]?{D}+) -FS (f|F|l|L) -IS ((u|U)|(u|U)?(l|L|ll|LL)|(l|L|ll|LL)(u|U)) - -INTEGER_SUFFIX [ \n\t]*(U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu) -DIGIT [0-9] -NONDIGIT [a-zA-Z_] -HEXDIGIT [0-9A-Fa-f] -OCTALDIGIT [0-7] -UCHARLOWERCASE \\u{HEXDIGIT}{4} -UCHARUPPERCASE \\U{HEXDIGIT}{8} -ID_EXTRA_CHAR (":") -ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}|{ID_EXTRA_CHAR} -IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})* -ESCSEQ \\(\'|\"|\?|\\|a|b|f|n|r|t|v|{OCTALDIGIT}{1,3}|u{HEXDIGIT}{4}|U{HEXDIGIT}{8}|x{HEXDIGIT}+) -%% - - /* - * Using start conditions to deal with comments - * and strings. - */ - -"/*" BEGIN(comment_ml); -[^*\n]* /* eat anything that's not a '*' */ -"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ -\n ++yylineno; -"*"+"/" BEGIN(INITIAL); - -"//" BEGIN(comment_sl); -[^\n]*\n ++yylineno; BEGIN(INITIAL); - -L\' BEGIN(char_const); return CHARACTER_CONSTANT_START; -\' BEGIN(char_const); return CHARACTER_CONSTANT_START; -\' BEGIN(INITIAL); return SQUOTE; - -L\" BEGIN(string_lit); return STRING_LITERAL_START; -\" BEGIN(string_lit); return STRING_LITERAL_START; -\" BEGIN(INITIAL); return DQUOTE; - -ESCSEQ return ESCSEQ; -\n ; /* ignore */ -. setstring(yyextra, yylval, yytext); return CHAR_STRING_TOKEN; - - -0[xX]{H}+{IS}? setstring(yyextra, yylval, yytext); return HEXADECIMAL_CONSTANT; -0[0-7]*{IS}? setstring(yyextra, yylval, yytext); return OCTAL_CONSTANT; -[1-9]{D}*{IS}? setstring(yyextra, yylval, yytext); return DECIMAL_CONSTANT; - -{D}+{E}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; -{D}*"."{D}+{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; -{D}+"."{D}*{E}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; -0[xX]{H}+{P}{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; -0[xX]{H}*"."{H}+{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; -0[xX]{H}+"."{H}*{P}?{FS}? setstring(yyextra, yylval, yytext); return FLOAT_CONSTANT; - -"[" return LSBRAC; -"]" return RSBRAC; -"(" return LPAREN; -")" return RPAREN; -"{" return LBRAC; -"}" return RBRAC; -"->" return RARROW; - -"*" return STAR; -"+" return PLUS; -"-" return MINUS; - -"%" return MOD_OP; -"/" return DIV_OP; -">>" return RIGHT_OP; -"<<" return LEFT_OP; - -"==" return EQ_OP; -"!=" return NE_OP; -"<=" return LE_OP; -">=" return GE_OP; -"<" return LT_OP; -">" return GT_OP; -"&&" return AND_OP; -"||" return OR_OP; -"!" return NOT_OP; - -":=" return ASSIGN; -":" return COLON; -";" return SEMICOLON; -"..." return DOTDOTDOT; -"." return DOT; -"=" return EQUAL; -"," return COMMA; -"^" return XOR_BIN; -"&" return AND_BIN; -"|" return OR_BIN; -"~" return NOT_BIN; -"$"{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); return GLOBAL_IDENTIFIER; -{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); return IDENTIFIER; -[ \t\n]+ ; /* ignore */ -. return ERROR; -%% diff --git a/src/lib/lttng-ctl/filter/filter-parser.y b/src/lib/lttng-ctl/filter/filter-parser.y deleted file mode 100644 index ba2d3007b..000000000 --- a/src/lib/lttng-ctl/filter/filter-parser.y +++ /dev/null @@ -1,679 +0,0 @@ -%{ -/* - * filter-parser.y - * - * LTTng filter expression parser - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - * Grammar inspired from http://www.quut.com/c/ANSI-C-grammar-y.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" - -#include - -#define WIDTH_u64_SCANF_IS_A_BROKEN_API "20" -#define WIDTH_o64_SCANF_IS_A_BROKEN_API "22" -#define WIDTH_x64_SCANF_IS_A_BROKEN_API "17" -#define WIDTH_lg_SCANF_IS_A_BROKEN_API "4096" /* Hugely optimistic approximation */ - -LTTNG_HIDDEN -int yydebug; -LTTNG_HIDDEN -int filter_parser_debug = 0; - -LTTNG_HIDDEN -int yyparse(struct filter_parser_ctx *parser_ctx, yyscan_t scanner); -LTTNG_HIDDEN -int yylex(union YYSTYPE *yyval, yyscan_t scanner); -LTTNG_HIDDEN -int yylex_init_extra(struct filter_parser_ctx *parser_ctx, yyscan_t * ptr_yy_globals); -LTTNG_HIDDEN -int yylex_destroy(yyscan_t yyparser_ctx); -LTTNG_HIDDEN -void yyrestart(FILE * in_str, yyscan_t parser_ctx); - -struct gc_string { - struct cds_list_head gc; - size_t alloclen; - char s[]; -}; - -static const char *node_type_to_str[] = { - [ NODE_UNKNOWN ] = "NODE_UNKNOWN", - [ NODE_ROOT ] = "NODE_ROOT", - [ NODE_EXPRESSION ] = "NODE_EXPRESSION", - [ NODE_OP ] = "NODE_OP", - [ NODE_UNARY_OP ] = "NODE_UNARY_OP", -}; - -LTTNG_HIDDEN -const char *node_type(struct filter_node *node) -{ - if (node->type < NR_NODE_TYPES) - return node_type_to_str[node->type]; - else - return NULL; -} - -static struct gc_string *gc_string_alloc(struct filter_parser_ctx *parser_ctx, - size_t len) -{ - struct gc_string *gstr; - size_t alloclen; - - /* TODO: could be faster with find first bit or glib Gstring */ - /* sizeof long to account for malloc header (int or long ?) */ - for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + len; - alloclen *= 2); - - gstr = zmalloc(alloclen); - if (!gstr) { - goto end; - } - cds_list_add(&gstr->gc, &parser_ctx->allocated_strings); - gstr->alloclen = alloclen; -end: - return gstr; -} - -/* - * note: never use gc_string_append on a string that has external references. - * gsrc will be garbage collected immediately, and gstr might be. - * Should only be used to append characters to a string literal or constant. - */ -static -struct gc_string *gc_string_append(struct filter_parser_ctx *parser_ctx, - struct gc_string *gstr, - struct gc_string *gsrc) -{ - size_t newlen = strlen(gsrc->s) + strlen(gstr->s) + 1; - size_t alloclen; - - /* TODO: could be faster with find first bit or glib Gstring */ - /* sizeof long to account for malloc header (int or long ?) */ - for (alloclen = 8; alloclen < sizeof(long) + sizeof(*gstr) + newlen; - alloclen *= 2); - - if (alloclen > gstr->alloclen) { - struct gc_string *newgstr; - - newgstr = gc_string_alloc(parser_ctx, newlen); - strcpy(newgstr->s, gstr->s); - strcat(newgstr->s, gsrc->s); - cds_list_del(&gstr->gc); - free(gstr); - gstr = newgstr; - } else { - strcat(gstr->s, gsrc->s); - } - cds_list_del(&gsrc->gc); - free(gsrc); - return gstr; -} - -LTTNG_HIDDEN -void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src) -{ - lvalp->gs = gc_string_alloc(parser_ctx, strlen(src) + 1); - strcpy(lvalp->gs->s, src); -} - -static struct filter_node *make_node(struct filter_parser_ctx *scanner, - enum node_type type) -{ - struct filter_ast *ast = filter_parser_get_ast(scanner); - struct filter_node *node; - - node = zmalloc(sizeof(*node)); - if (!node) - return NULL; - memset(node, 0, sizeof(*node)); - node->type = type; - cds_list_add(&node->gc, &ast->allocated_nodes); - - switch (type) { - case NODE_ROOT: - fprintf(stderr, "[error] %s: trying to create root node\n", __func__); - break; - - case NODE_EXPRESSION: - break; - case NODE_OP: - break; - case NODE_UNARY_OP: - break; - - case NODE_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown node type %d\n", __func__, - (int) type); - break; - } - - return node; -} - -static struct filter_node *make_op_node(struct filter_parser_ctx *scanner, - enum op_type type, - struct filter_node *lchild, - struct filter_node *rchild) -{ - struct filter_ast *ast = filter_parser_get_ast(scanner); - struct filter_node *node; - - node = zmalloc(sizeof(*node)); - if (!node) - return NULL; - memset(node, 0, sizeof(*node)); - node->type = NODE_OP; - cds_list_add(&node->gc, &ast->allocated_nodes); - node->u.op.type = type; - node->u.op.lchild = lchild; - node->u.op.rchild = rchild; - return node; -} - -static -void yyerror(struct filter_parser_ctx *parser_ctx, yyscan_t scanner, const char *str) -{ - fprintf(stderr, "error %s\n", str); -} - -#define parse_error(parser_ctx, str) \ -do { \ - yyerror(parser_ctx, parser_ctx->scanner, YY_("parse error: " str "\n")); \ - YYERROR; \ -} while (0) - -static void free_strings(struct cds_list_head *list) -{ - struct gc_string *gstr, *tmp; - - cds_list_for_each_entry_safe(gstr, tmp, list, gc) - free(gstr); -} - -static struct filter_ast *filter_ast_alloc(void) -{ - struct filter_ast *ast; - - ast = zmalloc(sizeof(*ast)); - if (!ast) - return NULL; - memset(ast, 0, sizeof(*ast)); - CDS_INIT_LIST_HEAD(&ast->allocated_nodes); - ast->root.type = NODE_ROOT; - return ast; -} - -static void filter_ast_free(struct filter_ast *ast) -{ - struct filter_node *node, *tmp; - - cds_list_for_each_entry_safe(node, tmp, &ast->allocated_nodes, gc) - free(node); - free(ast); -} - -LTTNG_HIDDEN -int filter_parser_ctx_append_ast(struct filter_parser_ctx *parser_ctx) -{ - return yyparse(parser_ctx, parser_ctx->scanner); -} - -LTTNG_HIDDEN -struct filter_parser_ctx *filter_parser_ctx_alloc(FILE *input) -{ - struct filter_parser_ctx *parser_ctx; - int ret; - - yydebug = filter_parser_debug; - - parser_ctx = zmalloc(sizeof(*parser_ctx)); - if (!parser_ctx) - return NULL; - memset(parser_ctx, 0, sizeof(*parser_ctx)); - - ret = yylex_init_extra(parser_ctx, &parser_ctx->scanner); - if (ret) { - fprintf(stderr, "yylex_init error\n"); - goto cleanup_parser_ctx; - } - /* Start processing new stream */ - yyrestart(input, parser_ctx->scanner); - - parser_ctx->ast = filter_ast_alloc(); - if (!parser_ctx->ast) - goto cleanup_lexer; - CDS_INIT_LIST_HEAD(&parser_ctx->allocated_strings); - - if (yydebug) - fprintf(stdout, "parser_ctx input is a%s.\n", - isatty(fileno(input)) ? "n interactive tty" : - " noninteractive file"); - - return parser_ctx; - -cleanup_lexer: - ret = yylex_destroy(parser_ctx->scanner); - if (!ret) - fprintf(stderr, "yylex_destroy error\n"); -cleanup_parser_ctx: - free(parser_ctx); - return NULL; -} - -LTTNG_HIDDEN -void filter_parser_ctx_free(struct filter_parser_ctx *parser_ctx) -{ - int ret; - - free_strings(&parser_ctx->allocated_strings); - filter_ast_free(parser_ctx->ast); - ret = yylex_destroy(parser_ctx->scanner); - if (ret) - fprintf(stderr, "yylex_destroy error\n"); - free(parser_ctx); -} - -%} - -%code provides -{ -#include "common/macros.h" - -LTTNG_HIDDEN -void setstring(struct filter_parser_ctx *parser_ctx, YYSTYPE *lvalp, const char *src); -} - -%define api.pure - /* %locations */ -%parse-param {struct filter_parser_ctx *parser_ctx} -%parse-param {yyscan_t scanner} -%lex-param {yyscan_t scanner} -%start translation_unit -%token CHARACTER_CONSTANT_START SQUOTE STRING_LITERAL_START DQUOTE -%token ESCSEQ CHAR_STRING_TOKEN -%token DECIMAL_CONSTANT OCTAL_CONSTANT HEXADECIMAL_CONSTANT FLOAT_CONSTANT -%token LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW -%token STAR PLUS MINUS -%token MOD_OP DIV_OP RIGHT_OP LEFT_OP -%token EQ_OP NE_OP LE_OP GE_OP LT_OP GT_OP AND_OP OR_OP NOT_OP -%token ASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA -%token XOR_BIN AND_BIN OR_BIN NOT_BIN - -%token IDENTIFIER GLOBAL_IDENTIFIER -%token ERROR -%union -{ - long long ll; - char c; - struct gc_string *gs; - struct filter_node *n; -} - -%type s_char s_char_sequence c_char c_char_sequence - -%type primary_expression -%type prefix_expression -%type prefix_expression_rec -%type postfix_expression -%type unary_expression -%type unary_operator -%type multiplicative_expression -%type additive_expression -%type shift_expression -%type relational_expression -%type equality_expression -%type and_expression -%type exclusive_or_expression -%type inclusive_or_expression -%type logical_and_expression -%type logical_or_expression -%type expression -%type identifiers - -%% - - -/* 1.5 Constants */ - -c_char_sequence: - c_char - { $$ = $1; } - | c_char_sequence c_char - { $$ = gc_string_append(parser_ctx, $1, $2); } - ; - -c_char: - CHAR_STRING_TOKEN - { $$ = yylval.gs; } - | ESCSEQ - { - parse_error(parser_ctx, "escape sequences not supported yet"); - } - ; - -/* 1.6 String literals */ - -s_char_sequence: - s_char - { $$ = $1; } - | s_char_sequence s_char - { $$ = gc_string_append(parser_ctx, $1, $2); } - ; - -s_char: - CHAR_STRING_TOKEN - { $$ = yylval.gs; } - | ESCSEQ - { - parse_error(parser_ctx, "escape sequences not supported yet"); - } - ; - -primary_expression: - DECIMAL_CONSTANT - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_CONSTANT; - if (sscanf(yylval.gs->s, "%" WIDTH_u64_SCANF_IS_A_BROKEN_API SCNu64, - &$$->u.expression.u.constant) != 1) { - parse_error(parser_ctx, "cannot scanf decimal constant"); - } - } - | OCTAL_CONSTANT - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_CONSTANT; - if (!strcmp(yylval.gs->s, "0")) { - $$->u.expression.u.constant = 0; - } else if (sscanf(yylval.gs->s, "0%" WIDTH_o64_SCANF_IS_A_BROKEN_API SCNo64, - &$$->u.expression.u.constant) != 1) { - parse_error(parser_ctx, "cannot scanf octal constant"); - } - } - | HEXADECIMAL_CONSTANT - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_CONSTANT; - if (sscanf(yylval.gs->s, "0x%" WIDTH_x64_SCANF_IS_A_BROKEN_API SCNx64, - &$$->u.expression.u.constant) != 1) { - parse_error(parser_ctx, "cannot scanf hexadecimal constant"); - } - } - | FLOAT_CONSTANT - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_FLOAT_CONSTANT; - if (sscanf(yylval.gs->s, "%" WIDTH_lg_SCANF_IS_A_BROKEN_API "lg", - &$$->u.expression.u.float_constant) != 1) { - parse_error(parser_ctx, "cannot scanf float constant"); - } - } - | STRING_LITERAL_START DQUOTE - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_STRING; - $$->u.expression.u.string = ""; - } - | STRING_LITERAL_START s_char_sequence DQUOTE - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_STRING; - $$->u.expression.u.string = $2->s; - } - | CHARACTER_CONSTANT_START c_char_sequence SQUOTE - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_STRING; - $$->u.expression.u.string = $2->s; - } - | LPAREN expression RPAREN - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_NESTED; - $$->u.expression.u.child = $2; - } - ; - -identifiers - : IDENTIFIER - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_IDENTIFIER; - $$->u.expression.u.identifier = yylval.gs->s; - } - | GLOBAL_IDENTIFIER - { - $$ = make_node(parser_ctx, NODE_EXPRESSION); - $$->u.expression.type = AST_EXP_GLOBAL_IDENTIFIER; - $$->u.expression.u.identifier = yylval.gs->s; - } - ; - -prefix_expression_rec - : LSBRAC unary_expression RSBRAC - { - $$ = $2; - } - | LSBRAC unary_expression RSBRAC prefix_expression_rec - { - $$ = $2; - $$->u.expression.pre_op = AST_LINK_BRACKET; - $$->u.expression.prev = $4; - } - ; - -prefix_expression - : identifiers - { - $$ = $1; - } - | identifiers prefix_expression_rec - { - $$ = $1; - $$->u.expression.pre_op = AST_LINK_BRACKET; - $$->u.expression.next_bracket = $2; - } - ; - -postfix_expression - : prefix_expression - { - $$ = $1; - } - | postfix_expression DOT prefix_expression - { - $$ = $3; - $$->u.expression.post_op = AST_LINK_DOT; - $$->u.expression.prev = $1; - } - | postfix_expression RARROW prefix_expression - { - $$ = $3; - $$->u.expression.post_op = AST_LINK_RARROW; - $$->u.expression.prev = $1; - } - ; - -unary_expression - : postfix_expression - { $$ = $1; } - | primary_expression - { $$ = $1; } - | unary_operator unary_expression - { - $$ = $1; - $$->u.unary_op.child = $2; - } - ; - -unary_operator - : PLUS - { - $$ = make_node(parser_ctx, NODE_UNARY_OP); - $$->u.unary_op.type = AST_UNARY_PLUS; - } - | MINUS - { - $$ = make_node(parser_ctx, NODE_UNARY_OP); - $$->u.unary_op.type = AST_UNARY_MINUS; - } - | NOT_OP - { - $$ = make_node(parser_ctx, NODE_UNARY_OP); - $$->u.unary_op.type = AST_UNARY_NOT; - } - | NOT_BIN - { - $$ = make_node(parser_ctx, NODE_UNARY_OP); - $$->u.unary_op.type = AST_UNARY_BIT_NOT; - } - ; - -multiplicative_expression - : unary_expression - { $$ = $1; } - | multiplicative_expression STAR unary_expression - { - $$ = make_op_node(parser_ctx, AST_OP_MUL, $1, $3); - } - | multiplicative_expression DIV_OP unary_expression - { - $$ = make_op_node(parser_ctx, AST_OP_DIV, $1, $3); - } - | multiplicative_expression MOD_OP unary_expression - { - $$ = make_op_node(parser_ctx, AST_OP_MOD, $1, $3); - } - ; - -additive_expression - : multiplicative_expression - { $$ = $1; } - | additive_expression PLUS multiplicative_expression - { - $$ = make_op_node(parser_ctx, AST_OP_PLUS, $1, $3); - } - | additive_expression MINUS multiplicative_expression - { - $$ = make_op_node(parser_ctx, AST_OP_MINUS, $1, $3); - } - ; - -shift_expression - : additive_expression - { $$ = $1; } - | shift_expression LEFT_OP additive_expression - { - $$ = make_op_node(parser_ctx, AST_OP_BIT_LSHIFT, $1, $3); - } - | shift_expression RIGHT_OP additive_expression - { - $$ = make_op_node(parser_ctx, AST_OP_BIT_RSHIFT, $1, $3); - } - ; - -and_expression - : shift_expression - { $$ = $1; } - | and_expression AND_BIN shift_expression - { - $$ = make_op_node(parser_ctx, AST_OP_BIT_AND, $1, $3); - } - ; - -exclusive_or_expression - : and_expression - { $$ = $1; } - | exclusive_or_expression XOR_BIN and_expression - { - $$ = make_op_node(parser_ctx, AST_OP_BIT_XOR, $1, $3); - } - ; - -inclusive_or_expression - : exclusive_or_expression - { $$ = $1; } - | inclusive_or_expression OR_BIN exclusive_or_expression - { - $$ = make_op_node(parser_ctx, AST_OP_BIT_OR, $1, $3); - } - ; - -relational_expression - : inclusive_or_expression - { $$ = $1; } - | relational_expression LT_OP inclusive_or_expression - { - $$ = make_op_node(parser_ctx, AST_OP_LT, $1, $3); - } - | relational_expression GT_OP inclusive_or_expression - { - $$ = make_op_node(parser_ctx, AST_OP_GT, $1, $3); - } - | relational_expression LE_OP inclusive_or_expression - { - $$ = make_op_node(parser_ctx, AST_OP_LE, $1, $3); - } - | relational_expression GE_OP inclusive_or_expression - { - $$ = make_op_node(parser_ctx, AST_OP_GE, $1, $3); - } - ; - -equality_expression - : relational_expression - { $$ = $1; } - | equality_expression EQ_OP relational_expression - { - $$ = make_op_node(parser_ctx, AST_OP_EQ, $1, $3); - } - | equality_expression NE_OP relational_expression - { - $$ = make_op_node(parser_ctx, AST_OP_NE, $1, $3); - } - ; - -logical_and_expression - : equality_expression - { $$ = $1; } - | logical_and_expression AND_OP equality_expression - { - $$ = make_op_node(parser_ctx, AST_OP_AND, $1, $3); - } - ; - -logical_or_expression - : logical_and_expression - { $$ = $1; } - | logical_or_expression OR_OP logical_and_expression - { - $$ = make_op_node(parser_ctx, AST_OP_OR, $1, $3); - } - ; - -expression - : logical_or_expression - { $$ = $1; } - ; - -translation_unit - : expression - { - parser_ctx->ast->root.u.root.child = $1; - } - ; diff --git a/src/lib/lttng-ctl/filter/filter-symbols.h b/src/lib/lttng-ctl/filter/filter-symbols.h deleted file mode 100644 index 031776da0..000000000 --- a/src/lib/lttng-ctl/filter/filter-symbols.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _FILTER_SYMBOLS_H -#define _FILTER_SYMBOLS_H - -/* - * filter-symbols.h - * - * LTTng filter flex/bison symbol prefixes - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define yy_create_buffer lttng_yy_create_buffer -#define yy_delete_buffer lttng_yy_delete_buffer -#define yy_flush_buffer lttng_yy_flush_buffer -#define yy_scan_buffer lttng_yy_scan_buffer -#define yy_scan_bytes lttng_yy_scan_bytes -#define yy_scan_string lttng_yy_scan_string -#define yy_switch_to_buffer lttng_yy_switch_to_buffer -#define yyalloc lttng_yyalloc -#define yyfree lttng_yyfree -#define yyget_column lttng_yyget_column -#define yyget_debug lttng_yyget_debug -#define yyget_extra lttng_yyget_extra -#define yyget_in lttng_yyget_in -#define yyget_leng lttng_yyget_leng -#define yyget_lineno lttng_yyget_lineno -#define yyget_lval lttng_yyget_lval -#define yyget_out lttng_yyget_out -#define yyget_text lttng_yyget_text -#define yylex_init lttng_yylex_init -#define yypop_buffer_state lttng_yypop_buffer_state -#define yypush_buffer_state lttng_yypush_buffer_state -#define yyrealloc lttng_yyrealloc -#define yyset_column lttng_yyset_column -#define yyset_debug lttng_yyset_debug -#define yyset_extra lttng_yyset_extra -#define yyset_in lttng_yyset_in -#define yyset_lineno lttng_yyset_lineno -#define yyset_lval lttng_yyset_lval -#define yyset_out lttng_yyset_out - -#endif /* _FILTER_SYMBOLS_H */ diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c b/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c deleted file mode 100644 index 699273c3d..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * filter-visitor-generate-bytecode.c - * - * LTTng filter bytecode generation - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include - -#include "filter-bytecode.h" -#include "filter-ir.h" -#include "filter-ast.h" - -#include - -#ifndef max_t -#define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b))) -#endif - -#define INIT_ALLOC_SIZE 4 - -static -int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx, - struct ir_op *node); - -static inline int get_count_order(unsigned int count) -{ - int order; - - order = lttng_fls(count) - 1; - if (count & (count - 1)) - order++; - return order; -} - -static -int bytecode_init(struct lttng_filter_bytecode_alloc **fb) -{ - uint32_t alloc_len; - - alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + INIT_ALLOC_SIZE; - *fb = calloc(alloc_len, 1); - if (!*fb) { - return -ENOMEM; - } else { - (*fb)->alloc_len = alloc_len; - return 0; - } -} - -static -int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align, uint32_t len) -{ - int32_t ret; - uint32_t padding = offset_align((*fb)->b.len, align); - uint32_t new_len = (*fb)->b.len + padding + len; - uint32_t new_alloc_len = sizeof(struct lttng_filter_bytecode_alloc) + new_len; - uint32_t old_alloc_len = (*fb)->alloc_len; - - if (new_len > LTTNG_FILTER_MAX_LEN) - return -EINVAL; - - if (new_alloc_len > old_alloc_len) { - struct lttng_filter_bytecode_alloc *newptr; - - new_alloc_len = - max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); - newptr = realloc(*fb, new_alloc_len); - if (!newptr) - return -ENOMEM; - *fb = newptr; - /* We zero directly the memory from start of allocation. */ - memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len); - (*fb)->alloc_len = new_alloc_len; - } - (*fb)->b.len += padding; - ret = (*fb)->b.len; - (*fb)->b.len += len; - return ret; -} - -static -int bytecode_push(struct lttng_filter_bytecode_alloc **fb, const void *data, - uint32_t align, uint32_t len) -{ - int32_t offset; - - offset = bytecode_reserve(fb, align, len); - if (offset < 0) - return offset; - memcpy(&(*fb)->b.data[offset], data, len); - return 0; -} - -static -int bytecode_push_logical(struct lttng_filter_bytecode_alloc **fb, - struct logical_op *data, - uint32_t align, uint32_t len, - uint16_t *skip_offset) -{ - int32_t offset; - - offset = bytecode_reserve(fb, align, len); - if (offset < 0) - return offset; - memcpy(&(*fb)->b.data[offset], data, len); - *skip_offset = - (void *) &((struct logical_op *) &(*fb)->b.data[offset])->skip_offset - - (void *) &(*fb)->b.data[0]; - return 0; -} - -static -int bytecode_patch(struct lttng_filter_bytecode_alloc **fb, - const void *data, - uint16_t offset, - uint32_t len) -{ - if (offset >= (*fb)->b.len) { - return -EINVAL; - } - memcpy(&(*fb)->b.data[offset], data, len); - return 0; -} - -static -int visit_node_root(struct filter_parser_ctx *ctx, struct ir_op *node) -{ - int ret; - struct return_op insn; - - /* Visit child */ - ret = recursive_visit_gen_bytecode(ctx, node->u.root.child); - if (ret) - return ret; - - /* Generate end of bytecode instruction */ - insn.op = FILTER_OP_RETURN; - 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) -{ - int ret; - - switch (node->data_type) { - case IR_DATA_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown data type in %s\n", - __func__); - return -EINVAL; - - case IR_DATA_STRING: - { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op) - + strlen(node->u.load.u.string.value) + 1; - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - - 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; - } - case IR_DATA_NUMERIC: - { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op) - + sizeof(struct literal_numeric); - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_LOAD_S64; - memcpy(insn->data, &node->u.load.u.num, sizeof(int64_t)); - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); - return ret; - } - case IR_DATA_FLOAT: - { - struct load_op *insn; - uint32_t insn_len = sizeof(struct load_op) - + sizeof(struct literal_double); - - insn = calloc(insn_len, 1); - if (!insn) - return -ENOMEM; - insn->op = FILTER_OP_LOAD_DOUBLE; - memcpy(insn->data, &node->u.load.u.flt, sizeof(double)); - ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); - free(insn); - return ret; - } - case IR_DATA_EXPRESSION: - return visit_node_load_expression(ctx, node); - } -} - -static -int visit_node_unary(struct filter_parser_ctx *ctx, struct ir_op *node) -{ - int ret; - struct unary_op insn; - - /* Visit child */ - ret = recursive_visit_gen_bytecode(ctx, node->u.unary.child); - if (ret) - return ret; - - /* Generate end of bytecode instruction */ - switch (node->u.unary.type) { - case AST_UNARY_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown unary node type in %s\n", - __func__); - return -EINVAL; - case AST_UNARY_PLUS: - /* Nothing to do. */ - return 0; - case AST_UNARY_MINUS: - insn.op = FILTER_OP_UNARY_MINUS; - return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); - 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)); - } -} - -/* - * Binary comparator nesting is disallowed. This allows fitting into - * only 2 registers. - */ -static -int visit_node_binary(struct filter_parser_ctx *ctx, struct ir_op *node) -{ - int ret; - struct binary_op insn; - - /* Visit child */ - ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left); - if (ret) - return ret; - ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right); - if (ret) - return ret; - - switch (node->u.binary.type) { - case AST_OP_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown unary node type in %s\n", - __func__); - return -EINVAL; - - case AST_OP_AND: - case AST_OP_OR: - fprintf(stderr, "[error] Unexpected logical node type in %s\n", - __func__); - return -EINVAL; - - case AST_OP_MUL: - insn.op = FILTER_OP_MUL; - break; - case AST_OP_DIV: - insn.op = FILTER_OP_DIV; - break; - case AST_OP_MOD: - insn.op = FILTER_OP_MOD; - break; - case AST_OP_PLUS: - insn.op = FILTER_OP_PLUS; - break; - case AST_OP_MINUS: - insn.op = FILTER_OP_MINUS; - break; - case AST_OP_BIT_RSHIFT: - insn.op = FILTER_OP_BIT_RSHIFT; - break; - case AST_OP_BIT_LSHIFT: - insn.op = FILTER_OP_BIT_LSHIFT; - break; - case AST_OP_BIT_AND: - insn.op = FILTER_OP_BIT_AND; - break; - case AST_OP_BIT_OR: - insn.op = FILTER_OP_BIT_OR; - break; - case AST_OP_BIT_XOR: - insn.op = FILTER_OP_BIT_XOR; - break; - - case AST_OP_EQ: - insn.op = FILTER_OP_EQ; - break; - case AST_OP_NE: - insn.op = FILTER_OP_NE; - break; - case AST_OP_GT: - insn.op = FILTER_OP_GT; - break; - case AST_OP_LT: - insn.op = FILTER_OP_LT; - break; - case AST_OP_GE: - insn.op = FILTER_OP_GE; - break; - case AST_OP_LE: - insn.op = FILTER_OP_LE; - break; - } - return bytecode_push(&ctx->bytecode, &insn, 1, sizeof(insn)); -} - -/* - * A logical op always return a s64 (1 or 0). - */ -static -int visit_node_logical(struct filter_parser_ctx *ctx, struct ir_op *node) -{ - int ret; - struct logical_op insn; - uint16_t skip_offset_loc; - uint16_t target_loc; - - /* Visit left child */ - ret = recursive_visit_gen_bytecode(ctx, node->u.binary.left); - if (ret) - 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_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_EXPRESSION) { - cast_insn.op = FILTER_OP_CAST_TO_S64; - } else { - cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; - } - ret = bytecode_push(&ctx->bytecode, &cast_insn, - 1, sizeof(cast_insn)); - if (ret) - return ret; - } - switch (node->u.logical.type) { - default: - fprintf(stderr, "[error] Unknown node type in %s\n", - __func__); - return -EINVAL; - - case AST_OP_AND: - insn.op = FILTER_OP_AND; - break; - case AST_OP_OR: - insn.op = FILTER_OP_OR; - break; - } - insn.skip_offset = (uint16_t) -1UL; /* Temporary */ - ret = bytecode_push_logical(&ctx->bytecode, &insn, 1, sizeof(insn), - &skip_offset_loc); - if (ret) - return ret; - /* Visit right child */ - ret = recursive_visit_gen_bytecode(ctx, node->u.binary.right); - if (ret) - 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_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_EXPRESSION) { - cast_insn.op = FILTER_OP_CAST_TO_S64; - } else { - cast_insn.op = FILTER_OP_CAST_DOUBLE_TO_S64; - } - ret = bytecode_push(&ctx->bytecode, &cast_insn, - 1, sizeof(cast_insn)); - if (ret) - return ret; - } - /* We now know where the logical op can skip. */ - target_loc = (uint16_t) bytecode_get_len(&ctx->bytecode->b); - ret = bytecode_patch(&ctx->bytecode, - &target_loc, /* Offset to jump to */ - skip_offset_loc, /* Where to patch */ - sizeof(uint16_t)); - return ret; -} - -/* - * Postorder traversal of the tree. We need the children result before - * we can evaluate the parent. - */ -static -int recursive_visit_gen_bytecode(struct filter_parser_ctx *ctx, - struct ir_op *node) -{ - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown node type in %s\n", - __func__); - return -EINVAL; - - case IR_OP_ROOT: - return visit_node_root(ctx, node); - case IR_OP_LOAD: - return visit_node_load(ctx, node); - case IR_OP_UNARY: - return visit_node_unary(ctx, node); - case IR_OP_BINARY: - return visit_node_binary(ctx, node); - case IR_OP_LOGICAL: - return visit_node_logical(ctx, node); - } -} - -LTTNG_HIDDEN -void filter_bytecode_free(struct filter_parser_ctx *ctx) -{ - if (!ctx) { - return; - } - - if (ctx->bytecode) { - free(ctx->bytecode); - ctx->bytecode = NULL; - } - - if (ctx->bytecode_reloc) { - free(ctx->bytecode_reloc); - ctx->bytecode_reloc = NULL; - } -} - -LTTNG_HIDDEN -int filter_visitor_bytecode_generate(struct filter_parser_ctx *ctx) -{ - int ret; - - ret = bytecode_init(&ctx->bytecode); - if (ret) - return ret; - ret = bytecode_init(&ctx->bytecode_reloc); - if (ret) - goto error; - ret = recursive_visit_gen_bytecode(ctx, ctx->ir_root); - if (ret) - goto error; - - /* Finally, append symbol table to bytecode */ - ctx->bytecode->b.reloc_table_offset = bytecode_get_len(&ctx->bytecode->b); - return bytecode_push(&ctx->bytecode, ctx->bytecode_reloc->b.data, - 1, bytecode_get_len(&ctx->bytecode_reloc->b)); - -error: - filter_bytecode_free(ctx); - return ret; -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c b/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c deleted file mode 100644 index 11c6b610c..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-generate-ir.c +++ /dev/null @@ -1,909 +0,0 @@ -/* - * filter-visitor-generate-ir.c - * - * LTTng filter generate intermediate representation - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -#include -#include - -static -struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx, - struct filter_node *node, enum ir_side side); - -static -struct ir_op *make_op_root(struct ir_op *child, enum ir_side side) -{ - struct ir_op *op; - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - switch (child->data_type) { - case IR_DATA_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown root child data type\n"); - free(op); - return NULL; - case IR_DATA_STRING: - fprintf(stderr, "[error] String cannot be root data type\n"); - free(op); - return NULL; - case IR_DATA_NUMERIC: - case IR_DATA_FIELD_REF: - case IR_DATA_GET_CONTEXT_REF: - case IR_DATA_EXPRESSION: - /* ok */ - break; - } - op->op = IR_OP_ROOT; - op->side = side; - op->data_type = child->data_type; - op->signedness = child->signedness; - op->u.root.child = child; - return op; -} - -static -enum ir_load_string_type get_literal_string_type(const char *string) -{ - assert(string); - - if (strutils_is_star_glob_pattern(string)) { - if (strutils_is_star_at_the_end_only_glob_pattern(string)) { - return IR_LOAD_STRING_TYPE_GLOB_STAR_END; - } - - return IR_LOAD_STRING_TYPE_GLOB_STAR; - } - - return IR_LOAD_STRING_TYPE_PLAIN; -} - -static -struct ir_op *make_op_load_string(const char *string, enum ir_side side) -{ - struct ir_op *op; - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_LOAD; - op->data_type = IR_DATA_STRING; - op->signedness = IR_SIGN_UNKNOWN; - op->side = side; - op->u.load.u.string.type = get_literal_string_type(string); - op->u.load.u.string.value = strdup(string); - if (!op->u.load.u.string.value) { - free(op); - return NULL; - } - return op; -} - -static -struct ir_op *make_op_load_numeric(int64_t v, enum ir_side side) -{ - struct ir_op *op; - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_LOAD; - op->data_type = IR_DATA_NUMERIC; - /* TODO: for now, all numeric values are signed */ - op->signedness = IR_SIGNED; - op->side = side; - op->u.load.u.num = v; - return op; -} - -static -struct ir_op *make_op_load_float(double v, enum ir_side side) -{ - struct ir_op *op; - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_LOAD; - op->data_type = IR_DATA_FLOAT; - op->signedness = IR_SIGN_UNKNOWN; - op->side = side; - op->u.load.u.flt = v; - return op; -} - -static -void free_load_expression(struct ir_load_expression *load_expression) -{ - struct ir_load_expression_op *exp_op; - - if (!load_expression) - return; - exp_op = load_expression->child; - for (;;) { - struct ir_load_expression_op *prev_exp_op; - - if (!exp_op) - break; - switch (exp_op->type) { - case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT: - case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT: - case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT: - case IR_LOAD_EXPRESSION_GET_INDEX: - case IR_LOAD_EXPRESSION_LOAD_FIELD: - break; - case IR_LOAD_EXPRESSION_GET_SYMBOL: - free(exp_op->u.symbol); - break; - } - prev_exp_op = exp_op; - exp_op = exp_op->next; - free(prev_exp_op); - } - free(load_expression); -} - -/* - * Returns the first node of the chain, after initializing the next - * pointers. - */ -static -struct filter_node *load_expression_get_forward_chain(struct filter_node *node) -{ - struct filter_node *prev_node; - - for (;;) { - assert(node->type == NODE_EXPRESSION); - prev_node = node; - node = node->u.expression.prev; - if (!node) { - break; - } - node->u.expression.next = prev_node; - } - return prev_node; -} - -static -struct ir_load_expression *create_load_expression(struct filter_node *node) -{ - struct ir_load_expression *load_exp; - struct ir_load_expression_op *load_exp_op, *prev_op; - const char *str; - - /* Get forward chain. */ - node = load_expression_get_forward_chain(node); - if (!node) - return NULL; - load_exp = calloc(sizeof(struct ir_load_expression), 1); - if (!load_exp) - return NULL; - - /* Root */ - load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); - if (!load_exp_op) - goto error; - load_exp->child = load_exp_op; - str = node->u.expression.u.string; - if (!strcmp(str, "$ctx")) { - load_exp_op->type = IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT; - node = node->u.expression.next; - if (!node) { - fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str); - goto error; - } - str = node->u.expression.u.string; - } else if (!strcmp(str, "$app")) { - load_exp_op->type = IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT; - node = node->u.expression.next; - if (!node) { - fprintf(stderr, "[error] Expecting identifier after \'%s\'\n", str); - goto error; - } - str = node->u.expression.u.string; - } else if (str[0] == '$') { - fprintf(stderr, "[error] Unexpected identifier \'%s\'\n", str); - goto error; - } else { - load_exp_op->type = IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT; - } - - for (;;) { - struct filter_node *bracket_node; - - prev_op = load_exp_op; - load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); - if (!load_exp_op) - goto error; - prev_op->next = load_exp_op; - load_exp_op->type = IR_LOAD_EXPRESSION_GET_SYMBOL; - load_exp_op->u.symbol = strdup(str); - if (!load_exp_op->u.symbol) - goto error; - - /* Explore brackets from current node. */ - for (bracket_node = node->u.expression.next_bracket; - bracket_node != NULL; - bracket_node = bracket_node->u.expression.next_bracket) { - prev_op = load_exp_op; - load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); - if (!load_exp_op) - goto error; - prev_op->next = load_exp_op; - load_exp_op->type = IR_LOAD_EXPRESSION_GET_INDEX; - load_exp_op->u.index = bracket_node->u.expression.u.constant; - } - /* Go to next chain element. */ - node = node->u.expression.next; - if (!node) - break; - str = node->u.expression.u.string; - } - /* Add final load field */ - prev_op = load_exp_op; - load_exp_op = calloc(sizeof(struct ir_load_expression_op), 1); - if (!load_exp_op) - goto error; - prev_op->next = load_exp_op; - load_exp_op->type = IR_LOAD_EXPRESSION_LOAD_FIELD; - return load_exp; - -error: - free_load_expression(load_exp); - return NULL; -} - -static -struct ir_op *make_op_load_expression(struct filter_node *node, - enum ir_side side) -{ - struct ir_op *op; - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_LOAD; - op->data_type = IR_DATA_EXPRESSION; - op->signedness = IR_SIGN_DYN; - op->side = side; - op->u.load.u.expression = create_load_expression(node); - if (!op->u.load.u.expression) { - goto error; - } - return op; - -error: - free_load_expression(op->u.load.u.expression); - free(op); - return NULL; -} - -static -struct ir_op *make_op_unary(enum unary_op_type unary_op_type, - const char *op_str, enum ir_op_signedness signedness, - struct ir_op *child, enum ir_side side) -{ - struct ir_op *op = NULL; - - if (child->data_type == IR_DATA_STRING) { - fprintf(stderr, "[error] unary operation '%s' not allowed on string literal\n", op_str); - goto error; - } - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_UNARY; - op->data_type = child->data_type; - op->signedness = signedness; - op->side = side; - op->u.unary.type = unary_op_type; - op->u.unary.child = child; - return op; - -error: - free(op); - return NULL; -} - -/* - * unary + is pretty much useless. - */ -static -struct ir_op *make_op_unary_plus(struct ir_op *child, enum ir_side side) -{ - return make_op_unary(AST_UNARY_PLUS, "+", child->signedness, - child, side); -} - -static -struct ir_op *make_op_unary_minus(struct ir_op *child, enum ir_side side) -{ - return make_op_unary(AST_UNARY_MINUS, "-", child->signedness, - child, side); -} - -static -struct ir_op *make_op_unary_not(struct ir_op *child, enum ir_side side) -{ - return make_op_unary(AST_UNARY_NOT, "!", child->signedness, - child, side); -} - -static -struct ir_op *make_op_unary_bit_not(struct ir_op *child, enum ir_side side) -{ - return make_op_unary(AST_UNARY_BIT_NOT, "~", child->signedness, - child, side); -} - -static -struct ir_op *make_op_binary_compare(enum op_type bin_op_type, - const char *op_str, struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - struct ir_op *op = NULL; - - if (left->data_type == IR_DATA_UNKNOWN - || right->data_type == IR_DATA_UNKNOWN) { - fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str); - goto error; - - } - if ((left->data_type == IR_DATA_STRING - && (right->data_type == IR_DATA_NUMERIC || right->data_type == IR_DATA_FLOAT)) - || ((left->data_type == IR_DATA_NUMERIC || left->data_type == IR_DATA_FLOAT) && - right->data_type == IR_DATA_STRING)) { - fprintf(stderr, "[error] binary operation '%s' operand type mismatch\n", op_str); - goto error; - } - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_BINARY; - op->u.binary.type = bin_op_type; - op->u.binary.left = left; - op->u.binary.right = right; - - /* we return a boolean, represented as signed numeric */ - op->data_type = IR_DATA_NUMERIC; - op->signedness = IR_SIGNED; - op->side = side; - - return op; - -error: - free(op); - return NULL; -} - -static -struct ir_op *make_op_binary_eq(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_EQ, "==", left, right, side); -} - -static -struct ir_op *make_op_binary_ne(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_NE, "!=", left, right, side); -} - -static -struct ir_op *make_op_binary_gt(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_GT, ">", left, right, side); -} - -static -struct ir_op *make_op_binary_lt(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_LT, "<", left, right, side); -} - -static -struct ir_op *make_op_binary_ge(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_GE, ">=", left, right, side); -} - -static -struct ir_op *make_op_binary_le(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_compare(AST_OP_LE, "<=", left, right, side); -} - -static -struct ir_op *make_op_binary_logical(enum op_type bin_op_type, - const char *op_str, struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - struct ir_op *op = NULL; - - if (left->data_type == IR_DATA_UNKNOWN - || right->data_type == IR_DATA_UNKNOWN) { - fprintf(stderr, "[error] binary operation '%s' has unknown operand type\n", op_str); - goto error; - - } - if (left->data_type == IR_DATA_STRING - || right->data_type == IR_DATA_STRING) { - fprintf(stderr, "[error] logical binary operation '%s' cannot have string operand\n", op_str); - goto error; - } - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_LOGICAL; - op->u.binary.type = bin_op_type; - op->u.binary.left = left; - op->u.binary.right = right; - - /* we return a boolean, represented as signed numeric */ - op->data_type = IR_DATA_NUMERIC; - op->signedness = IR_SIGNED; - op->side = side; - - return op; - -error: - free(op); - return NULL; -} - -static -struct ir_op *make_op_binary_bitwise(enum op_type bin_op_type, - const char *op_str, struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - struct ir_op *op = NULL; - - if (left->data_type == IR_DATA_UNKNOWN - || right->data_type == IR_DATA_UNKNOWN) { - fprintf(stderr, "[error] bitwise binary operation '%s' has unknown operand type\n", op_str); - goto error; - - } - if (left->data_type == IR_DATA_STRING - || right->data_type == IR_DATA_STRING) { - fprintf(stderr, "[error] bitwise binary operation '%s' cannot have string operand\n", op_str); - goto error; - } - if (left->data_type == IR_DATA_FLOAT - || right->data_type == IR_DATA_FLOAT) { - fprintf(stderr, "[error] bitwise binary operation '%s' cannot have floating point operand\n", op_str); - goto error; - } - - op = calloc(sizeof(struct ir_op), 1); - if (!op) - return NULL; - op->op = IR_OP_BINARY; - op->u.binary.type = bin_op_type; - op->u.binary.left = left; - op->u.binary.right = right; - - /* we return a signed numeric */ - op->data_type = IR_DATA_NUMERIC; - op->signedness = IR_SIGNED; - op->side = side; - - return op; - -error: - free(op); - return NULL; -} - -static -struct ir_op *make_op_binary_logical_and(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_logical(AST_OP_AND, "&&", left, right, side); -} - -static -struct ir_op *make_op_binary_logical_or(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_logical(AST_OP_OR, "||", left, right, side); -} - -static -struct ir_op *make_op_binary_bitwise_rshift(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_bitwise(AST_OP_BIT_RSHIFT, ">>", left, right, side); -} - -static -struct ir_op *make_op_binary_bitwise_lshift(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_bitwise(AST_OP_BIT_LSHIFT, "<<", left, right, side); -} - -static -struct ir_op *make_op_binary_bitwise_and(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_bitwise(AST_OP_BIT_AND, "&", left, right, side); -} - -static -struct ir_op *make_op_binary_bitwise_or(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_bitwise(AST_OP_BIT_OR, "|", left, right, side); -} - -static -struct ir_op *make_op_binary_bitwise_xor(struct ir_op *left, struct ir_op *right, - enum ir_side side) -{ - return make_op_binary_bitwise(AST_OP_BIT_XOR, "^", left, right, side); -} - -static -void filter_free_ir_recursive(struct ir_op *op) -{ - if (!op) - return; - switch (op->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] Unknown op type in %s\n", - __func__); - break; - case IR_OP_ROOT: - filter_free_ir_recursive(op->u.root.child); - break; - case IR_OP_LOAD: - switch (op->data_type) { - case IR_DATA_STRING: - free(op->u.load.u.string.value); - break; - case IR_DATA_FIELD_REF: /* fall-through */ - case IR_DATA_GET_CONTEXT_REF: - free(op->u.load.u.ref); - break; - case IR_DATA_EXPRESSION: - free_load_expression(op->u.load.u.expression); - default: - break; - } - break; - case IR_OP_UNARY: - filter_free_ir_recursive(op->u.unary.child); - break; - case IR_OP_BINARY: - filter_free_ir_recursive(op->u.binary.left); - filter_free_ir_recursive(op->u.binary.right); - break; - case IR_OP_LOGICAL: - filter_free_ir_recursive(op->u.logical.left); - filter_free_ir_recursive(op->u.logical.right); - break; - } - free(op); -} - -static -struct ir_op *make_expression(struct filter_parser_ctx *ctx, - struct filter_node *node, enum ir_side side) -{ - switch (node->u.expression.type) { - case AST_EXP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown expression type\n", __func__); - return NULL; - - case AST_EXP_STRING: - return make_op_load_string(node->u.expression.u.string, side); - case AST_EXP_CONSTANT: - return make_op_load_numeric(node->u.expression.u.constant, - side); - case AST_EXP_FLOAT_CONSTANT: - return make_op_load_float(node->u.expression.u.float_constant, - side); - case AST_EXP_IDENTIFIER: - case AST_EXP_GLOBAL_IDENTIFIER: - return make_op_load_expression(node, side); - case AST_EXP_NESTED: - return generate_ir_recursive(ctx, node->u.expression.u.child, - side); - } -} - -static -struct ir_op *make_op(struct filter_parser_ctx *ctx, - struct filter_node *node, enum ir_side side) -{ - struct ir_op *op = NULL, *lchild, *rchild; - const char *op_str = "?"; - - switch (node->u.op.type) { - case AST_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown binary op type\n", __func__); - return NULL; - - /* - * The following binary operators other than comparators and - * logical and/or are not supported yet. - */ - case AST_OP_MUL: - op_str = "*"; - goto error_not_supported; - case AST_OP_DIV: - op_str = "/"; - goto error_not_supported; - case AST_OP_MOD: - op_str = "%"; - goto error_not_supported; - case AST_OP_PLUS: - op_str = "+"; - goto error_not_supported; - case AST_OP_MINUS: - op_str = "-"; - goto error_not_supported; - - case AST_OP_BIT_RSHIFT: - case AST_OP_BIT_LSHIFT: - case AST_OP_BIT_AND: - case AST_OP_BIT_OR: - case AST_OP_BIT_XOR: - lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); - if (!lchild) - return NULL; - rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT); - if (!rchild) { - filter_free_ir_recursive(lchild); - return NULL; - } - break; - - case AST_OP_EQ: - case AST_OP_NE: - case AST_OP_GT: - case AST_OP_LT: - case AST_OP_GE: - case AST_OP_LE: - lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); - if (!lchild) - return NULL; - rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_RIGHT); - if (!rchild) { - filter_free_ir_recursive(lchild); - return NULL; - } - break; - - case AST_OP_AND: - case AST_OP_OR: - /* - * Both children considered as left, since we need to - * populate R0. - */ - lchild = generate_ir_recursive(ctx, node->u.op.lchild, IR_LEFT); - if (!lchild) - return NULL; - rchild = generate_ir_recursive(ctx, node->u.op.rchild, IR_LEFT); - if (!rchild) { - filter_free_ir_recursive(lchild); - return NULL; - } - break; - } - - switch (node->u.op.type) { - case AST_OP_AND: - op = make_op_binary_logical_and(lchild, rchild, side); - break; - case AST_OP_OR: - op = make_op_binary_logical_or(lchild, rchild, side); - break; - case AST_OP_EQ: - op = make_op_binary_eq(lchild, rchild, side); - break; - case AST_OP_NE: - op = make_op_binary_ne(lchild, rchild, side); - break; - case AST_OP_GT: - op = make_op_binary_gt(lchild, rchild, side); - break; - case AST_OP_LT: - op = make_op_binary_lt(lchild, rchild, side); - break; - case AST_OP_GE: - op = make_op_binary_ge(lchild, rchild, side); - break; - case AST_OP_LE: - op = make_op_binary_le(lchild, rchild, side); - break; - case AST_OP_BIT_RSHIFT: - op = make_op_binary_bitwise_rshift(lchild, rchild, side); - break; - case AST_OP_BIT_LSHIFT: - op = make_op_binary_bitwise_lshift(lchild, rchild, side); - break; - case AST_OP_BIT_AND: - op = make_op_binary_bitwise_and(lchild, rchild, side); - break; - case AST_OP_BIT_OR: - op = make_op_binary_bitwise_or(lchild, rchild, side); - break; - case AST_OP_BIT_XOR: - op = make_op_binary_bitwise_xor(lchild, rchild, side); - break; - default: - break; - } - - if (!op) { - filter_free_ir_recursive(rchild); - filter_free_ir_recursive(lchild); - } - return op; - -error_not_supported: - fprintf(stderr, "[error] %s: binary operation '%s' not supported\n", - __func__, op_str); - return NULL; -} - -static -struct ir_op *make_unary_op(struct filter_parser_ctx *ctx, - struct filter_node *node, enum ir_side side) -{ - switch (node->u.unary_op.type) { - case AST_UNARY_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown unary op type\n", __func__); - return NULL; - - case AST_UNARY_PLUS: - { - struct ir_op *op, *child; - - child = generate_ir_recursive(ctx, node->u.unary_op.child, - side); - if (!child) - return NULL; - op = make_op_unary_plus(child, side); - if (!op) { - filter_free_ir_recursive(child); - return NULL; - } - return op; - } - case AST_UNARY_MINUS: - { - struct ir_op *op, *child; - - child = generate_ir_recursive(ctx, node->u.unary_op.child, - side); - if (!child) - return NULL; - op = make_op_unary_minus(child, side); - if (!op) { - filter_free_ir_recursive(child); - return NULL; - } - return op; - } - case AST_UNARY_NOT: - { - struct ir_op *op, *child; - - child = generate_ir_recursive(ctx, node->u.unary_op.child, - side); - if (!child) - return NULL; - op = make_op_unary_not(child, side); - if (!op) { - filter_free_ir_recursive(child); - return NULL; - } - return op; - } - case AST_UNARY_BIT_NOT: - { - struct ir_op *op, *child; - - child = generate_ir_recursive(ctx, node->u.unary_op.child, - side); - if (!child) - return NULL; - op = make_op_unary_bit_not(child, side); - if (!op) { - filter_free_ir_recursive(child); - return NULL; - } - return op; - } - } - - return NULL; -} - -static -struct ir_op *generate_ir_recursive(struct filter_parser_ctx *ctx, - struct filter_node *node, enum ir_side side) -{ - switch (node->type) { - case NODE_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown node type\n", __func__); - return NULL; - - case NODE_ROOT: - { - struct ir_op *op, *child; - - child = generate_ir_recursive(ctx, node->u.root.child, - side); - if (!child) - return NULL; - op = make_op_root(child, side); - if (!op) { - filter_free_ir_recursive(child); - return NULL; - } - return op; - } - case NODE_EXPRESSION: - return make_expression(ctx, node, side); - case NODE_OP: - return make_op(ctx, node, side); - case NODE_UNARY_OP: - return make_unary_op(ctx, node, side); - } - return 0; -} - -LTTNG_HIDDEN -void filter_ir_free(struct filter_parser_ctx *ctx) -{ - filter_free_ir_recursive(ctx->ir_root); - ctx->ir_root = NULL; -} - -LTTNG_HIDDEN -int filter_visitor_ir_generate(struct filter_parser_ctx *ctx) -{ - struct ir_op *op; - - op = generate_ir_recursive(ctx, &ctx->ast->root, IR_LEFT); - if (!op) { - return -EINVAL; - } - ctx->ir_root = op; - return 0; -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c deleted file mode 100644 index bcbb69e3f..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-comparator.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * filter-visitor-ir-check-binary-comparator.c - * - * LTTng filter IR check binary comparator - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -static -int check_bin_comparator(struct ir_op *node) -{ - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown op type\n", __func__); - return -EINVAL; - - case IR_OP_ROOT: - return check_bin_comparator(node->u.root.child); - case IR_OP_LOAD: - return 0; - case IR_OP_UNARY: - return check_bin_comparator(node->u.unary.child); - case IR_OP_BINARY: - { - int ret; - - if (node->u.binary.left->data_type == IR_DATA_STRING - || node->u.binary.right->data_type - == IR_DATA_STRING) { - if (node->u.binary.type != AST_OP_EQ - && node->u.binary.type != AST_OP_NE) { - fprintf(stderr, "[error] Only '==' and '!=' comparators are allowed for strings\n"); - return -EINVAL; - } - } - - ret = check_bin_comparator(node->u.binary.left); - if (ret) - return ret; - return check_bin_comparator(node->u.binary.right); - } - case IR_OP_LOGICAL: - { - int ret; - - ret = check_bin_comparator(node->u.logical.left); - if (ret) - return ret; - return check_bin_comparator(node->u.logical.right); - } - } -} - -int filter_visitor_ir_check_binary_comparator(struct filter_parser_ctx *ctx) -{ - return check_bin_comparator(ctx->ir_root); -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c deleted file mode 100644 index 04a5fbdd6..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-ir-check-binary-op-nesting.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * filter-visitor-ir-check-binary-op-nesting.c - * - * LTTng filter IR check binary op nesting - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -#include - -static -int check_bin_op_nesting_recursive(struct ir_op *node, int nesting) -{ - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown op type\n", __func__); - return -EINVAL; - - case IR_OP_ROOT: - return check_bin_op_nesting_recursive(node->u.root.child, - nesting); - case IR_OP_LOAD: - return 0; - case IR_OP_UNARY: - return check_bin_op_nesting_recursive(node->u.unary.child, - nesting); - case IR_OP_BINARY: - { - int ret; - - ret = check_bin_op_nesting_recursive(node->u.binary.left, - nesting + 1); - if (ret) - return ret; - return check_bin_op_nesting_recursive(node->u.binary.right, - nesting + 1); - } - case IR_OP_LOGICAL: - { - int ret; - - ret = check_bin_op_nesting_recursive(node->u.logical.left, - nesting); - if (ret) - return ret; - return check_bin_op_nesting_recursive(node->u.logical.right, - nesting); - } - } -} - -LTTNG_HIDDEN -int filter_visitor_ir_check_binary_op_nesting(struct filter_parser_ctx *ctx) -{ - return check_bin_op_nesting_recursive(ctx->ir_root, 0); -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c deleted file mode 100644 index 5e2778e09..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-ir-normalize-glob-patterns.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * filter-visitor-ir-normalize-glob-patterns.c - * - * LTTng filter IR normalize string - * - * Copyright 2017 Philippe Proulx - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -static -int normalize_glob_patterns(struct ir_op *node) -{ - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown op type\n", __func__); - return -EINVAL; - - case IR_OP_ROOT: - return normalize_glob_patterns(node->u.root.child); - case IR_OP_LOAD: - { - if (node->data_type == IR_DATA_STRING) { - enum ir_load_string_type type = - node->u.load.u.string.type; - if (type == IR_LOAD_STRING_TYPE_GLOB_STAR_END || - type == IR_LOAD_STRING_TYPE_GLOB_STAR) { - assert(node->u.load.u.string.value); - strutils_normalize_star_glob_pattern( - node->u.load.u.string.value); - } - } - - return 0; - } - case IR_OP_UNARY: - return normalize_glob_patterns(node->u.unary.child); - case IR_OP_BINARY: - { - int ret = normalize_glob_patterns(node->u.binary.left); - - if (ret) - return ret; - return normalize_glob_patterns(node->u.binary.right); - } - case IR_OP_LOGICAL: - { - int ret; - - ret = normalize_glob_patterns(node->u.logical.left); - if (ret) - return ret; - return normalize_glob_patterns(node->u.logical.right); - } - } -} - -/* - * This function normalizes all the globbing literal strings with - * utils_normalize_glob_pattern(). See the documentation of - * utils_normalize_glob_pattern() for more details. - */ -LTTNG_HIDDEN -int filter_visitor_ir_normalize_glob_patterns(struct filter_parser_ctx *ctx) -{ - return normalize_glob_patterns(ctx->ir_root); -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c deleted file mode 100644 index 3e749a4da..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-globbing.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * filter-visitor-ir-validate-globbing.c - * - * LTTng filter IR validate globbing - * - * Copyright 2017 Philippe Proulx - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -static -int validate_globbing(struct ir_op *node) -{ - int ret; - - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown op type\n", __func__); - return -EINVAL; - - case IR_OP_ROOT: - return validate_globbing(node->u.root.child); - case IR_OP_LOAD: - return 0; - case IR_OP_UNARY: - return validate_globbing(node->u.unary.child); - case IR_OP_BINARY: - { - struct ir_op *left = node->u.binary.left; - struct ir_op *right = node->u.binary.right; - - if (left->op == IR_OP_LOAD && right->op == IR_OP_LOAD && - left->data_type == IR_DATA_STRING && - right->data_type == IR_DATA_STRING) { - /* Test 1. */ - if (left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR && - right->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) { - fprintf(stderr, "[error] Cannot compare two globbing patterns\n"); - return -1; - } - - if (right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR && - left->u.load.u.string.type != IR_LOAD_STRING_TYPE_PLAIN) { - fprintf(stderr, "[error] Cannot compare two globbing patterns\n"); - return -1; - } - } - - if ((left->op == IR_OP_LOAD && left->data_type == IR_DATA_STRING) || - (right->op == IR_OP_LOAD && right->data_type == IR_DATA_STRING)) { - if ((left->op == IR_OP_LOAD && left->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR) || - (right->op == IR_OP_LOAD && right->u.load.u.string.type == IR_LOAD_STRING_TYPE_GLOB_STAR)) { - /* Test 2. */ - if (node->u.binary.type != AST_OP_EQ && - node->u.binary.type != AST_OP_NE) { - fprintf(stderr, "[error] Only the `==` and `!=` operators are allowed with a globbing pattern\n"); - return -1; - } - } - } - - ret = validate_globbing(left); - if (ret) { - return ret; - } - - return validate_globbing(right); - } - case IR_OP_LOGICAL: - ret = validate_globbing(node->u.logical.left); - if (ret) - return ret; - return validate_globbing(node->u.logical.right); - } -} - -/* - * This function recursively validates that: - * - * 1. When there's a binary operation between two literal strings, - * if one of them has the IR_LOAD_STRING_TYPE_GLOB_STAR type, - * the other one has the IR_LOAD_STRING_TYPE_PLAIN type. - * - * In other words, you cannot compare two globbing patterns, except - * for two globbing patterns with only a star at the end for backward - * compatibility reasons. - * - * 2. When there's a binary operation between two literal strings, if - * one of them is a (full) star globbing pattern, the binary - * operation is either == or !=. - */ -LTTNG_HIDDEN -int filter_visitor_ir_validate_globbing(struct filter_parser_ctx *ctx) -{ - return validate_globbing(ctx->ir_root); -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c b/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c deleted file mode 100644 index 1df83b03f..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-ir-validate-string.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * filter-visitor-ir-validate-string.c - * - * LTTng filter IR validate string - * - * Copyright 2014 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "filter-ast.h" -#include "filter-parser.h" -#include "filter-ir.h" - -enum parse_char_result { - PARSE_CHAR_UNKNOWN = -2, - PARSE_CHAR_WILDCARD = -1, - PARSE_CHAR_NORMAL = 0, -}; - -static -enum parse_char_result parse_char(const char **p) -{ - switch (**p) { - case '\\': - (*p)++; - switch (**p) { - case '\\': - case '*': - return PARSE_CHAR_NORMAL; - default: - return PARSE_CHAR_UNKNOWN; - } - case '*': - return PARSE_CHAR_WILDCARD; - default: - return PARSE_CHAR_NORMAL; - } -} - -static -int validate_string(struct ir_op *node) -{ - switch (node->op) { - case IR_OP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown op type\n", __func__); - return -EINVAL; - - case IR_OP_ROOT: - return validate_string(node->u.root.child); - case IR_OP_LOAD: - { - int ret = 0; - - if (node->data_type == IR_DATA_STRING) { - const char *str; - - assert(node->u.load.u.string.value); - str = node->u.load.u.string.value; - - for (;;) { - enum parse_char_result res; - - if (!(*str)) { - break; - } - - res = parse_char(&str); - str++; - - switch (res) { - case PARSE_CHAR_UNKNOWN: - ret = -EINVAL; - fprintf(stderr, - "Unsupported escape character detected.\n"); - goto end_load; - case PARSE_CHAR_NORMAL: - default: - break; - } - } - } -end_load: - return ret; - } - case IR_OP_UNARY: - return validate_string(node->u.unary.child); - case IR_OP_BINARY: - { - int ret = validate_string(node->u.binary.left); - - if (ret) - return ret; - return validate_string(node->u.binary.right); - } - case IR_OP_LOGICAL: - { - int ret; - - ret = validate_string(node->u.logical.left); - if (ret) - return ret; - return validate_string(node->u.logical.right); - } - } -} - -LTTNG_HIDDEN -int filter_visitor_ir_validate_string(struct filter_parser_ctx *ctx) -{ - return validate_string(ctx->ir_root); -} diff --git a/src/lib/lttng-ctl/filter/filter-visitor-xml.c b/src/lib/lttng-ctl/filter/filter-visitor-xml.c deleted file mode 100644 index d5ff0c187..000000000 --- a/src/lib/lttng-ctl/filter/filter-visitor-xml.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * filter-visitor-xml.c - * - * LTTng filter XML pretty printer visitor - * - * Copyright 2012 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "filter-ast.h" -#include "filter-parser.h" - -#include - -#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) - -static -int recursive_visit_print(struct filter_node *node, FILE *stream, int indent); - -static -void print_tabs(FILE *fd, int depth) -{ - int i; - - for (i = 0; i < depth; i++) - fprintf(fd, "\t"); -} - -static -int recursive_visit_print_expression(struct filter_node *node, - FILE *stream, int indent) -{ - struct filter_node *iter_node; - - if (!node) { - fprintf(stderr, "[error] %s: NULL child\n", __func__); - return -EINVAL; - } - switch (node->u.expression.type) { - case AST_EXP_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown expression\n", __func__); - return -EINVAL; - case AST_EXP_STRING: - print_tabs(stream, indent); - fprintf(stream, "\n", - node->u.expression.u.string); - break; - case AST_EXP_CONSTANT: - print_tabs(stream, indent); - fprintf(stream, "\n", - node->u.expression.u.constant); - break; - case AST_EXP_FLOAT_CONSTANT: - print_tabs(stream, indent); - fprintf(stream, "\n", - node->u.expression.u.float_constant); - break; - case AST_EXP_IDENTIFIER: /* fall-through */ - case AST_EXP_GLOBAL_IDENTIFIER: - print_tabs(stream, indent); - fprintf(stream, "<%s value=\"%s\"/>\n", - node->u.expression.type == AST_EXP_IDENTIFIER ? - "identifier" : "global_identifier", - node->u.expression.u.identifier); - iter_node = node->u.expression.next; - while (iter_node) { - print_tabs(stream, indent); - fprintf(stream, "\n"); - if (recursive_visit_print_expression(iter_node, - stream, indent + 1)) { - return -EINVAL; - } - print_tabs(stream, indent); - fprintf(stream, "\n"); - iter_node = iter_node->u.expression.next; - - } - break; - case AST_EXP_NESTED: - return recursive_visit_print(node->u.expression.u.child, - stream, indent + 1); - } - return 0; -} - - -static -int recursive_visit_print(struct filter_node *node, FILE *stream, int indent) -{ - int ret; - - if (!node) { - fprintf(stderr, "[error] %s: NULL child\n", __func__); - return -EINVAL; - } - switch (node->type) { - case NODE_UNKNOWN: - default: - fprintf(stderr, "[error] %s: unknown node type\n", __func__); - return -EINVAL; - case NODE_ROOT: - print_tabs(stream, indent); - fprintf(stream, "\n"); - ret = recursive_visit_print(node->u.root.child, stream, - indent + 1); - print_tabs(stream, indent); - fprintf(stream, "\n"); - return ret; - case NODE_EXPRESSION: - print_tabs(stream, indent); - fprintf(stream, "\n"); - ret = recursive_visit_print_expression(node, stream, - indent + 1); - print_tabs(stream, indent); - fprintf(stream, "\n"); - return ret; - case NODE_OP: - print_tabs(stream, indent); - fprintf(stream, ">\""); - break; - case AST_OP_BIT_LSHIFT: - fprintf(stream, "\"<<\""); - break; - case AST_OP_AND: - fprintf(stream, "\"&&\""); - break; - case AST_OP_OR: - fprintf(stream, "\"||\""); - break; - case AST_OP_BIT_AND: - fprintf(stream, "\"&\""); - break; - case AST_OP_BIT_OR: - fprintf(stream, "\"|\""); - break; - case AST_OP_BIT_XOR: - fprintf(stream, "\"^\""); - break; - - case AST_OP_EQ: - fprintf(stream, "\"==\""); - break; - case AST_OP_NE: - fprintf(stream, "\"!=\""); - break; - case AST_OP_GT: - fprintf(stream, "\">\""); - break; - case AST_OP_LT: - fprintf(stream, "\"<\""); - break; - case AST_OP_GE: - fprintf(stream, "\">=\""); - break; - case AST_OP_LE: - fprintf(stream, "\"<=\""); - break; - } - fprintf(stream, ">\n"); - ret = recursive_visit_print(node->u.op.lchild, - stream, indent + 1); - if (ret) - return ret; - ret = recursive_visit_print(node->u.op.rchild, - stream, indent + 1); - if (ret) - return ret; - print_tabs(stream, indent); - fprintf(stream, "\n"); - return ret; - case NODE_UNARY_OP: - print_tabs(stream, indent); - fprintf(stream, "\n"); - ret = recursive_visit_print(node->u.unary_op.child, - stream, indent + 1); - print_tabs(stream, indent); - fprintf(stream, "\n"); - return ret; - } - return 0; -} - -LTTNG_HIDDEN -int filter_visitor_print_xml(struct filter_parser_ctx *ctx, FILE *stream, - int indent) -{ - return recursive_visit_print(&ctx->ast->root, stream, indent); -} diff --git a/src/lib/lttng-ctl/filter/memstream.h b/src/lib/lttng-ctl/filter/memstream.h deleted file mode 100644 index cd246eed2..000000000 --- a/src/lib/lttng-ctl/filter/memstream.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012 (C) Mathieu Desnoyers - * - * SPDX-License-Identifier: MIT - * - */ - -#ifndef _LTTNG_CTL_MEMSTREAM_H -#define _LTTNG_CTL_MEMSTREAM_H - -#ifdef LTTNG_HAVE_FMEMOPEN -#include - -static inline -FILE *lttng_fmemopen(void *buf, size_t size, const char *mode) -{ - return fmemopen(buf, size, mode); -} - -#else /* LTTNG_HAVE_FMEMOPEN */ - -#include -#include - -/* - * Fallback for systems which don't have fmemopen. Copy buffer to a - * temporary file, and use that file as FILE * input. - */ -static inline -FILE *lttng_fmemopen(void *buf, size_t size, const char *mode) -{ - char tmpname[PATH_MAX]; - size_t len; - FILE *fp; - int ret; - - /* - * Support reading only. - */ - if (strcmp(mode, "rb") != 0) { - return NULL; - } - strncpy(tmpname, "/tmp/lttng-tmp-XXXXXX", PATH_MAX); - ret = mkstemp(tmpname); - if (ret < 0) { - return NULL; - } - /* - * We need to write to the file. - */ - fp = fdopen(ret, "w+"); - if (!fp) { - goto error_unlink; - } - /* Copy the entire buffer to the file */ - len = fwrite(buf, sizeof(char), size, fp); - if (len != size) { - goto error_close; - } - ret = fseek(fp, 0L, SEEK_SET); - if (ret < 0) { - PERROR("fseek"); - goto error_close; - } - /* We keep the handle open, but can unlink the file on the VFS. */ - ret = unlink(tmpname); - if (ret < 0) { - PERROR("unlink"); - } - return fp; - -error_close: - ret = fclose(fp); - if (ret < 0) { - PERROR("close"); - } -error_unlink: - ret = unlink(tmpname); - if (ret < 0) { - PERROR("unlink"); - } - return NULL; -} - -#endif /* LTTNG_HAVE_FMEMOPEN */ - -#endif /* _LTTNG_CTL_MEMSTREAM_H */ diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 81ddeb74a..4eb36a261 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -43,26 +43,12 @@ #include #include -#include "filter/filter-ast.h" -#include "filter/filter-parser.h" -#include "filter/filter-bytecode.h" -#include "filter/memstream.h" +#include +#include +#include +#include #include "lttng-ctl-helper.h" -#ifdef DEBUG -static const int print_xml = 1; -#define dbg_printf(fmt, args...) \ - printf("[debug liblttng-ctl] " fmt, ## args) -#else -static const int print_xml = 0; -#define dbg_printf(fmt, args...) \ -do { \ - /* do nothing but check printf format */ \ - if (0) \ - printf("[debug liblttnctl] " fmt, ## args); \ -} while (0) -#endif - #define COPY_DOMAIN_PACKED(dst, src) \ do { \ struct lttng_domain _tmp_domain; \ @@ -1050,133 +1036,6 @@ error: return NULL; } -/* - * Generate the filter bytecode from a given filter expression string. Put the - * newly allocated parser context in ctxp and populate the lsm object with the - * expression len. - * - * Return 0 on success else a LTTNG_ERR_* code and ctxp is untouched. - */ -static int generate_filter(char *filter_expression, - struct lttcomm_session_msg *lsm, struct filter_parser_ctx **ctxp) -{ - int ret; - struct filter_parser_ctx *ctx = NULL; - FILE *fmem = NULL; - - assert(filter_expression); - assert(lsm); - assert(ctxp); - - /* - * Casting const to non-const, as the underlying function will use it in - * read-only mode. - */ - fmem = lttng_fmemopen((void *) filter_expression, - strlen(filter_expression), "r"); - if (!fmem) { - fprintf(stderr, "Error opening memory as stream\n"); - ret = -LTTNG_ERR_FILTER_NOMEM; - goto error; - } - ctx = filter_parser_ctx_alloc(fmem); - if (!ctx) { - fprintf(stderr, "Error allocating parser\n"); - ret = -LTTNG_ERR_FILTER_NOMEM; - goto filter_alloc_error; - } - ret = filter_parser_ctx_append_ast(ctx); - if (ret) { - fprintf(stderr, "Parse error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - if (print_xml) { - ret = filter_visitor_print_xml(ctx, stdout, 0); - if (ret) { - fflush(stdout); - fprintf(stderr, "XML print error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - } - - dbg_printf("Generating IR... "); - fflush(stdout); - ret = filter_visitor_ir_generate(ctx); - if (ret) { - fprintf(stderr, "Generate IR error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - dbg_printf("done\n"); - - dbg_printf("Validating IR... "); - fflush(stdout); - ret = filter_visitor_ir_check_binary_op_nesting(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Normalize globbing patterns in the expression. */ - ret = filter_visitor_ir_normalize_glob_patterns(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Validate strings used as literals in the expression. */ - ret = filter_visitor_ir_validate_string(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - /* Validate globbing patterns in the expression. */ - ret = filter_visitor_ir_validate_globbing(ctx); - if (ret) { - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - - dbg_printf("done\n"); - - dbg_printf("Generating bytecode... "); - fflush(stdout); - ret = filter_visitor_bytecode_generate(ctx); - if (ret) { - fprintf(stderr, "Generate bytecode error\n"); - ret = -LTTNG_ERR_FILTER_INVAL; - goto parse_error; - } - dbg_printf("done\n"); - dbg_printf("Size of bytecode generated: %u bytes.\n", - bytecode_get_len(&ctx->bytecode->b)); - - lsm->u.enable.bytecode_len = sizeof(ctx->bytecode->b) - + bytecode_get_len(&ctx->bytecode->b); - lsm->u.enable.expression_len = strlen(filter_expression) + 1; - - /* No need to keep the memory stream. */ - if (fclose(fmem) != 0) { - PERROR("fclose"); - } - - *ctxp = ctx; - return 0; - -parse_error: - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); -filter_alloc_error: - if (fclose(fmem) != 0) { - PERROR("fclose"); - } -error: - return ret; -} - /* * Enable event(s) for a channel, possibly with exclusions and a filter. * If no event name is specified, all events are enabled. @@ -1279,10 +1138,14 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, } } - ret = generate_filter(filter_expression, &lsm, &ctx); + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { goto filter_error; } + + lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b); + lsm.u.enable.expression_len = strlen(filter_expression) + 1; } ret = lttng_dynamic_buffer_set_capacity(&payload.buffer, @@ -1509,10 +1372,14 @@ int lttng_disable_event_ext(struct lttng_handle *handle, } } - ret = generate_filter(filter_expression, &lsm, &ctx); + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { goto filter_error; } + + lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b); + lsm.u.enable.expression_len = strlen(filter_expression) + 1; } varlen_data = zmalloc(lsm.u.disable.bytecode_len