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; }
/* * Generate the filter bytecode from a give 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; } ret = filter_visitor_set_parent(ctx); if (ret) { fprintf(stderr, "Set parent 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; } /* 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; } 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; }
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; } 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; } ret = filter_visitor_set_parent(ctx); if (ret) { fprintf(stderr, "Set parent 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 0 if (run_bytecode) { int64_t retval; printf("Interpreting bytecode... "); fflush(stdout); ret = bytecode_interpret(&ctx->bytecode->b, &retval, NULL); if (ret) { fprintf(stderr, "Error interpreting bytecode\n"); goto parse_error; } else { printf("Bytecode interpret result: %" PRIi64 "\n", retval); } printf("done\n"); } #endif //0 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); }
/* * Create or enable an event with a filter expression. * * Return negative error value on error. * Return size of returned session payload data if OK. */ int lttng_enable_event_with_filter(struct lttng_handle *handle, struct lttng_event *event, const char *channel_name, const char *filter_expression) { struct lttcomm_session_msg lsm; struct filter_parser_ctx *ctx; FILE *fmem; int ret = 0; if (!filter_expression) { /* * Fall back to normal event enabling if no filter * specified. */ return lttng_enable_event(handle, event, channel_name); } /* * Empty filter string will always be rejected by the parser * anyway, so treat this corner-case early to eliminate * lttng_fmemopen error for 0-byte allocation. */ if (handle == NULL || filter_expression[0] == '\0') { return -LTTNG_ERR_INVALID; } /* * 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"); return -LTTNG_ERR_FILTER_NOMEM; } ctx = filter_parser_ctx_alloc(fmem); if (!ctx) { fprintf(stderr, "Error allocating parser\n"); ret = -LTTNG_ERR_FILTER_NOMEM; goto alloc_error; } ret = filter_parser_ctx_append_ast(ctx); if (ret) { fprintf(stderr, "Parse error\n"); ret = -LTTNG_ERR_FILTER_INVAL; goto parse_error; } ret = filter_visitor_set_parent(ctx); if (ret) { fprintf(stderr, "Set parent 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; } 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)); memset(&lsm, 0, sizeof(lsm)); lsm.cmd_type = LTTNG_ENABLE_EVENT_WITH_FILTER; /* Copy channel name */ copy_string(lsm.u.enable.channel_name, channel_name, sizeof(lsm.u.enable.channel_name)); /* Copy event name */ if (event) { memcpy(&lsm.u.enable.event, event, sizeof(lsm.u.enable.event)); } lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) + bytecode_get_len(&ctx->bytecode->b); copy_lttng_domain(&lsm.domain, &handle->domain); copy_string(lsm.session.name, handle->session_name, sizeof(lsm.session.name)); ret = ask_sessiond_varlen(&lsm, &ctx->bytecode->b, lsm.u.enable.bytecode_len, NULL); filter_bytecode_free(ctx); filter_ir_free(ctx); filter_parser_ctx_free(ctx); if (fclose(fmem) != 0) { perror("fclose"); } return ret; parse_error: filter_bytecode_free(ctx); filter_ir_free(ctx); filter_parser_ctx_free(ctx); alloc_error: if (fclose(fmem) != 0) { perror("fclose"); } return ret; }
/* * 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_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) { 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_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) { 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; }
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) + 1; insn = calloc(insn_len, 1); if (!insn) return -ENOMEM; insn->op = FILTER_OP_LOAD_STRING; strcpy(insn->data, node->u.load.u.string); 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_FIELD_REF: /* fall-through */ case IR_DATA_GET_CONTEXT_REF: { struct load_op *insn; uint32_t insn_len = sizeof(struct load_op) + sizeof(struct field_ref); struct field_ref ref_offset; uint32_t reloc_offset_u32; uint16_t reloc_offset; insn = calloc(insn_len, 1); if (!insn) return -ENOMEM; switch(node->data_type) { case IR_DATA_FIELD_REF: insn->op = FILTER_OP_LOAD_FIELD_REF; break; case IR_DATA_GET_CONTEXT_REF: insn->op = FILTER_OP_GET_CONTEXT_REF; break; default: free(insn); return -EINVAL; } ref_offset.offset = (uint16_t) -1U; memcpy(insn->data, &ref_offset, sizeof(ref_offset)); /* reloc_offset points to struct load_op */ reloc_offset_u32 = bytecode_get_len(&ctx->bytecode->b); if (reloc_offset_u32 > LTTNG_FILTER_MAX_LEN - 1) { free(insn); return -EINVAL; } reloc_offset = (uint16_t) reloc_offset_u32; ret = bytecode_push(&ctx->bytecode, insn, 1, insn_len); if (ret) { free(insn); return ret; } /* append reloc */ ret = bytecode_push(&ctx->bytecode_reloc, &reloc_offset, 1, sizeof(reloc_offset)); if (ret) { free(insn); return ret; } ret = bytecode_push(&ctx->bytecode_reloc, node->u.load.u.ref, 1, strlen(node->u.load.u.ref) + 1); free(insn); return ret; } } }