Commit f47716b7 authored by Kalesh Singh's avatar Kalesh Singh Committed by Steven Rostedt (VMware)
Browse files

tracing/histogram: Covert expr to const if both operands are constants

If both operands of a hist trigger expression are constants, convert the
expression to a constant. This optimization avoids having to perform the
same calculation multiple times and also saves on memory since the
merged constants are represented by a single struct hist_field instead
or multiple.

Link: https://lkml.kernel.org/r/20211025200852.3002369-6-kaleshsingh@google.com



Signed-off-by: default avatarKalesh Singh <kaleshsingh@google.com>
Suggested-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent c5eac6ee
Loading
Loading
Loading
Loading
+74 −30
Original line number Diff line number Diff line
@@ -2411,9 +2411,15 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
	return ERR_PTR(ret);
}

/*
 * If the operands are var refs, return pointers the
 * variable(s) referenced in var1 and var2, else NULL.
 */
static int check_expr_operands(struct trace_array *tr,
			       struct hist_field *operand1,
			       struct hist_field *operand2)
			       struct hist_field *operand2,
			       struct hist_field **var1,
			       struct hist_field **var2)
{
	unsigned long operand1_flags = operand1->flags;
	unsigned long operand2_flags = operand2->flags;
@@ -2426,6 +2432,7 @@ static int check_expr_operands(struct trace_array *tr,
		if (!var)
			return -EINVAL;
		operand1_flags = var->flags;
		*var1 = var;
	}

	if ((operand2_flags & HIST_FIELD_FL_VAR_REF) ||
@@ -2436,6 +2443,7 @@ static int check_expr_operands(struct trace_array *tr,
		if (!var)
			return -EINVAL;
		operand2_flags = var->flags;
		*var2 = var;
	}

	if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) !=
@@ -2453,9 +2461,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
				     char *var_name, unsigned int *n_subexprs)
{
	struct hist_field *operand1 = NULL, *operand2 = NULL, *expr = NULL;
	unsigned long operand_flags;
	struct hist_field *var1 = NULL, *var2 = NULL;
	unsigned long operand_flags, operand2_flags;
	int field_op, ret = -EINVAL;
	char *sep, *operand1_str;
	hist_field_fn_t op_fn;
	bool combine_consts;

	if (*n_subexprs > 3) {
		hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
@@ -2512,11 +2523,38 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
		goto free;
	}

	ret = check_expr_operands(file->tr, operand1, operand2);
	switch (field_op) {
	case FIELD_OP_MINUS:
		op_fn = hist_field_minus;
		break;
	case FIELD_OP_PLUS:
		op_fn = hist_field_plus;
		break;
	case FIELD_OP_DIV:
		op_fn = hist_field_div;
		break;
	case FIELD_OP_MULT:
		op_fn = hist_field_mult;
		break;
	default:
		ret = -EINVAL;
		goto free;
	}

	ret = check_expr_operands(file->tr, operand1, operand2, &var1, &var2);
	if (ret)
		goto free;

	flags |= HIST_FIELD_FL_EXPR;
	operand_flags = var1 ? var1->flags : operand1->flags;
	operand2_flags = var2 ? var2->flags : operand2->flags;

	/*
	 * If both operands are constant, the expression can be
	 * collapsed to a single constant.
	 */
	combine_consts = operand_flags & operand2_flags & HIST_FIELD_FL_CONST;

	flags |= combine_consts ? HIST_FIELD_FL_CONST : HIST_FIELD_FL_EXPR;

	flags |= operand1->flags &
		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
@@ -2533,33 +2571,39 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
	expr->operands[0] = operand1;
	expr->operands[1] = operand2;

	if (combine_consts) {
		if (var1)
			expr->operands[0] = var1;
		if (var2)
			expr->operands[1] = var2;

		expr->constant = op_fn(expr, NULL, NULL, NULL, NULL);

		expr->operands[0] = NULL;
		expr->operands[1] = NULL;

		/*
		 * var refs won't be destroyed immediately
		 * See: destroy_hist_field()
		 */
		destroy_hist_field(operand2, 0);
		destroy_hist_field(operand1, 0);

		expr->name = expr_str(expr, 0);
	} else {
		expr->fn = op_fn;

		/* The operand sizes should be the same, so just pick one */
		expr->size = operand1->size;

		expr->operator = field_op;
	expr->name = expr_str(expr, 0);
		expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
		if (!expr->type) {
			ret = -ENOMEM;
			goto free;
		}

	switch (field_op) {
	case FIELD_OP_MINUS:
		expr->fn = hist_field_minus;
		break;
	case FIELD_OP_PLUS:
		expr->fn = hist_field_plus;
		break;
	case FIELD_OP_DIV:
		expr->fn = hist_field_div;
		break;
	case FIELD_OP_MULT:
		expr->fn = hist_field_mult;
		break;
	default:
		ret = -EINVAL;
		goto free;
		expr->name = expr_str(expr, 0);
	}

	return expr;