Skip to content

Commit e1fd0b2

Browse files
committed
Merge tag 'trace-v5.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull more tracing updates from Steven Rostedt: - osnoise and timerlat updates that will work with the RTLA tool (Real-Time Linux Analysis). Specifically it disconnects the work load (threads that look for latency) from the tracing instances attached to them, allowing for more than one instance to retrieve data from the work load. - Optimization on division in the trace histogram trigger code to use shift and multiply when possible. Also added documentation. - Fix prototype to my_direct_func in direct ftrace trampoline sample code. * tag 'trace-v5.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: ftrace/samples: Add missing prototype for my_direct_func tracing/selftests: Add tests for hist trigger expression parsing tracing/histogram: Document hist trigger variables tracing/histogram: Update division by 0 documentation tracing/histogram: Optimize division by constants tracing/osnoise: Remove PREEMPT_RT ifdefs from inside functions tracing/osnoise: Remove STACKTRACE ifdefs from inside functions tracing/osnoise: Allow multiple instances of the same tracer tracing/osnoise: Remove TIMERLAT ifdefs from inside functions tracing/osnoise: Support a list of trace_array *tr tracing/osnoise: Use start/stop_per_cpu_kthreads() on osnoise_cpus_write() tracing/osnoise: Split workload start from the tracer start tracing/osnoise: Improve comments about barrier need for NMI callbacks tracing/osnoise: Do not follow tracing_cpumask
2 parents 25edbc3 + 67d4f6e commit e1fd0b2

File tree

6 files changed

+616
-183
lines changed

6 files changed

+616
-183
lines changed

Documentation/trace/histogram.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,8 @@ using the same key and variable from yet another event::
17661766
Expressions support the use of addition, subtraction, multiplication and
17671767
division operators (+-\*/).
17681768

1769-
Note that division by zero always returns -1.
1769+
Note if division by zero cannot be detected at parse time (i.e. the
1770+
divisor is not a constant), the result will be -1.
17701771

17711772
Numeric constants can also be used directly in an expression::
17721773

kernel/trace/trace.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5605,6 +5605,7 @@ static const char readme_msg[] =
56055605
#ifdef CONFIG_HIST_TRIGGERS
56065606
" hist trigger\t- If set, event hits are aggregated into a hash table\n"
56075607
"\t Format: hist:keys=<field1[,field2,...]>\n"
5608+
"\t [:<var1>=<field|var_ref|numeric_literal>[,<var2>=...]]\n"
56085609
"\t [:values=<field1[,field2,...]>]\n"
56095610
"\t [:sort=<field1[,field2,...]>]\n"
56105611
"\t [:size=#entries]\n"
@@ -5616,6 +5617,16 @@ static const char readme_msg[] =
56165617
"\t common_timestamp - to record current timestamp\n"
56175618
"\t common_cpu - to record the CPU the event happened on\n"
56185619
"\n"
5620+
"\t A hist trigger variable can be:\n"
5621+
"\t - a reference to a field e.g. x=current_timestamp,\n"
5622+
"\t - a reference to another variable e.g. y=$x,\n"
5623+
"\t - a numeric literal: e.g. ms_per_sec=1000,\n"
5624+
"\t - an arithmetic expression: e.g. time_secs=current_timestamp/1000\n"
5625+
"\n"
5626+
"\t hist trigger aritmethic expressions support addition(+), subtraction(-),\n"
5627+
"\t multiplication(*) and division(/) operators. An operand can be either a\n"
5628+
"\t variable reference, field or numeric literal.\n"
5629+
"\n"
56195630
"\t When a matching event is hit, an entry is added to a hash\n"
56205631
"\t table using the key(s) and value(s) named, and the value of a\n"
56215632
"\t sum called 'hitcount' is incremented. Keys and values\n"

kernel/trace/trace_events_hist.c

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \
6969
C(INVALID_STR_OPERAND, "String type can not be an operand in expression"), \
7070
C(EXPECT_NUMBER, "Expecting numeric literal"), \
71-
C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"),
71+
C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"), \
72+
C(DIVISION_BY_ZERO, "Division by zero"),
7273

7374
#undef C
7475
#define C(a, b) HIST_ERR_##a
@@ -92,6 +93,7 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field,
9293
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
9394
#define HIST_ACTIONS_MAX 8
9495
#define HIST_CONST_DIGITS_MAX 21
96+
#define HIST_DIV_SHIFT 20 /* For optimizing division by constants */
9597

9698
enum field_op_id {
9799
FIELD_OP_NONE,
@@ -160,6 +162,8 @@ struct hist_field {
160162

161163
/* Numeric literals are represented as u64 */
162164
u64 constant;
165+
/* Used to optimize division by constants */
166+
u64 div_multiplier;
163167
};
164168

165169
static u64 hist_field_none(struct hist_field *field,
@@ -311,6 +315,68 @@ static u64 hist_field_div(struct hist_field *hist_field,
311315
return div64_u64(val1, val2);
312316
}
313317

318+
static u64 div_by_power_of_two(struct hist_field *hist_field,
319+
struct tracing_map_elt *elt,
320+
struct trace_buffer *buffer,
321+
struct ring_buffer_event *rbe,
322+
void *event)
323+
{
324+
struct hist_field *operand1 = hist_field->operands[0];
325+
struct hist_field *operand2 = hist_field->operands[1];
326+
327+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
328+
329+
return val1 >> __ffs64(operand2->constant);
330+
}
331+
332+
static u64 div_by_not_power_of_two(struct hist_field *hist_field,
333+
struct tracing_map_elt *elt,
334+
struct trace_buffer *buffer,
335+
struct ring_buffer_event *rbe,
336+
void *event)
337+
{
338+
struct hist_field *operand1 = hist_field->operands[0];
339+
struct hist_field *operand2 = hist_field->operands[1];
340+
341+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
342+
343+
return div64_u64(val1, operand2->constant);
344+
}
345+
346+
static u64 div_by_mult_and_shift(struct hist_field *hist_field,
347+
struct tracing_map_elt *elt,
348+
struct trace_buffer *buffer,
349+
struct ring_buffer_event *rbe,
350+
void *event)
351+
{
352+
struct hist_field *operand1 = hist_field->operands[0];
353+
struct hist_field *operand2 = hist_field->operands[1];
354+
355+
u64 val1 = operand1->fn(operand1, elt, buffer, rbe, event);
356+
357+
/*
358+
* If the divisor is a constant, do a multiplication and shift instead.
359+
*
360+
* Choose Z = some power of 2. If Y <= Z, then:
361+
* X / Y = (X * (Z / Y)) / Z
362+
*
363+
* (Z / Y) is a constant (mult) which is calculated at parse time, so:
364+
* X / Y = (X * mult) / Z
365+
*
366+
* The division by Z can be replaced by a shift since Z is a power of 2:
367+
* X / Y = (X * mult) >> HIST_DIV_SHIFT
368+
*
369+
* As long, as X < Z the results will not be off by more than 1.
370+
*/
371+
if (val1 < (1 << HIST_DIV_SHIFT)) {
372+
u64 mult = operand2->div_multiplier;
373+
374+
return (val1 * mult + ((1 << HIST_DIV_SHIFT) - 1)) >> HIST_DIV_SHIFT;
375+
}
376+
377+
return div64_u64(val1, operand2->constant);
378+
}
379+
314380
static u64 hist_field_mult(struct hist_field *hist_field,
315381
struct tracing_map_elt *elt,
316382
struct trace_buffer *buffer,
@@ -573,6 +639,25 @@ struct snapshot_context {
573639
void *key;
574640
};
575641

642+
/*
643+
* Returns the specific division function to use if the divisor
644+
* is constant. This avoids extra branches when the trigger is hit.
645+
*/
646+
static hist_field_fn_t hist_field_get_div_fn(struct hist_field *divisor)
647+
{
648+
u64 div = divisor->constant;
649+
650+
if (!(div & (div - 1)))
651+
return div_by_power_of_two;
652+
653+
/* If the divisor is too large, do a regular division */
654+
if (div > (1 << HIST_DIV_SHIFT))
655+
return div_by_not_power_of_two;
656+
657+
divisor->div_multiplier = div64_u64((u64)(1 << HIST_DIV_SHIFT), div);
658+
return div_by_mult_and_shift;
659+
}
660+
576661
static void track_data_free(struct track_data *track_data)
577662
{
578663
struct hist_elt_data *elt_data;
@@ -2575,6 +2660,24 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
25752660
expr->operands[0] = operand1;
25762661
expr->operands[1] = operand2;
25772662

2663+
if (field_op == FIELD_OP_DIV &&
2664+
operand2_flags & HIST_FIELD_FL_CONST) {
2665+
u64 divisor = var2 ? var2->constant : operand2->constant;
2666+
2667+
if (!divisor) {
2668+
hist_err(file->tr, HIST_ERR_DIVISION_BY_ZERO, errpos(str));
2669+
ret = -EDOM;
2670+
goto free;
2671+
}
2672+
2673+
/*
2674+
* Copy the divisor here so we don't have to look it up
2675+
* later if this is a var ref
2676+
*/
2677+
operand2->constant = divisor;
2678+
op_fn = hist_field_get_div_fn(operand2);
2679+
}
2680+
25782681
if (combine_consts) {
25792682
if (var1)
25802683
expr->operands[0] = var1;

0 commit comments

Comments
 (0)