Skip to content

Commit

Permalink
tracing/hist: add modulus operator
Browse files Browse the repository at this point in the history
Currently historgram field expressions can use addition ('+'),
substraction ('-'), division ('/'), and multiplication ('*') operators.
It would be helpful to also have a modulus ('%') operator.

This is helpful for capturing the alignment of pointers. For example, on
arm64 with CONFIG_KPROBE_EVENTS_ON_NOTRACE=y, we can record the size and
alignment of copies to user with:

| # echo 'p:copy_to_user __arch_copy_to_user to=$arg1 from=$arg2 n=$arg3' >> /sys/kernel/tracing/kprobe_events
| # echo 'hist keys=n,to%8:vals=hitcount:sort=n,to%8' > /sys/kernel/tracing/events/kprobes/copy_to_user/trigger
| # cat /sys/kernel/tracing/events/kprobes/copy_to_user/hist
| # event histogram
| #
| # trigger info: hist:keys=n,to%8:vals=hitcount:sort=n,to%8:size=2048 [active]
| #
|
| { n:          1, to%8:          1 } hitcount:          5
| { n:          8, to%8:          0 } hitcount:          3
| { n:         16, to%8:          0 } hitcount:          2
| { n:         32, to%8:          0 } hitcount:          1
| { n:         36, to%8:          0 } hitcount:          1
| { n:        128, to%8:          0 } hitcount:          4
| { n:        336, to%8:          0 } hitcount:          1
| { n:        832, to%8:          0 } hitcount:          3
|
| Totals:
|     Hits: 20
|     Entries: 8
|     Dropped: 0

Add a modulus operator, with the same precedence as multiplication and
division, matching C's operator precedence.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Steven Rostedt (Google) <rostedt@goodmis.org>
Cc: Tom Zanussi <zanussi@kernel.org>
Cc: linux-trace-kernel@vger.kernel.org
  • Loading branch information
Mark Rutland authored and intel-lab-lkp committed Mar 2, 2023
1 parent c04f07b commit a2602e9
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Documentation/trace/histogram.rst
Expand Up @@ -1772,8 +1772,8 @@ using the same key and variable from yet another event::

# echo 'hist:key=pid:wakeupswitch_lat=$wakeup_lat+$switchtime_lat ...' >> event3/trigger

Expressions support the use of addition, subtraction, multiplication and
division operators (+-\*/).
Expressions support the use of addition, subtraction, multiplication,
division, modulus operators (+-\*/%).

Note if division by zero cannot be detected at parse time (i.e. the
divisor is not a constant), the result will be -1.
Expand Down
35 changes: 33 additions & 2 deletions kernel/trace/trace_events_hist.c
Expand Up @@ -103,6 +103,7 @@ enum field_op_id {
FIELD_OP_UNARY_MINUS,
FIELD_OP_DIV,
FIELD_OP_MULT,
FIELD_OP_MOD,
};

enum hist_field_fn {
Expand Down Expand Up @@ -131,6 +132,7 @@ enum hist_field_fn {
HIST_FIELD_FN_PLUS,
HIST_FIELD_FN_DIV,
HIST_FIELD_FN_MULT,
HIST_FIELD_FN_MOD,
HIST_FIELD_FN_DIV_POWER2,
HIST_FIELD_FN_DIV_NOT_POWER2,
HIST_FIELD_FN_DIV_MULT_SHIFT,
Expand Down Expand Up @@ -437,6 +439,21 @@ static u64 hist_field_mult(struct hist_field *hist_field,
return val1 * val2;
}

static u64 hist_field_mod(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 = hist_fn_call(operand1, elt, buffer, rbe, event);
u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event);

return val1 % val2;
}

static u64 hist_field_unary_minus(struct hist_field *hist_field,
struct tracing_map_elt *elt,
struct trace_buffer *buffer,
Expand Down Expand Up @@ -1797,6 +1814,9 @@ static char *expr_str(struct hist_field *field, unsigned int level)
case FIELD_OP_MULT:
strcat(expr, "*");
break;
case FIELD_OP_MOD:
strcat(expr, "%");
break;
default:
kfree(expr);
return NULL;
Expand Down Expand Up @@ -1860,8 +1880,8 @@ static int contains_operator(char *str, char **sep)
return field_op;

/*
* Second, consider the higher precedence multiplication and division
* operators.
* Second, consider the higher precedence multiplication, division, and
* modulus operators.
*/
op = strrchr(str, '/');
if (op > *sep) {
Expand All @@ -1875,6 +1895,12 @@ static int contains_operator(char *str, char **sep)
field_op = FIELD_OP_MULT;
}

op = strrchr(str, '%');
if (op > *sep) {
*sep = op;
field_op = FIELD_OP_MOD;
}

return field_op;
}

Expand Down Expand Up @@ -2710,6 +2736,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
case FIELD_OP_MULT:
op_fn = HIST_FIELD_FN_MULT;
break;
case FIELD_OP_MOD:
op_fn = HIST_FIELD_FN_MOD;
break;
default:
ret = -EINVAL;
goto free_operands;
Expand Down Expand Up @@ -4326,6 +4355,8 @@ static u64 hist_fn_call(struct hist_field *hist_field,
return hist_field_div(hist_field, elt, buffer, rbe, event);
case HIST_FIELD_FN_MULT:
return hist_field_mult(hist_field, elt, buffer, rbe, event);
case HIST_FIELD_FN_MOD:
return hist_field_mod(hist_field, elt, buffer, rbe, event);
case HIST_FIELD_FN_DIV_POWER2:
return div_by_power_of_two(hist_field, elt, buffer, rbe, event);
case HIST_FIELD_FN_DIV_NOT_POWER2:
Expand Down

0 comments on commit a2602e9

Please sign in to comment.