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;
}
Example #2
0
/*
 * 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;
}
Example #3
0
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);
}
Example #4
0
/*
 * 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;
	}
	}
}