Skip to content

Commit bcef044

Browse files
Kalesh Singhrostedt
authored andcommitted
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/[email protected] Signed-off-by: Kalesh Singh <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 52cfb37 commit bcef044

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

kernel/trace/trace_events_hist.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ enum field_op_id {
9797
FIELD_OP_PLUS,
9898
FIELD_OP_MINUS,
9999
FIELD_OP_UNARY_MINUS,
100+
FIELD_OP_DIV,
101+
FIELD_OP_MULT,
100102
};
101103

102104
/*
@@ -285,6 +287,40 @@ static u64 hist_field_minus(struct hist_field *hist_field,
285287
return val1 - val2;
286288
}
287289

290+
static u64 hist_field_div(struct hist_field *hist_field,
291+
struct tracing_map_elt *elt,
292+
struct trace_buffer *buffer,
293+
struct ring_buffer_event *rbe,
294+
void *event)
295+
{
296+
struct hist_field *operand1 = hist_field->operands[0];
297+
struct hist_field *operand2 = hist_field->operands[1];
298+
299+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
300+
u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
301+
302+
/* Return -1 for the undefined case */
303+
if (!val2)
304+
return -1;
305+
306+
return div64_u64(val1, val2);
307+
}
308+
309+
static u64 hist_field_mult(struct hist_field *hist_field,
310+
struct tracing_map_elt *elt,
311+
struct trace_buffer *buffer,
312+
struct ring_buffer_event *rbe,
313+
void *event)
314+
{
315+
struct hist_field *operand1 = hist_field->operands[0];
316+
struct hist_field *operand2 = hist_field->operands[1];
317+
318+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
319+
u64 val2 = operand2->fn(operand2, elt, buffer, rbe, event);
320+
321+
return val1 * val2;
322+
}
323+
288324
static u64 hist_field_unary_minus(struct hist_field *hist_field,
289325
struct tracing_map_elt *elt,
290326
struct trace_buffer *buffer,
@@ -1592,6 +1628,12 @@ static char *expr_str(struct hist_field *field, unsigned int level)
15921628
case FIELD_OP_PLUS:
15931629
strcat(expr, "+");
15941630
break;
1631+
case FIELD_OP_DIV:
1632+
strcat(expr, "/");
1633+
break;
1634+
case FIELD_OP_MULT:
1635+
strcat(expr, "*");
1636+
break;
15951637
default:
15961638
kfree(expr);
15971639
return NULL;
@@ -1607,7 +1649,7 @@ static int contains_operator(char *str)
16071649
enum field_op_id field_op = FIELD_OP_NONE;
16081650
char *op;
16091651

1610-
op = strpbrk(str, "+-");
1652+
op = strpbrk(str, "+-/*");
16111653
if (!op)
16121654
return FIELD_OP_NONE;
16131655

@@ -1628,6 +1670,12 @@ static int contains_operator(char *str)
16281670
case '+':
16291671
field_op = FIELD_OP_PLUS;
16301672
break;
1673+
case '/':
1674+
field_op = FIELD_OP_DIV;
1675+
break;
1676+
case '*':
1677+
field_op = FIELD_OP_MULT;
1678+
break;
16311679
default:
16321680
break;
16331681
}
@@ -2361,10 +2409,26 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
23612409
case FIELD_OP_PLUS:
23622410
sep = "+";
23632411
break;
2412+
case FIELD_OP_DIV:
2413+
sep = "/";
2414+
break;
2415+
case FIELD_OP_MULT:
2416+
sep = "*";
2417+
break;
23642418
default:
23652419
goto free;
23662420
}
23672421

2422+
/*
2423+
* Multiplication and division are only supported in single operator
2424+
* expressions, since the expression is always evaluated from right
2425+
* to left.
2426+
*/
2427+
if ((field_op == FIELD_OP_DIV || field_op == FIELD_OP_MULT) && level > 0) {
2428+
hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
2429+
return ERR_PTR(-EINVAL);
2430+
}
2431+
23682432
operand1_str = strsep(&str, sep);
23692433
if (!operand1_str || !str)
23702434
goto free;
@@ -2436,6 +2500,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
24362500
case FIELD_OP_PLUS:
24372501
expr->fn = hist_field_plus;
24382502
break;
2503+
case FIELD_OP_DIV:
2504+
expr->fn = hist_field_div;
2505+
break;
2506+
case FIELD_OP_MULT:
2507+
expr->fn = hist_field_mult;
2508+
break;
24392509
default:
24402510
ret = -EINVAL;
24412511
goto free;

0 commit comments

Comments
 (0)