/* 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'"),
/* * 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; unsignedint idx;
};
/* * 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].
*/ unsignedint var_ref_idx; bool read_once;
unsignedint var_str_idx;
/* Numeric literals are represented as u64 */
u64 constant; /* Used to optimize division by constants */
u64 div_multiplier;
};
/* * 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;
/* * 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.
*/ unsignedint var_ref_idx[SYNTH_FIELDS_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;
/* * Returns the specific division function to use if the divisor * is constant. This avoids extra branches when the trigger is hit.
*/ staticenum hist_field_fn hist_field_get_div_fn(struct hist_field *divisor)
{
u64 div = divisor->constant;
if (!(div & (div - 1))) return HIST_FIELD_FN_DIV_POWER2;
/* If the divisor is too large, do a regular division */ if (div > (1 << HIST_DIV_SHIFT)) return HIST_FIELD_FN_DIV_NOT_POWER2;
/** * check_field_for_var_ref - Check if a VAR_REF field references a variable * @hist_field: The VAR_REF field to check * @var_data: The hist trigger that owns the variable * @var_idx: The trigger variable identifier * * Check the given VAR_REF field to see whether or not it references * the given variable associated with the given trigger. * * Return: The VAR_REF field if it does reference the variable, NULL if not
*/ staticstruct hist_field *
check_field_for_var_ref(struct hist_field *hist_field, struct hist_trigger_data *var_data, unsignedint var_idx)
{
WARN_ON(!(hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF));
/** * find_var_ref - Check if a trigger has a reference to a trigger variable * @hist_data: The hist trigger that might have a reference to the variable * @var_data: The hist trigger that owns the variable * @var_idx: The trigger variable identifier * * Check the list of var_refs[] on the first hist trigger to see * whether any of them are references to the variable on the second * trigger. * * Return: The VAR_REF field referencing the variable if so, NULL if not
*/ staticstruct hist_field *find_var_ref(struct hist_trigger_data *hist_data, struct hist_trigger_data *var_data, unsignedint var_idx)
{ struct hist_field *hist_field; unsignedint i;
for (i = 0; i < hist_data->n_var_refs; i++) {
hist_field = hist_data->var_refs[i]; if (check_field_for_var_ref(hist_field, var_data, var_idx)) return hist_field;
}
return NULL;
}
/** * find_any_var_ref - Check if there is a reference to a given trigger variable * @hist_data: The hist trigger * @var_idx: The trigger variable identifier * * Check to see whether the given variable is currently referenced by * any other trigger. * * The trigger the variable is defined on is explicitly excluded - the * assumption being that a self-reference doesn't prevent a trigger * from being removed. * * Return: The VAR_REF field referencing the variable if so, NULL if not
*/ staticstruct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, unsignedint var_idx)
{ struct trace_array *tr = hist_data->event_file->tr; struct hist_field *found = NULL; struct hist_var_data *var_data;
list_for_each_entry(var_data, &tr->hist_vars, list) { if (var_data->hist_data == hist_data) continue;
found = find_var_ref(var_data->hist_data, hist_data, var_idx); if (found) break;
}
return found;
}
/** * check_var_refs - Check if there is a reference to any of trigger's variables * @hist_data: The hist trigger * * A trigger can define one or more variables. If any one of them is * currently referenced by any other trigger, this function will * determine that. * * Typically used to determine whether or not a trigger can be removed * - if there are any references to a trigger's variables, it cannot. * * Return: True if there is a reference to any of trigger's variables
*/ staticbool check_var_refs(struct hist_trigger_data *hist_data)
{ struct hist_field *field; bool found = false; int i;
for_each_hist_field(i, hist_data) {
field = hist_data->fields[i]; if (field && field->flags & HIST_FIELD_FL_VAR) { if (find_any_var_ref(hist_data, field->var.idx)) {
found = true; break;
}
}
}
switch (field->operator) { case FIELD_OP_MINUS:
strcat(expr, "-"); break; case FIELD_OP_PLUS:
strcat(expr, "+"); break; case FIELD_OP_DIV:
strcat(expr, "/"); break; case FIELD_OP_MULT:
strcat(expr, "*"); break; default:
kfree(expr); return NULL;
}
expr_field_str(field->operands[1], expr);
return expr;
}
/* * If field_op != FIELD_OP_NONE, *sep points to the root operator * of the expression tree to be evaluated.
*/ staticint contains_operator(char *str, char **sep)
{ enum field_op_id field_op = FIELD_OP_NONE; char *minus_op, *plus_op, *div_op, *mult_op;
/* * Report the last occurrence of the operators first, so that the * expression is evaluated left to right. This is important since * subtraction and division are not associative. * * e.g * 64/8/4/2 is 1, i.e 64/8/4/2 = ((64/8)/4)/2 * 14-7-5-2 is 0, i.e 14-7-5-2 = ((14-7)-5)-2
*/
/* * First, find lower precedence addition and subtraction * since the expression will be evaluated recursively.
*/
minus_op = strrchr(str, '-'); if (minus_op) { /* * Unary minus is not supported in sub-expressions. If * present, it is always the next root operator.
*/ if (minus_op == str) {
field_op = FIELD_OP_UNARY_MINUS; goto out;
}
field_op = FIELD_OP_MINUS;
}
plus_op = strrchr(str, '+'); if (plus_op || minus_op) { /* * For operators of the same precedence use to rightmost as the * root, so that the expression is evaluated left to right.
*/ if (plus_op > minus_op)
field_op = FIELD_OP_PLUS; goto out;
}
/* * Multiplication and division have higher precedence than addition and * subtraction.
*/
div_op = strrchr(str, '/'); if (div_op)
field_op = FIELD_OP_DIV;
mult_op = strrchr(str, '*'); /* * For operators of the same precedence use to rightmost as the * root, so that the expression is evaluated left to right.
*/ if (mult_op > div_op)
field_op = FIELD_OP_MULT;
out: if (sep) { switch (field_op) { case FIELD_OP_UNARY_MINUS: case FIELD_OP_MINUS:
*sep = minus_op; break; case FIELD_OP_PLUS:
*sep = plus_op; break; case FIELD_OP_DIV:
*sep = div_op; break; case FIELD_OP_MULT:
*sep = mult_op; break; case FIELD_OP_NONE: default:
*sep = NULL; break;
}
}
/* Pointers to strings are just pointers and dangerous to dereference */ if (is_string_field(field) &&
(field->filter_type != FILTER_PTR_STRING)) {
flags |= HIST_FIELD_FL_STRING;
for (i = 0; i < hist_data->n_var_refs; i++) {
ref_field = hist_data->var_refs[i]; if (ref_field->var.idx == var_field->var.idx &&
ref_field->var.hist_data == var_field->hist_data) return i;
}
return -ENOENT;
}
/** * create_var_ref - Create a variable reference and attach it to trigger * @hist_data: The trigger that will be referencing the variable * @var_field: The VAR field to create a reference to * @system: The optional system string * @event_name: The optional event_name string * * Given a variable hist_field, create a VAR_REF hist_field that * represents a reference to it. * * This function also adds the reference to the trigger that * now references the variable. * * Return: The VAR_REF field if successful, NULL if not
*/ staticstruct hist_field *create_var_ref(struct hist_trigger_data *hist_data, struct hist_field *var_field, char *system, char *event_name)
{ unsignedlong flags = HIST_FIELD_FL_VAR_REF; struct hist_field *ref_field; int i;
/* Check if the variable already exists */ for (i = 0; i < hist_data->n_var_refs; i++) {
ref_field = hist_data->var_refs[i]; if (ref_field->var.idx == var_field->var.idx &&
ref_field->var.hist_data == var_field->hist_data) {
get_hist_field(ref_field); return ref_field;
}
} /* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */ if (hist_data->n_var_refs >= TRACING_MAP_VARS_MAX) return NULL;
ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); if (ref_field) { if (init_var_ref(ref_field, var_field, system, event_name)) {
destroy_hist_field(ref_field, 0); return NULL;
}
if (isdigit(str[0])) {
hist_field = parse_const(hist_data, str, var_name, flags); if (!hist_field) {
ret = -EINVAL; goto out;
} return hist_field;
}
s = strchr(str, '.'); if (s) {
s = strchr(++s, '.'); if (s) {
ref_system = strsep(&str, "."); if (!str) {
ret = -EINVAL; goto out;
}
ref_event = strsep(&str, "."); if (!str) {
ret = -EINVAL; goto out;
}
ref_var = str;
}
}
s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var); if (!s) {
hist_field = parse_var_ref(hist_data, ref_system,
ref_event, ref_var); if (hist_field) { if (var_name) {
hist_field = create_alias(hist_data, hist_field, var_name); if (!hist_field) {
ret = -ENOMEM; goto out;
}
} return hist_field;
}
} else
str = s;
field = parse_field(hist_data, file, str, flags, &buckets); if (IS_ERR(field)) {
ret = PTR_ERR(field); goto out;
}
hist_field = create_hist_field(hist_data, field, *flags, var_name); if (!hist_field) {
ret = -ENOMEM; goto out;
}
hist_field->buckets = buckets;
/* Unary minus operator, increment n_subexprs */
++*n_subexprs;
/* we support only -(xxx) i.e. explicit parens required */
if (*n_subexprs > 3) {
hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
ret = -EINVAL; goto free;
}
str++; /* skip leading '-' */
s = strchr(str, '('); if (s)
str++; else {
ret = -EINVAL; goto free;
}
s = strrchr(str, ')'); if (s) { /* unary minus not supported in sub-expressions */ if (*(s+1) != '\0') {
hist_err(file->tr, HIST_ERR_UNARY_MINUS_SUBEXPR,
errpos(str));
ret = -EINVAL; goto free;
}
*s = '\0';
} else {
ret = -EINVAL; /* no closing ')' */ goto free;
}
flags |= HIST_FIELD_FL_EXPR;
expr = create_hist_field(hist_data, NULL, flags, var_name); if (!expr) {
ret = -ENOMEM; goto free;
}
operand_flags = 0;
operand1 = parse_expr(hist_data, file, str, operand_flags, NULL, n_subexprs); if (IS_ERR(operand1)) {
ret = PTR_ERR(operand1); goto free;
} if (operand1->flags & HIST_FIELD_FL_STRING) { /* String type can not be the operand of unary operator. */
hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(str));
destroy_hist_field(operand1, 0);
ret = -EINVAL; goto free;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.