// 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"), #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 enum field_op_id { FIELD_OP_NONE, FIELD_OP_PLUS, FIELD_OP_MINUS, FIELD_OP_UNARY_MINUS,}; /* * 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; hist_field_fn_t fn; unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; const char *type; struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; struct hist_trigger_data *hist_data; /* * 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;}; static u64 hist_field_none(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event){ return 0;} 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_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 = operand->fn(operand, elt, buffer, rbe, event); return (u64) ilog2(roundup_pow_of_two(val));} 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 = operand1->fn(operand1, elt, buffer, rbe, event); u64 val2 = operand2->fn(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 = 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, 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)operand->fn(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 HIST_STACKTRACE_DEPTH 16#define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long))#define HIST_STACKTRACE_SKIP 5 #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, HIST_FIELD_FL_EXPR = 1 << 13, HIST_FIELD_FL_VAR_REF = 1 << 14, HIST_FIELD_FL_CPU = 1 << 15, HIST_FIELD_FL_ALIAS = 1 << 16,}; struct var_defs { unsigned int n_vars; char *name[TRACING_MAP_VARS_MAX]; char *expr[TRACING_MAP_VARS_MAX];}; struct hist_trigger_attrs { char *keys_str; char *vals_str; char *sort_key_str; char *name; char *clock; bool pause; bool cont; bool clear; bool ts_in_usecs; unsigned int map_bits; char *assignment_str[TRACING_MAP_VARS_MAX]; unsigned int n_assignments; char *action_str[HIST_ACTIONS_MAX]; unsigned int n_actions; struct var_defs var_defs;}; struct field_var { struct hist_field *var; struct hist_field *val;}; struct field_var_hist { struct hist_trigger_data *hist_data; char *cmd;}; struct hist_trigger_data { struct hist_field *fields[HIST_FIELDS_MAX]; unsigned int n_vals; unsigned int n_keys; unsigned int n_fields; unsigned int n_vars; unsigned int n_var_str; unsigned int key_size; struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; unsigned int n_sort_keys; struct trace_event_file *event_file; struct hist_trigger_attrs *attrs; struct tracing_map *map; bool enable_timestamps; bool remove; struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; unsigned int n_var_refs; struct action_data *actions[HIST_ACTIONS_MAX]; unsigned int n_actions; struct field_var *field_vars[SYNTH_FIELDS_MAX]; unsigned int n_field_vars; unsigned int n_field_var_str; struct field_var_hist *field_var_hists[SYNTH_FIELDS_MAX]; unsigned int n_field_var_hists; struct field_var *save_vars[SYNTH_FIELDS_MAX]; unsigned int n_save_vars; unsigned int n_save_var_str;}; struct action_data; typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, struct tracing_map_elt *elt, struct trace_buffer *buffer, void *rec, struct ring_buffer_event *rbe, void *key, struct action_data *data, u64 *var_ref_vals); typedef bool (*check_track_val_fn_t) (u64 track_val, u64 var_val); enum handler_id { HANDLER_ONMATCH = 1, HANDLER_ONMAX, HANDLER_ONCHANGE,}; enum action_id { ACTION_SAVE = 1, ACTION_TRACE, ACTION_SNAPSHOT,}; struct action_data { enum handler_id handler; enum action_id action; char *action_name; action_fn_t fn; unsigned int n_params; char *params[SYNTH_FIELDS_MAX]; /* * When a histogram trigger is hit, the values of any * references to variables, including variables being passed * as parameters to synthetic events, are collected into a * var_ref_vals array. This var_ref_idx array is an array of * indices into the var_ref_vals array, one for each synthetic * event param, and is passed to the synthetic event * invocation. */ unsigned int var_ref_idx[TRACING_MAP_VARS_MAX]; struct synth_event *synth_event; bool use_trace_keyword; char *synth_event_name; union { struct { char *event; char *event_system; } match_data; struct { /* * var_str contains the $-unstripped variable * name referenced by var_ref, and used when * printing the action. Because var_ref * creation is deferred to create_actions(), * we need a per-action way to save it until * then, thus var_str. */ char *var_str; /* * var_ref refers to the variable being * tracked e.g onmax($var). */ struct hist_field *var_ref; /* * track_var contains the 'invisible' tracking * variable created to keep the current * e.g. max value. */ struct hist_field *track_var; check_track_val_fn_t check_val; action_fn_t save_data; } track_data; };}; struct track_data { u64 track_val; bool updated; unsigned int key_len; void *key; struct tracing_map_elt elt; struct action_data *action_data; struct hist_trigger_data *hist_data;}; struct hist_elt_data { char *comm; u64 *var_ref_vals; char *field_var_str[SYNTH_FIELDS_MAX];}; struct snapshot_context { struct tracing_map_elt *elt; void *key;}; static void track_data_free(struct track_data *track_data){ struct hist_elt_data *elt_data; if (!track_data) return;Lines 1–500 of 5976. 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"), #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 enum field_op_id { FIELD_OP_NONE, FIELD_OP_PLUS, FIELD_OP_MINUS, FIELD_OP_UNARY_MINUS, }; /* * 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; hist_field_fn_t fn; unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; const char *type; struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; struct hist_trigger_data *hist_data; /* * 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; }; static u64 hist_field_none(struct hist_field *field, struct tracing_map_elt *elt, struct trace_buffer *buffer, struct ring_buffer_event *rbe, void *event) { return 0; } 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_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 = operand->fn(operand, elt, buffer, rbe, event); return (u64) ilog2(roundup_pow_of_two(val)); } 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 = operand1->fn(operand1, elt, buffer, rbe, event); u64 val2 = operand2->fn(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 = 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, 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)operand->fn(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 HIST_STACKTRACE_DEPTH 16 #define HIST_STACKTRACE_SIZE (HIST_STACKTRACE_DEPTH * sizeof(unsigned long)) #define HIST_STACKTRACE_SKIP 5 #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, HIST_FIELD_FL_EXPR = 1 << 13, HIST_FIELD_FL_VAR_REF = 1 << 14, HIST_FIELD_FL_CPU = 1 << 15, HIST_FIELD_FL_ALIAS = 1 << 16, }; struct var_defs { unsigned int n_vars; char *name[TRACING_MAP_VARS_MAX]; char *expr[TRACING_MAP_VARS_MAX]; }; struct hist_trigger_attrs { char *keys_str; char *vals_str; char *sort_key_str; char *name; char *clock; bool pause; bool cont; bool clear; bool ts_in_usecs; unsigned int map_bits; char *assignment_str[TRACING_MAP_VARS_MAX]; unsigned int n_assignments; char *action_str[HIST_ACTIONS_MAX]; unsigned int n_actions; struct var_defs var_defs; }; struct field_var { struct hist_field *var; struct hist_field *val; }; struct field_var_hist { struct hist_trigger_data *hist_data; char *cmd; }; struct hist_trigger_data { struct hist_field *fields[HIST_FIELDS_MAX]; unsigned int n_vals; unsigned int n_keys; unsigned int n_fields; unsigned int n_vars; unsigned int n_var_str; unsigned int key_size; struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX]; unsigned int n_sort_keys; struct trace_event_file *event_file; struct hist_trigger_attrs *attrs; struct tracing_map *map; bool enable_timestamps; bool remove; struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; unsigned int n_var_refs; struct action_data *actions[HIST_ACTIONS_MAX]; unsigned int n_actions; struct field_var *field_vars[SYNTH_FIELDS_MAX]; unsigned int n_field_vars; unsigned int n_field_var_str; struct field_var_hist *field_var_hists[SYNTH_FIELDS_MAX]; unsigned int n_field_var_hists; struct field_var *save_vars[SYNTH_FIELDS_MAX]; unsigned int n_save_vars; unsigned int n_save_var_str; }; struct action_data; typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, struct tracing_map_elt *elt, struct trace_buffer *buffer, void *rec, struct ring_buffer_event *rbe, void *key, struct action_data *data, u64 *var_ref_vals); typedef bool (*check_track_val_fn_t) (u64 track_val, u64 var_val); enum handler_id { HANDLER_ONMATCH = 1, HANDLER_ONMAX, HANDLER_ONCHANGE, }; enum action_id { ACTION_SAVE = 1, ACTION_TRACE, ACTION_SNAPSHOT, }; struct action_data { enum handler_id handler; enum action_id action; char *action_name; action_fn_t fn; unsigned int n_params; char *params[SYNTH_FIELDS_MAX]; /* * When a histogram trigger is hit, the values of any * references to variables, including variables being passed * as parameters to synthetic events, are collected into a * var_ref_vals array. This var_ref_idx array is an array of * indices into the var_ref_vals array, one for each synthetic * event param, and is passed to the synthetic event * invocation. */ unsigned int var_ref_idx[TRACING_MAP_VARS_MAX]; struct synth_event *synth_event; bool use_trace_keyword; char *synth_event_name; union { struct { char *event; char *event_system; } match_data; struct { /* * var_str contains the $-unstripped variable * name referenced by var_ref, and used when * printing the action. Because var_ref * creation is deferred to create_actions(), * we need a per-action way to save it until * then, thus var_str. */ char *var_str; /* * var_ref refers to the variable being * tracked e.g onmax($var). */ struct hist_field *var_ref; /* * track_var contains the 'invisible' tracking * variable created to keep the current * e.g. max value. */ struct hist_field *track_var; check_track_val_fn_t check_val; action_fn_t save_data; } track_data; }; }; struct track_data { u64 track_val; bool updated; unsigned int key_len; void *key; struct tracing_map_elt elt; struct action_data *action_data; struct hist_trigger_data *hist_data; }; struct hist_elt_data { char *comm; u64 *var_ref_vals; char *field_var_str[SYNTH_FIELDS_MAX]; }; struct snapshot_context { struct tracing_map_elt *elt; void *key; }; static void track_data_free(struct track_data *track_data) { struct hist_elt_data *elt_data; if (!track_data) return;