// SPDX-License-Identifier: GPL-2.0/* * trace_events_hist - trace event hist triggers * * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h>#include <linux/kallsyms.h>#include <linux/security.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/stacktrace.h>#include <linux/rculist.h>#include <linux/tracefs.h> /* for gfp flag names */#include <linux/trace_events.h>#include <trace/events/mmflags.h> #include "tracing_map.h"#include "trace_synth.h" #define ERRORS \ C(NONE, "No error"), \ C(DUPLICATE_VAR, "Variable already defined"), \ C(VAR_NOT_UNIQUE, "Variable name not unique, need to use fully qualified name (subsys.event.var) for variable"), \ C(TOO_MANY_VARS, "Too many variables defined"), \ C(MALFORMED_ASSIGNMENT, "Malformed assignment"), \ C(NAMED_MISMATCH, "Named hist trigger doesn't match existing named trigger (includes variables)"), \ C(TRIGGER_EEXIST, "Hist trigger already exists"), \ C(TRIGGER_ENOENT_CLEAR, "Can't clear or continue a nonexistent hist trigger"), \ C(SET_CLOCK_FAIL, "Couldn't set trace_clock"), \ C(BAD_FIELD_MODIFIER, "Invalid field modifier"), \ C(TOO_MANY_SUBEXPR, "Too many subexpressions (3 max)"), \ C(TIMESTAMP_MISMATCH, "Timestamp units in expression don't match"), \ C(TOO_MANY_FIELD_VARS, "Too many field variables defined"), \ C(EVENT_FILE_NOT_FOUND, "Event file not found"), \ C(HIST_NOT_FOUND, "Matching event histogram not found"), \ C(HIST_CREATE_FAIL, "Couldn't create histogram for field"), \ C(SYNTH_VAR_NOT_FOUND, "Couldn't find synthetic variable"), \ C(SYNTH_EVENT_NOT_FOUND,"Couldn't find synthetic event"), \ C(SYNTH_TYPE_MISMATCH, "Param type doesn't match synthetic event field type"), \ C(SYNTH_COUNT_MISMATCH, "Param count doesn't match synthetic event field count"), \ C(FIELD_VAR_PARSE_FAIL, "Couldn't parse field variable"), \ C(VAR_CREATE_FIND_FAIL, "Couldn't create or find variable"), \ C(ONX_NOT_VAR, "For onmax(x) or onchange(x), x must be a variable"), \ C(ONX_VAR_NOT_FOUND, "Couldn't find onmax or onchange variable"), \ C(ONX_VAR_CREATE_FAIL, "Couldn't create onmax or onchange variable"), \ C(FIELD_VAR_CREATE_FAIL,"Couldn't create field variable"), \ C(TOO_MANY_PARAMS, "Too many action params"), \ C(PARAM_NOT_FOUND, "Couldn't find param"), \ C(INVALID_PARAM, "Invalid action param"), \ C(ACTION_NOT_FOUND, "No action found"), \ C(NO_SAVE_PARAMS, "No params found for save()"), \ C(TOO_MANY_SAVE_ACTIONS,"Can't have more than one save() action per hist"), \ C(ACTION_MISMATCH, "Handler doesn't support action"), \ C(NO_CLOSING_PAREN, "No closing paren found"), \ C(SUBSYS_NOT_FOUND, "Missing subsystem"), \ C(INVALID_SUBSYS_EVENT, "Invalid subsystem or event name"), \ C(INVALID_REF_KEY, "Using variable references in keys not supported"), \ C(VAR_NOT_FOUND, "Couldn't find variable"), \ C(FIELD_NOT_FOUND, "Couldn't find field"), \ C(EMPTY_ASSIGNMENT, "Empty assignment"), \ C(INVALID_SORT_MODIFIER,"Invalid sort modifier"), \ C(EMPTY_SORT_FIELD, "Empty sort field"), \ C(TOO_MANY_SORT_FIELDS, "Too many sort fields (Max = 2)"), \ C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \ C(INVALID_STR_OPERAND, "String type can not be an operand in expression"), \ C(EXPECT_NUMBER, "Expecting numeric literal"), \ C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"), \ C(DIVISION_BY_ZERO, "Division by zero"), \ C(NEED_NOHC_VAL, "Non-hitcount value is required for 'nohitcount'"), #undef C#define C(a, b) HIST_ERR_##a enum { ERRORS }; #undef C#define C(a, b) b static const char *err_text[] = { ERRORS }; struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event); #define HIST_FIELD_OPERANDS_MAX 2#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)#define HIST_ACTIONS_MAX 8#define HIST_CONST_DIGITS_MAX 21#define HIST_DIV_SHIFT 20 /* For optimizing division by constants */ enum field_op_id { FIELD_OP_NONE, FIELD_OP_PLUS, FIELD_OP_MINUS, FIELD_OP_UNARY_MINUS, FIELD_OP_DIV, FIELD_OP_MULT,}; enum hist_field_fn { HIST_FIELD_FN_NOP, HIST_FIELD_FN_VAR_REF, HIST_FIELD_FN_COUNTER, HIST_FIELD_FN_CONST, HIST_FIELD_FN_LOG2, HIST_FIELD_FN_BUCKET, HIST_FIELD_FN_TIMESTAMP, HIST_FIELD_FN_CPU, HIST_FIELD_FN_STRING, HIST_FIELD_FN_DYNSTRING, HIST_FIELD_FN_RELDYNSTRING, HIST_FIELD_FN_PSTRING, HIST_FIELD_FN_S64, HIST_FIELD_FN_U64, HIST_FIELD_FN_S32, HIST_FIELD_FN_U32, HIST_FIELD_FN_S16, HIST_FIELD_FN_U16, HIST_FIELD_FN_S8, HIST_FIELD_FN_U8, HIST_FIELD_FN_UMINUS, HIST_FIELD_FN_MINUS, HIST_FIELD_FN_PLUS, HIST_FIELD_FN_DIV, HIST_FIELD_FN_MULT, HIST_FIELD_FN_DIV_POWER2, HIST_FIELD_FN_DIV_NOT_POWER2, HIST_FIELD_FN_DIV_MULT_SHIFT, HIST_FIELD_FN_EXECNAME, HIST_FIELD_FN_STACK,}; /* * A hist_var (histogram variable) contains variable information for * hist_fields having the HIST_FIELD_FL_VAR or HIST_FIELD_FL_VAR_REF * flag set. A hist_var has a variable name e.g. ts0, and is * associated with a given histogram trigger, as specified by * hist_data. The hist_var idx is the unique index assigned to the * variable by the hist trigger's tracing_map. The idx is what is * used to set a variable's value and, by a variable reference, to * retrieve it. */struct hist_var { char *name; struct hist_trigger_data *hist_data; unsigned int idx;}; struct hist_field { struct ftrace_event_field *field; unsigned long flags; unsigned long buckets; const char *type; struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; struct hist_trigger_data *hist_data; enum hist_field_fn fn_num; unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; /* * Variable fields contain variable-specific info in var. */ struct hist_var var; enum field_op_id operator; char *system; char *event_name; /* * The name field is used for EXPR and VAR_REF fields. VAR * fields contain the variable name in var.name. */ char *name; /* * When a histogram trigger is hit, if it has any references * to variables, the values of those variables are collected * into a var_ref_vals array by resolve_var_refs(). The * current value of each variable is read from the tracing_map * using the hist field's hist_var.idx and entered into the * var_ref_idx entry i.e. var_ref_vals[var_ref_idx]. */ unsigned int var_ref_idx; bool read_once; unsigned int var_str_idx; /* Numeric literals are represented as u64 */ u64 constant; /* Used to optimize division by constants */ u64 div_multiplier;}; static u64 hist_fn_call(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event); static u64 hist_field_const(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ return field->constant;} static u64 hist_field_counter(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ return 1;} static u64 hist_field_string(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ char *addr = (char *)(event + hist_field->field->offset); return (u64)(unsigned long)addr;} static u64 hist_field_dynstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ u32 str_item = *(u32 *)(event + hist_field->field->offset); int str_loc = str_item & 0xffff; char *addr = (char *)(event + str_loc); return (u64)(unsigned long)addr;} static u64 hist_field_reldynstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ u32 *item = event + hist_field->field->offset; u32 str_item = *item; int str_loc = str_item & 0xffff; char *addr = (char *)&item[1] + str_loc; return (u64)(unsigned long)addr;} static u64 hist_field_pstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ char **addr = (char **)(event + hist_field->field->offset); return (u64)(unsigned long)*addr;} static u64 hist_field_log2(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ struct hist_field *operand = hist_field->operands[0]; u64 val = hist_fn_call(operand, elt, buffer, rbe, event); return (u64) ilog2(roundup_pow_of_two(val));} static u64 hist_field_bucket(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ struct hist_field *operand = hist_field->operands[0]; unsigned long buckets = hist_field->buckets; u64 val = hist_fn_call(operand, elt, buffer, rbe, event); if (WARN_ON_ONCE(!buckets)) return val; if (val >= LONG_MAX) val = div64_ul(val, buckets); else val = (u64)((unsigned long)val / buckets); return val * buckets;} static u64 hist_field_plus(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_minus(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_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 = hist_fn_call(operand1, elt, buffer, rbe, event); u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event); /* Return -1 for the undefined case */ if (!val2) return -1; /* Use shift if the divisor is a power of 2 */ if (!(val2 & (val2 - 1))) return val1 >> __ffs64(val2); return div64_u64(val1, val2);} static u64 div_by_power_of_two(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); return val1 >> __ffs64(operand2->constant);} static u64 div_by_not_power_of_two(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); return div64_u64(val1, operand2->constant);} static u64 div_by_mult_and_shift(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); /* * If the divisor is a constant, do a multiplication and shift instead. * * Choose Z = some power of 2. If Y <= Z, then: * X / Y = (X * (Z / Y)) / Z * * (Z / Y) is a constant (mult) which is calculated at parse time, so: * X / Y = (X * mult) / Z * * The division by Z can be replaced by a shift since Z is a power of 2: * X / Y = (X * mult) >> HIST_DIV_SHIFT * * As long, as X < Z the results will not be off by more than 1. */ if (val1 < (1 << HIST_DIV_SHIFT)) { u64 mult = operand2->div_multiplier; return (val1 * mult + ((1 << HIST_DIV_SHIFT) - 1)) >> HIST_DIV_SHIFT; } return div64_u64(val1, operand2->constant);} 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 = 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, struct ring_buffer_event *rbe, void *event){ struct hist_field *operand = hist_field->operands[0]; s64 sval = (s64)hist_fn_call(operand, elt, buffer, rbe, event); u64 val = (u64)-sval; return val;} #define DEFINE_HIST_FIELD_FN(type) \ static u64 hist_field_##type(struct hist_field *hist_field, \ struct tracing_map_elt *elt, \ struct trace_buffer *buffer, \ struct ring_buffer_event *rbe, \ void *event) \{ \ type *addr = (type *)(event + hist_field->field->offset); \ \ return (u64)(unsigned long)*addr; \} DEFINE_HIST_FIELD_FN(s64);DEFINE_HIST_FIELD_FN(u64);DEFINE_HIST_FIELD_FN(s32);DEFINE_HIST_FIELD_FN(u32);DEFINE_HIST_FIELD_FN(s16);DEFINE_HIST_FIELD_FN(u16);DEFINE_HIST_FIELD_FN(s8);DEFINE_HIST_FIELD_FN(u8); #define for_each_hist_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) #define for_each_hist_val_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) #define for_each_hist_key_field(i, hist_data) \ for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) #define HITCOUNT_IDX 0#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE) enum hist_field_flags { HIST_FIELD_FL_HITCOUNT = 1 << 0, HIST_FIELD_FL_KEY = 1 << 1, HIST_FIELD_FL_STRING = 1 << 2, HIST_FIELD_FL_HEX = 1 << 3, HIST_FIELD_FL_SYM = 1 << 4, HIST_FIELD_FL_SYM_OFFSET = 1 << 5, HIST_FIELD_FL_EXECNAME = 1 << 6, HIST_FIELD_FL_SYSCALL = 1 << 7, HIST_FIELD_FL_STACKTRACE = 1 << 8, HIST_FIELD_FL_LOG2 = 1 << 9, HIST_FIELD_FL_TIMESTAMP = 1 << 10, HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, HIST_FIELD_FL_VAR = 1 << 12,Lines 1–500 of 6855. Load more
// SPDX-License-Identifier: GPL-2.0 /* * trace_events_hist - trace event hist triggers * * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h> #include <linux/kallsyms.h> #include <linux/security.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/stacktrace.h> #include <linux/rculist.h> #include <linux/tracefs.h> /* for gfp flag names */ #include <linux/trace_events.h> #include <trace/events/mmflags.h> #include "tracing_map.h" #include "trace_synth.h" #define ERRORS \ C(NONE, "No error"), \ C(DUPLICATE_VAR, "Variable already defined"), \ C(VAR_NOT_UNIQUE, "Variable name not unique, need to use fully qualified name (subsys.event.var) for variable"), \ C(TOO_MANY_VARS, "Too many variables defined"), \ C(MALFORMED_ASSIGNMENT, "Malformed assignment"), \ C(NAMED_MISMATCH, "Named hist trigger doesn't match existing named trigger (includes variables)"), \ C(TRIGGER_EEXIST, "Hist trigger already exists"), \ C(TRIGGER_ENOENT_CLEAR, "Can't clear or continue a nonexistent hist trigger"), \ C(SET_CLOCK_FAIL, "Couldn't set trace_clock"), \ C(BAD_FIELD_MODIFIER, "Invalid field modifier"), \ C(TOO_MANY_SUBEXPR, "Too many subexpressions (3 max)"), \ C(TIMESTAMP_MISMATCH, "Timestamp units in expression don't match"), \ C(TOO_MANY_FIELD_VARS, "Too many field variables defined"), \ C(EVENT_FILE_NOT_FOUND, "Event file not found"), \ C(HIST_NOT_FOUND, "Matching event histogram not found"), \ C(HIST_CREATE_FAIL, "Couldn't create histogram for field"), \ C(SYNTH_VAR_NOT_FOUND, "Couldn't find synthetic variable"), \ C(SYNTH_EVENT_NOT_FOUND,"Couldn't find synthetic event"), \ C(SYNTH_TYPE_MISMATCH, "Param type doesn't match synthetic event field type"), \ C(SYNTH_COUNT_MISMATCH, "Param count doesn't match synthetic event field count"), \ C(FIELD_VAR_PARSE_FAIL, "Couldn't parse field variable"), \ C(VAR_CREATE_FIND_FAIL, "Couldn't create or find variable"), \ C(ONX_NOT_VAR, "For onmax(x) or onchange(x), x must be a variable"), \ C(ONX_VAR_NOT_FOUND, "Couldn't find onmax or onchange variable"), \ C(ONX_VAR_CREATE_FAIL, "Couldn't create onmax or onchange variable"), \ C(FIELD_VAR_CREATE_FAIL,"Couldn't create field variable"), \ C(TOO_MANY_PARAMS, "Too many action params"), \ C(PARAM_NOT_FOUND, "Couldn't find param"), \ C(INVALID_PARAM, "Invalid action param"), \ C(ACTION_NOT_FOUND, "No action found"), \ C(NO_SAVE_PARAMS, "No params found for save()"), \ C(TOO_MANY_SAVE_ACTIONS,"Can't have more than one save() action per hist"), \ C(ACTION_MISMATCH, "Handler doesn't support action"), \ C(NO_CLOSING_PAREN, "No closing paren found"), \ C(SUBSYS_NOT_FOUND, "Missing subsystem"), \ C(INVALID_SUBSYS_EVENT, "Invalid subsystem or event name"), \ C(INVALID_REF_KEY, "Using variable references in keys not supported"), \ C(VAR_NOT_FOUND, "Couldn't find variable"), \ C(FIELD_NOT_FOUND, "Couldn't find field"), \ C(EMPTY_ASSIGNMENT, "Empty assignment"), \ C(INVALID_SORT_MODIFIER,"Invalid sort modifier"), \ C(EMPTY_SORT_FIELD, "Empty sort field"), \ C(TOO_MANY_SORT_FIELDS, "Too many sort fields (Max = 2)"), \ C(INVALID_SORT_FIELD, "Sort field must be a key or a val"), \ C(INVALID_STR_OPERAND, "String type can not be an operand in expression"), \ C(EXPECT_NUMBER, "Expecting numeric literal"), \ C(UNARY_MINUS_SUBEXPR, "Unary minus not supported in sub-expressions"), \ C(DIVISION_BY_ZERO, "Division by zero"), \ C(NEED_NOHC_VAL, "Non-hitcount value is required for 'nohitcount'"), #undef C #define C(a, b) HIST_ERR_##a enum { ERRORS }; #undef C #define C(a, b) b static const char *err_text[] = { ERRORS }; struct hist_field; typedef u64 (*hist_field_fn_t) (struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event); #define HIST_FIELD_OPERANDS_MAX 2 #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) #define HIST_ACTIONS_MAX 8 #define HIST_CONST_DIGITS_MAX 21 #define HIST_DIV_SHIFT 20 /* For optimizing division by constants */ enum field_op_id { FIELD_OP_NONE, FIELD_OP_PLUS, FIELD_OP_MINUS, FIELD_OP_UNARY_MINUS, FIELD_OP_DIV, FIELD_OP_MULT, }; enum hist_field_fn { HIST_FIELD_FN_NOP, HIST_FIELD_FN_VAR_REF, HIST_FIELD_FN_COUNTER, HIST_FIELD_FN_CONST, HIST_FIELD_FN_LOG2, HIST_FIELD_FN_BUCKET, HIST_FIELD_FN_TIMESTAMP, HIST_FIELD_FN_CPU, HIST_FIELD_FN_STRING, HIST_FIELD_FN_DYNSTRING, HIST_FIELD_FN_RELDYNSTRING, HIST_FIELD_FN_PSTRING, HIST_FIELD_FN_S64, HIST_FIELD_FN_U64, HIST_FIELD_FN_S32, HIST_FIELD_FN_U32, HIST_FIELD_FN_S16, HIST_FIELD_FN_U16, HIST_FIELD_FN_S8, HIST_FIELD_FN_U8, HIST_FIELD_FN_UMINUS, HIST_FIELD_FN_MINUS, HIST_FIELD_FN_PLUS, HIST_FIELD_FN_DIV, HIST_FIELD_FN_MULT, HIST_FIELD_FN_DIV_POWER2, HIST_FIELD_FN_DIV_NOT_POWER2, HIST_FIELD_FN_DIV_MULT_SHIFT, HIST_FIELD_FN_EXECNAME, HIST_FIELD_FN_STACK, }; /* * A hist_var (histogram variable) contains variable information for * hist_fields having the HIST_FIELD_FL_VAR or HIST_FIELD_FL_VAR_REF * flag set. A hist_var has a variable name e.g. ts0, and is * associated with a given histogram trigger, as specified by * hist_data. The hist_var idx is the unique index assigned to the * variable by the hist trigger's tracing_map. The idx is what is * used to set a variable's value and, by a variable reference, to * retrieve it. */ struct hist_var { char *name; struct hist_trigger_data *hist_data; unsigned int idx; }; struct hist_field { struct ftrace_event_field *field; unsigned long flags; unsigned long buckets; const char *type; struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; struct hist_trigger_data *hist_data; enum hist_field_fn fn_num; unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; /* * Variable fields contain variable-specific info in var. */ struct hist_var var; enum field_op_id operator; char *system; char *event_name; /* * The name field is used for EXPR and VAR_REF fields. VAR * fields contain the variable name in var.name. */ char *name; /* * When a histogram trigger is hit, if it has any references * to variables, the values of those variables are collected * into a var_ref_vals array by resolve_var_refs(). The * current value of each variable is read from the tracing_map * using the hist field's hist_var.idx and entered into the * var_ref_idx entry i.e. var_ref_vals[var_ref_idx]. */ unsigned int var_ref_idx; bool read_once; unsigned int var_str_idx; /* Numeric literals are represented as u64 */ u64 constant; /* Used to optimize division by constants */ u64 div_multiplier; }; static u64 hist_fn_call(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event); static u64 hist_field_const(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { return field->constant; } static u64 hist_field_counter(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { return 1; } static u64 hist_field_string(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { char *addr = (char *)(event + hist_field->field->offset); return (u64)(unsigned long)addr; } static u64 hist_field_dynstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { u32 str_item = *(u32 *)(event + hist_field->field->offset); int str_loc = str_item & 0xffff; char *addr = (char *)(event + str_loc); return (u64)(unsigned long)addr; } static u64 hist_field_reldynstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { u32 *item = event + hist_field->field->offset; u32 str_item = *item; int str_loc = str_item & 0xffff; char *addr = (char *)&item[1] + str_loc; return (u64)(unsigned long)addr; } static u64 hist_field_pstring(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { char **addr = (char **)(event + hist_field->field->offset); return (u64)(unsigned long)*addr; } static u64 hist_field_log2(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { struct hist_field *operand = hist_field->operands[0]; u64 val = hist_fn_call(operand, elt, buffer, rbe, event); return (u64) ilog2(roundup_pow_of_two(val)); } static u64 hist_field_bucket(struct hist_field *hist_field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { struct hist_field *operand = hist_field->operands[0]; unsigned long buckets = hist_field->buckets; u64 val = hist_fn_call(operand, elt, buffer, rbe, event); if (WARN_ON_ONCE(!buckets)) return val; if (val >= LONG_MAX) val = div64_ul(val, buckets); else val = (u64)((unsigned long)val / buckets); return val * buckets; } static u64 hist_field_plus(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_minus(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_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 = hist_fn_call(operand1, elt, buffer, rbe, event); u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event); /* Return -1 for the undefined case */ if (!val2) return -1; /* Use shift if the divisor is a power of 2 */ if (!(val2 & (val2 - 1))) return val1 >> __ffs64(val2); return div64_u64(val1, val2); } static u64 div_by_power_of_two(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); return val1 >> __ffs64(operand2->constant); } static u64 div_by_not_power_of_two(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); return div64_u64(val1, operand2->constant); } static u64 div_by_mult_and_shift(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); /* * If the divisor is a constant, do a multiplication and shift instead. * * Choose Z = some power of 2. If Y <= Z, then: * X / Y = (X * (Z / Y)) / Z * * (Z / Y) is a constant (mult) which is calculated at parse time, so: * X / Y = (X * mult) / Z * * The division by Z can be replaced by a shift since Z is a power of 2: * X / Y = (X * mult) >> HIST_DIV_SHIFT * * As long, as X < Z the results will not be off by more than 1. */ if (val1 < (1 << HIST_DIV_SHIFT)) { u64 mult = operand2->div_multiplier; return (val1 * mult + ((1 << HIST_DIV_SHIFT) - 1)) >> HIST_DIV_SHIFT; } return div64_u64(val1, operand2->constant); } 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 = 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, struct ring_buffer_event *rbe, void *event) { struct hist_field *operand = hist_field->operands[0]; s64 sval = (s64)hist_fn_call(operand, elt, buffer, rbe, event); u64 val = (u64)-sval; return val; } #define DEFINE_HIST_FIELD_FN(type) \ static u64 hist_field_##type(struct hist_field *hist_field, \ struct tracing_map_elt *elt, \ struct trace_buffer *buffer, \ struct ring_buffer_event *rbe, \ void *event) \ { \ type *addr = (type *)(event + hist_field->field->offset); \ \ return (u64)(unsigned long)*addr; \ } DEFINE_HIST_FIELD_FN(s64); DEFINE_HIST_FIELD_FN(u64); DEFINE_HIST_FIELD_FN(s32); DEFINE_HIST_FIELD_FN(u32); DEFINE_HIST_FIELD_FN(s16); DEFINE_HIST_FIELD_FN(u16); DEFINE_HIST_FIELD_FN(s8); DEFINE_HIST_FIELD_FN(u8); #define for_each_hist_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_fields; (i)++) #define for_each_hist_val_field(i, hist_data) \ for ((i) = 0; (i) < (hist_data)->n_vals; (i)++) #define for_each_hist_key_field(i, hist_data) \ for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++) #define HITCOUNT_IDX 0 #define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE) enum hist_field_flags { HIST_FIELD_FL_HITCOUNT = 1 << 0, HIST_FIELD_FL_KEY = 1 << 1, HIST_FIELD_FL_STRING = 1 << 2, HIST_FIELD_FL_HEX = 1 << 3, HIST_FIELD_FL_SYM = 1 << 4, HIST_FIELD_FL_SYM_OFFSET = 1 << 5, HIST_FIELD_FL_EXECNAME = 1 << 6, HIST_FIELD_FL_SYSCALL = 1 << 7, HIST_FIELD_FL_STACKTRACE = 1 << 8, HIST_FIELD_FL_LOG2 = 1 << 9, HIST_FIELD_FL_TIMESTAMP = 1 << 10, HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, HIST_FIELD_FL_VAR = 1 << 12,