mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 00:34:20 +08:00
tracing: Add division and multiplication support for hist triggers
Adds basic support for division and multiplication operations for hist trigger variable expressions. For simplicity this patch only supports, division and multiplication for a single operation expression (e.g. x=$a/$b), as currently expressions are always evaluated right to left. This can lead to some incorrect results: e.g. echo 'hist:keys=common_pid:x=8-4-2' >> event/trigger 8-4-2 should evaluate to 2 i.e. (8-4)-2 but currently x evaluate to 6 i.e. 8-(4-2) Multiplication and division in sub-expressions will work correctly, once correct operator precedence support is added (See next patch in this series). For the undefined case of division by 0, the histogram expression evaluates to (u64)(-1). Since this cannot be detected when the expression is created, it is the responsibility of the user to be aware and account for this possibility. Examples: echo 'hist:keys=common_pid:a=8,b=4,x=$a/$b' \ >> event/trigger echo 'hist:keys=common_pid:y=5*$b' \ >> event/trigger Link: https://lkml.kernel.org/r/20211025200852.3002369-3-kaleshsingh@google.com Signed-off-by: Kalesh Singh <kaleshsingh@google.com> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
parent
52cfb37353
commit
bcef044150
@ -97,6 +97,8 @@ enum field_op_id {
|
|||||||
FIELD_OP_PLUS,
|
FIELD_OP_PLUS,
|
||||||
FIELD_OP_MINUS,
|
FIELD_OP_MINUS,
|
||||||
FIELD_OP_UNARY_MINUS,
|
FIELD_OP_UNARY_MINUS,
|
||||||
|
FIELD_OP_DIV,
|
||||||
|
FIELD_OP_MULT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -285,6 +287,40 @@ static u64 hist_field_minus(struct hist_field *hist_field,
|
|||||||
return val1 - val2;
|
return val1 - val2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 hist_field_div(struct hist_field *hist_field,
|
||||||
|
struct tracing_map_elt *elt,
|
||||||
|
struct trace_buffer *buffer,
|
||||||
|
struct ring_buffer_event *rbe,
|
||||||
|
void *event)
|
||||||
|
{
|
||||||
|
struct hist_field *operand1 = hist_field->operands[0];
|
||||||
|
struct hist_field *operand2 = hist_field->operands[1];
|
||||||
|
|
||||||
|
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
|
||||||
|
u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
|
||||||
|
|
||||||
|
/* Return -1 for the undefined case */
|
||||||
|
if (!val2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return div64_u64(val1, val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 hist_field_mult(struct hist_field *hist_field,
|
||||||
|
struct tracing_map_elt *elt,
|
||||||
|
struct trace_buffer *buffer,
|
||||||
|
struct ring_buffer_event *rbe,
|
||||||
|
void *event)
|
||||||
|
{
|
||||||
|
struct hist_field *operand1 = hist_field->operands[0];
|
||||||
|
struct hist_field *operand2 = hist_field->operands[1];
|
||||||
|
|
||||||
|
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
|
||||||
|
u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
|
||||||
|
|
||||||
|
return val1 * val2;
|
||||||
|
}
|
||||||
|
|
||||||
static u64 hist_field_unary_minus(struct hist_field *hist_field,
|
static u64 hist_field_unary_minus(struct hist_field *hist_field,
|
||||||
struct tracing_map_elt *elt,
|
struct tracing_map_elt *elt,
|
||||||
struct trace_buffer *buffer,
|
struct trace_buffer *buffer,
|
||||||
@ -1592,6 +1628,12 @@ static char *expr_str(struct hist_field *field, unsigned int level)
|
|||||||
case FIELD_OP_PLUS:
|
case FIELD_OP_PLUS:
|
||||||
strcat(expr, "+");
|
strcat(expr, "+");
|
||||||
break;
|
break;
|
||||||
|
case FIELD_OP_DIV:
|
||||||
|
strcat(expr, "/");
|
||||||
|
break;
|
||||||
|
case FIELD_OP_MULT:
|
||||||
|
strcat(expr, "*");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
kfree(expr);
|
kfree(expr);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1607,7 +1649,7 @@ static int contains_operator(char *str)
|
|||||||
enum field_op_id field_op = FIELD_OP_NONE;
|
enum field_op_id field_op = FIELD_OP_NONE;
|
||||||
char *op;
|
char *op;
|
||||||
|
|
||||||
op = strpbrk(str, "+-");
|
op = strpbrk(str, "+-/*");
|
||||||
if (!op)
|
if (!op)
|
||||||
return FIELD_OP_NONE;
|
return FIELD_OP_NONE;
|
||||||
|
|
||||||
@ -1628,6 +1670,12 @@ static int contains_operator(char *str)
|
|||||||
case '+':
|
case '+':
|
||||||
field_op = FIELD_OP_PLUS;
|
field_op = FIELD_OP_PLUS;
|
||||||
break;
|
break;
|
||||||
|
case '/':
|
||||||
|
field_op = FIELD_OP_DIV;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
field_op = FIELD_OP_MULT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2361,10 +2409,26 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
|||||||
case FIELD_OP_PLUS:
|
case FIELD_OP_PLUS:
|
||||||
sep = "+";
|
sep = "+";
|
||||||
break;
|
break;
|
||||||
|
case FIELD_OP_DIV:
|
||||||
|
sep = "/";
|
||||||
|
break;
|
||||||
|
case FIELD_OP_MULT:
|
||||||
|
sep = "*";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
goto free;
|
goto free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiplication and division are only supported in single operator
|
||||||
|
* expressions, since the expression is always evaluated from right
|
||||||
|
* to left.
|
||||||
|
*/
|
||||||
|
if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT) && level > 0) {
|
||||||
|
hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
operand1_str = strsep(&str, sep);
|
operand1_str = strsep(&str, sep);
|
||||||
if (!operand1_str || !str)
|
if (!operand1_str || !str)
|
||||||
goto free;
|
goto free;
|
||||||
@ -2436,6 +2500,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
|||||||
case FIELD_OP_PLUS:
|
case FIELD_OP_PLUS:
|
||||||
expr->fn = hist_field_plus;
|
expr->fn = hist_field_plus;
|
||||||
break;
|
break;
|
||||||
|
case FIELD_OP_DIV:
|
||||||
|
expr->fn = hist_field_div;
|
||||||
|
break;
|
||||||
|
case FIELD_OP_MULT:
|
||||||
|
expr->fn = hist_field_mult;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto free;
|
goto free;
|
||||||
|
Loading…
Reference in New Issue
Block a user