if (event->dynamic_fields[i]->is_stack) { /* reserve one extra element for size */
len = *((unsignedlong *)str_val) + 1;
len *= sizeof(unsignedlong);
} else {
len = fetch_store_strlen((unsignedlong)str_val);
}
fields_size += len;
}
/* * Avoid ring buffer recursion detection, as this event * is being performed within another event.
*/
buffer = trace_file->tr->array_buffer.buffer;
guard(ring_buffer_nest)(buffer);
entry = trace_event_buffer_reserve(&fbuffer, trace_file, sizeof(*entry) + fields_size); if (!entry) return;
for (i = 0, n_u64 = 0; i < event->n_fields; i++) {
val_idx = var_ref_idx[i]; if (event->fields[i]->is_string) { char *str_val = (char *)(long)var_ref_vals[val_idx];
len = trace_string(entry, event, str_val,
event->fields[i]->is_dynamic,
data_size, &n_u64);
data_size += len; /* only dynamic string increments */
} elseif (event->fields[i]->is_stack) { long *stack = (long *)(long)var_ref_vals[val_idx];
staticint check_field_version(constchar *prefix, constchar *field_type, constchar *field_name)
{ /* * For backward compatibility, the old synthetic event command * format did not require semicolons, and in order to not * break user space, that old format must still work. If a new * feature is added, then the format that uses the new feature * will be required to have semicolons, as nothing that uses * the old format would be using the new, yet to be created, * feature. When a new feature is added, this will detect it, * and return a number greater than 1, and require the format * to use semicolons.
*/ return 1;
}
staticstruct synth_field *parse_synth_field(int argc, char **argv, int *consumed, int *field_version)
{ constchar *prefix = NULL, *field_type = argv[0], *field_name, *array; struct synth_field *field; int len, ret = -ENOMEM; struct seq_buf s;
ssize_t size;
ret = register_trace_event(&call->event); if (!ret) {
ret = -ENODEV; goto out;
}
call->flags = TRACE_EVENT_FL_TRACEPOINT;
call->class->reg = synth_event_reg;
call->class->probe = trace_event_raw_event_synth;
call->data = event;
call->tp = event->tp;
ret = trace_add_event_call(call); if (ret) {
pr_warn("Failed to register synthetic event: %s\n",
trace_event_name(call)); goto err;
}
ret = set_synth_event_print_fmt(call); /* unregister_trace_event() will be called inside */ if (ret < 0)
trace_remove_event_call(call);
out: return ret;
err:
unregister_trace_event(&call->event); goto out;
}
size = synth_field_size((char *)arg_pair->lhs); if (size == 0) { if (strstr((char *)arg_pair->lhs, "[")) return 0;
}
return size ? 0 : -EINVAL;
}
/** * synth_event_add_field - Add a new field to a synthetic event cmd * @cmd: A pointer to the dynevent_cmd struct representing the new event * @type: The type of the new field to add * @name: The name of the new field to add * * Add a new field to a synthetic event cmd object. Field ordering is in * the same order the fields are added. * * See synth_field_size() for available types. If field_name contains * [n] the field is considered to be an array. * * Return: 0 if successful, error otherwise.
*/ int synth_event_add_field(struct dynevent_cmd *cmd, constchar *type, constchar *name)
{ struct dynevent_arg_pair arg_pair; int ret;
if (cmd->type != DYNEVENT_TYPE_SYNTH) return -EINVAL;
if (!type || !name) return -EINVAL;
dynevent_arg_pair_init(&arg_pair, 0, ';');
arg_pair.lhs = type;
arg_pair.rhs = name;
ret = dynevent_arg_pair_add(cmd, &arg_pair, synth_event_check_arg_fn); if (ret) return ret;
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
ret = -EINVAL;
/** * synth_event_add_field_str - Add a new field to a synthetic event cmd * @cmd: A pointer to the dynevent_cmd struct representing the new event * @type_name: The type and name of the new field to add, as a single string * * Add a new field to a synthetic event cmd object, as a single * string. The @type_name string is expected to be of the form 'type * name', which will be appended by ';'. No sanity checking is done - * what's passed in is assumed to already be well-formed. Field * ordering is in the same order the fields are added. * * See synth_field_size() for available types. If field_name contains * [n] the field is considered to be an array. * * Return: 0 if successful, error otherwise.
*/ int synth_event_add_field_str(struct dynevent_cmd *cmd, constchar *type_name)
{ struct dynevent_arg arg; int ret;
if (cmd->type != DYNEVENT_TYPE_SYNTH) return -EINVAL;
if (!type_name) return -EINVAL;
dynevent_arg_init(&arg, ';');
arg.str = type_name;
ret = dynevent_arg_add(cmd, &arg, NULL); if (ret) return ret;
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
ret = -EINVAL;
/** * synth_event_add_fields - Add multiple fields to a synthetic event cmd * @cmd: A pointer to the dynevent_cmd struct representing the new event * @fields: An array of type/name field descriptions * @n_fields: The number of field descriptions contained in the fields array * * Add a new set of fields to a synthetic event cmd object. The event * fields that will be defined for the event should be passed in as an * array of struct synth_field_desc, and the number of elements in the * array passed in as n_fields. Field ordering will retain the * ordering given in the fields array. * * See synth_field_size() for available types. If field_name contains * [n] the field is considered to be an array. * * Return: 0 if successful, error otherwise.
*/ int synth_event_add_fields(struct dynevent_cmd *cmd, struct synth_field_desc *fields, unsignedint n_fields)
{ unsignedint i; int ret = 0;
for (i = 0; i < n_fields; i++) { if (fields[i].type == NULL || fields[i].name == NULL) {
ret = -EINVAL; break;
}
ret = synth_event_add_field(cmd, fields[i].type, fields[i].name); if (ret) break;
}
/** * __synth_event_gen_cmd_start - Start a synthetic event command from arg list * @cmd: A pointer to the dynevent_cmd struct representing the new event * @name: The name of the synthetic event * @mod: The module creating the event, NULL if not created from a module * @...: Variable number of arg (pairs), one pair for each field * * NOTE: Users normally won't want to call this function directly, but * rather use the synth_event_gen_cmd_start() wrapper, which * automatically adds a NULL to the end of the arg list. If this * function is used directly, make sure the last arg in the variable * arg list is NULL. * * Generate a synthetic event command to be executed by * synth_event_gen_cmd_end(). This function can be used to generate * the complete command or only the first part of it; in the latter * case, synth_event_add_field(), synth_event_add_field_str(), or * synth_event_add_fields() can be used to add more fields following * this. * * There should be an even number variable args, each pair consisting * of a type followed by a field name. * * See synth_field_size() for available types. If field_name contains * [n] the field is considered to be an array. * * Return: 0 if successful, error otherwise.
*/ int __synth_event_gen_cmd_start(struct dynevent_cmd *cmd, constchar *name, struct module *mod, ...)
{ struct dynevent_arg arg;
va_list args; int ret;
cmd->event_name = name;
cmd->private_data = mod;
if (cmd->type != DYNEVENT_TYPE_SYNTH) return -EINVAL;
dynevent_arg_init(&arg, 0);
arg.str = name;
ret = dynevent_arg_add(cmd, &arg, NULL); if (ret) return ret;
va_start(args, mod); for (;;) { constchar *type, *name;
type = va_arg(args, constchar *); if (!type) break;
name = va_arg(args, constchar *); if (!name) break;
if (++cmd->n_fields > SYNTH_FIELDS_MAX) {
ret = -EINVAL; break;
}
ret = synth_event_add_field(cmd, type, name); if (ret) break;
}
va_end(args);
/** * synth_event_gen_cmd_array_start - Start synthetic event command from an array * @cmd: A pointer to the dynevent_cmd struct representing the new event * @name: The name of the synthetic event * @mod: The module creating the event, NULL if not created from a module * @fields: An array of type/name field descriptions * @n_fields: The number of field descriptions contained in the fields array * * Generate a synthetic event command to be executed by * synth_event_gen_cmd_end(). This function can be used to generate * the complete command or only the first part of it; in the latter * case, synth_event_add_field(), synth_event_add_field_str(), or * synth_event_add_fields() can be used to add more fields following * this. * * The event fields that will be defined for the event should be * passed in as an array of struct synth_field_desc, and the number of * elements in the array passed in as n_fields. Field ordering will * retain the ordering given in the fields array. * * See synth_field_size() for available types. If field_name contains * [n] the field is considered to be an array. * * Return: 0 if successful, error otherwise.
*/ int synth_event_gen_cmd_array_start(struct dynevent_cmd *cmd, constchar *name, struct module *mod, struct synth_field_desc *fields, unsignedint n_fields)
{ struct dynevent_arg arg; unsignedint i; int ret = 0;
cmd->event_name = name;
cmd->private_data = mod;
if (cmd->type != DYNEVENT_TYPE_SYNTH) return -EINVAL;
if (n_fields > SYNTH_FIELDS_MAX) return -EINVAL;
dynevent_arg_init(&arg, 0);
arg.str = name;
ret = dynevent_arg_add(cmd, &arg, NULL); if (ret) return ret;
for (i = 0; i < n_fields; i++) { if (fields[i].type == NULL || fields[i].name == NULL) return -EINVAL;
ret = synth_event_add_field(cmd, fields[i].type, fields[i].name); if (ret) break;
}
if (name[0] == '\0') {
synth_err(SYNTH_ERR_INVALID_CMD, 0); return -EINVAL;
}
if (!is_good_name(name)) {
synth_err(SYNTH_ERR_BAD_NAME, errpos(name)); return -EINVAL;
}
mutex_lock(&event_mutex);
event = find_synth_event(name); if (event) {
synth_err(SYNTH_ERR_EVENT_EXISTS, errpos(name));
ret = -EEXIST; goto err;
}
tmp_fields = saved_fields = kstrdup(raw_fields, GFP_KERNEL); if (!tmp_fields) {
ret = -ENOMEM; goto err;
}
while ((field_str = strsep(&tmp_fields, ";")) != NULL) {
argv = argv_split(GFP_KERNEL, field_str, &argc); if (!argv) {
ret = -ENOMEM; goto err;
}
if (!argc) {
argv_free(argv); continue;
}
n_fields_this_loop = 0;
consumed = 0; while (argc > consumed) { int field_version;
field = parse_synth_field(argc - consumed,
argv + consumed, &consumed,
&field_version); if (IS_ERR(field)) {
ret = PTR_ERR(field); goto err_free_arg;
}
/* * Track the highest version of any field we * found in the command.
*/ if (field_version > cmd_version)
cmd_version = field_version;
/* * Now sort out what is and isn't valid for * each supported version. * * If we see more than 1 field per loop, it * means we have multiple fields between * semicolons, and that's something we no * longer support in a version 2 or greater * command.
*/ if (cmd_version > 1 && n_fields_this_loop >= 1) {
synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str));
ret = -EINVAL; goto err_free_arg;
}
if (n_fields == SYNTH_FIELDS_MAX) {
synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0);
ret = -EINVAL; goto err_free_arg;
}
fields[n_fields++] = field;
n_fields_this_loop++;
}
argv_free(argv);
if (consumed < argc) {
synth_err(SYNTH_ERR_INVALID_CMD, 0);
ret = -EINVAL; goto err;
}
}
if (n_fields == 0) {
synth_err(SYNTH_ERR_INVALID_CMD, 0);
ret = -EINVAL; goto err;
}
event = alloc_synth_event(name, n_fields, fields); if (IS_ERR(event)) {
ret = PTR_ERR(event);
event = NULL; goto err;
}
ret = register_synth_event(event); if (!ret)
dyn_event_add(&event->devent, &event->call); else
free_synth_event(event);
out:
mutex_unlock(&event_mutex);
kfree(saved_fields);
return ret;
err_free_arg:
argv_free(argv);
err: for (i = 0; i < n_fields; i++)
free_synth_field(fields[i]);
goto out;
}
/** * synth_event_create - Create a new synthetic event * @name: The name of the new synthetic event * @fields: An array of type/name field descriptions * @n_fields: The number of field descriptions contained in the fields array * @mod: The module creating the event, NULL if not created from a module * * Create a new synthetic event with the given name under the * trace/events/synthetic/ directory. The event fields that will be * defined for the event should be passed in as an array of struct * synth_field_desc, and the number elements in the array passed in as * n_fields. Field ordering will retain the ordering given in the * fields array. * * If the new synthetic event is being created from a module, the mod * param must be non-NULL. This will ensure that the trace buffer * won't contain unreadable events. * * The new synth event should be deleted using synth_event_delete() * function. The new synthetic event can be generated from modules or * other kernel code using trace_synth_event() and related functions. * * Return: 0 if successful, error otherwise.
*/ int synth_event_create(constchar *name, struct synth_field_desc *fields, unsignedint n_fields, struct module *mod)
{ struct dynevent_cmd cmd; char *buf; int ret;
buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); if (!buf) return -ENOMEM;
staticint destroy_synth_event(struct synth_event *se)
{ int ret;
if (se->ref) return -EBUSY;
if (trace_event_dyn_busy(&se->call)) return -EBUSY;
ret = unregister_synth_event(se); if (!ret) {
dyn_event_remove(&se->devent);
free_synth_event(se);
}
return ret;
}
/** * synth_event_delete - Delete a synthetic event * @event_name: The name of the new synthetic event * * Delete a synthetic event that was created with synth_event_create(). * * Return: 0 if successful, error otherwise.
*/ int synth_event_delete(constchar *event_name)
{ struct synth_event *se = NULL; struct module *mod = NULL; int ret = -ENOENT;
mutex_lock(&event_mutex);
se = find_synth_event(event_name); if (se) {
mod = se->mod;
ret = destroy_synth_event(se);
}
mutex_unlock(&event_mutex);
if (mod) { /* * It is safest to reset the ring buffer if the module * being unloaded registered any events that were * used. The only worry is if a new module gets * loaded, and takes on the same id as the events of * this module. When printing out the buffer, traced * events left over from this module may be passed to * the new module events and unexpected results may * occur.
*/
tracing_reset_all_online_cpus();
}
staticint check_command(constchar *raw_command)
{ char **argv = NULL, *cmd, *saved_cmd, *name_and_field; int argc, ret = 0;
cmd = saved_cmd = kstrdup(raw_command, GFP_KERNEL); if (!cmd) return -ENOMEM;
name_and_field = strsep(&cmd, ";"); if (!name_and_field) {
ret = -EINVAL; goto free;
}
if (name_and_field[0] == '!') goto free;
argv = argv_split(GFP_KERNEL, name_and_field, &argc); if (!argv) {
ret = -ENOMEM; goto free;
}
argv_free(argv);
if (argc < 3)
ret = -EINVAL;
free:
kfree(saved_cmd);
return ret;
}
staticint create_or_delete_synth_event(constchar *raw_command)
{ char *name = NULL, *fields, *p; int ret = 0;
raw_command = skip_spaces(raw_command); if (raw_command[0] == '\0') return ret;
last_cmd_set(raw_command);
ret = check_command(raw_command); if (ret) {
synth_err(SYNTH_ERR_INVALID_CMD, 0); return ret;
}
p = strpbrk(raw_command, " \t"); if (!p && raw_command[0] != '!') {
synth_err(SYNTH_ERR_INVALID_CMD, 0);
ret = -EINVAL; goto free;
}
name = kmemdup_nul(raw_command, p ? p - raw_command : strlen(raw_command), GFP_KERNEL); if (!name) return -ENOMEM;
if (name[0] == '!') {
ret = synth_event_delete(name + 1); goto free;
}
fields = skip_spaces(p);
ret = __create_synth_event(name, fields);
free:
kfree(name);
return ret;
}
staticint synth_event_run_command(struct dynevent_cmd *cmd)
{ struct synth_event *se; int ret;
ret = create_or_delete_synth_event(cmd->seq.buffer); if (ret) return ret;
se = find_synth_event(cmd->event_name); if (WARN_ON(!se)) return -ENOENT;
se->mod = cmd->private_data;
return ret;
}
/** * synth_event_cmd_init - Initialize a synthetic event command object * @cmd: A pointer to the dynevent_cmd struct representing the new event * @buf: A pointer to the buffer used to build the command * @maxlen: The length of the buffer passed in @buf * * Initialize a synthetic event command object. Use this before * calling any of the other dyenvent_cmd functions.
*/ void synth_event_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen)
{
dynevent_cmd_init(cmd, buf, maxlen, DYNEVENT_TYPE_SYNTH,
synth_event_run_command);
}
EXPORT_SYMBOL_GPL(synth_event_cmd_init);
staticinlineint
__synth_event_trace_init(struct trace_event_file *file, struct synth_event_trace_state *trace_state)
{ int ret = 0;
memset(trace_state, '\0', sizeof(*trace_state));
/* * Normal event tracing doesn't get called at all unless the * ENABLED bit is set (which attaches the probe thus allowing * this code to be called, etc). Because this is called * directly by the user, we don't have that but we still need * to honor not logging when disabled. For the iterated * trace case, we save the enabled state upon start and just * ignore the following data calls.
*/ if (!(file->flags & EVENT_FILE_FL_ENABLED) ||
trace_trigger_soft_disabled(file)) {
trace_state->disabled = true;
ret = -ENOENT; goto out;
}
staticinlineint
__synth_event_trace_start(struct trace_event_file *file, struct synth_event_trace_state *trace_state, int dynamic_fields_size)
{ int entry_size, fields_size = 0; int ret = 0;
/* * Avoid ring buffer recursion detection, as this event * is being performed within another event.
*/
trace_state->buffer = file->tr->array_buffer.buffer;
ring_buffer_nest_start(trace_state->buffer);
entry_size = sizeof(*trace_state->entry) + fields_size;
trace_state->entry = trace_event_buffer_reserve(&trace_state->fbuffer,
file,
entry_size); if (!trace_state->entry) {
ring_buffer_nest_end(trace_state->buffer);
ret = -EINVAL;
}
/** * synth_event_trace - Trace a synthetic event * @file: The trace_event_file representing the synthetic event * @n_vals: The number of values in vals * @...: Variable number of args containing the event values * * Trace a synthetic event using the values passed in the variable * argument list. * * The argument list should be a list 'n_vals' u64 values. The number * of vals must match the number of field in the synthetic event, and * must be in the same order as the synthetic event fields. * * All vals should be cast to u64, and string vals are just pointers * to strings, cast to u64. Strings will be copied into space * reserved in the event for the string, using these pointers. * * Return: 0 on success, err otherwise.
*/ int synth_event_trace(struct trace_event_file *file, unsignedint n_vals, ...)
{ unsignedint i, n_u64, len, data_size = 0; struct synth_event_trace_state state;
va_list args; int ret;
ret = __synth_event_trace_init(file, &state); if (ret) { if (ret == -ENOENT)
ret = 0; /* just disabled, not really an error */ return ret;
}
if (state.event->n_dynamic_fields) {
va_start(args, n_vals);
for (i = 0; i < state.event->n_fields; i++) {
u64 val = va_arg(args, u64);
if (state.event->fields[i]->is_string &&
state.event->fields[i]->is_dynamic) { char *str_val = (char *)(long)val;
data_size += strlen(str_val) + 1;
}
}
va_end(args);
}
ret = __synth_event_trace_start(file, &state, data_size); if (ret) return ret;
if (n_vals != state.event->n_fields) {
ret = -EINVAL; goto out;
}
data_size = 0;
va_start(args, n_vals); for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) {
u64 val;
val = va_arg(args, u64);
if (state.event->fields[i]->is_string) { char *str_val = (char *)(long)val;
/** * synth_event_trace_array - Trace a synthetic event from an array * @file: The trace_event_file representing the synthetic event * @vals: Array of values * @n_vals: The number of values in vals * * Trace a synthetic event using the values passed in as 'vals'. * * The 'vals' array is just an array of 'n_vals' u64. The number of * vals must match the number of field in the synthetic event, and * must be in the same order as the synthetic event fields. * * All vals should be cast to u64, and string vals are just pointers * to strings, cast to u64. Strings will be copied into space * reserved in the event for the string, using these pointers. * * Return: 0 on success, err otherwise.
*/ int synth_event_trace_array(struct trace_event_file *file, u64 *vals, unsignedint n_vals)
{ unsignedint i, n_u64, field_pos, len, data_size = 0; struct synth_event_trace_state state; char *str_val; int ret;
ret = __synth_event_trace_init(file, &state); if (ret) { if (ret == -ENOENT)
ret = 0; /* just disabled, not really an error */ return ret;
}
if (state.event->n_dynamic_fields) { for (i = 0; i < state.event->n_dynamic_fields; i++) {
field_pos = state.event->dynamic_fields[i]->field_pos;
str_val = (char *)(long)vals[field_pos];
len = strlen(str_val) + 1;
data_size += len;
}
}
ret = __synth_event_trace_start(file, &state, data_size); if (ret) return ret;
if (n_vals != state.event->n_fields) {
ret = -EINVAL; goto out;
}
data_size = 0;
for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) { if (state.event->fields[i]->is_string) { char *str_val = (char *)(long)vals[i];
len = trace_string(state.entry, state.event, str_val,
state.event->fields[i]->is_dynamic,
data_size, &n_u64);
data_size += len; /* only dynamic string increments */
} else { struct synth_field *field = state.event->fields[i];
u64 val = vals[i];
switch (field->size) { case 1:
state.entry->fields[n_u64].as_u8 = (u8)val; break;
case 2:
state.entry->fields[n_u64].as_u16 = (u16)val; break;
case 4:
state.entry->fields[n_u64].as_u32 = (u32)val; break;
/** * synth_event_trace_start - Start piecewise synthetic event trace * @file: The trace_event_file representing the synthetic event * @trace_state: A pointer to object tracking the piecewise trace state * * Start the trace of a synthetic event field-by-field rather than all * at once. * * This function 'opens' an event trace, which means space is reserved * for the event in the trace buffer, after which the event's * individual field values can be set through either * synth_event_add_next_val() or synth_event_add_val(). * * A pointer to a trace_state object is passed in, which will keep * track of the current event trace state until the event trace is * closed (and the event finally traced) using * synth_event_trace_end(). * * Note that synth_event_trace_end() must be called after all values * have been added for each event trace, regardless of whether adding * all field values succeeded or not. * * Note also that for a given event trace, all fields must be added * using either synth_event_add_next_val() or synth_event_add_val() * but not both together or interleaved. * * Return: 0 on success, err otherwise.
*/ int synth_event_trace_start(struct trace_event_file *file, struct synth_event_trace_state *trace_state)
{ int ret;
if (!trace_state) return -EINVAL;
ret = __synth_event_trace_init(file, trace_state); if (ret) { if (ret == -ENOENT)
ret = 0; /* just disabled, not really an error */ return ret;
}
if (trace_state->event->n_dynamic_fields) return -ENOTSUPP;
ret = __synth_event_trace_start(file, trace_state, 0);
staticint __synth_event_add_val(constchar *field_name, u64 val, struct synth_event_trace_state *trace_state)
{ struct synth_field *field = NULL; struct synth_trace_event *entry; struct synth_event *event; int i, ret = 0;
if (!trace_state) {
ret = -EINVAL; goto out;
}
/* can't mix add_next_synth_val() with add_synth_val() */ if (field_name) { if (trace_state->add_next) {
ret = -EINVAL; goto out;
}
trace_state->add_name = true;
} else { if (trace_state->add_name) {
ret = -EINVAL; goto out;
}
trace_state->add_next = true;
}
if (trace_state->disabled) goto out;
event = trace_state->event; if (trace_state->add_name) { for (i = 0; i < event->n_fields; i++) {
field = event->fields[i]; if (strcmp(field->name, field_name) == 0) break;
} if (!field) {
ret = -EINVAL; goto out;
}
} else { if (trace_state->cur_field >= event->n_fields) {
ret = -EINVAL; goto out;
}
field = event->fields[trace_state->cur_field++];
}
/** * synth_event_add_next_val - Add the next field's value to an open synth trace * @val: The value to set the next field to * @trace_state: A pointer to object tracking the piecewise trace state * * Set the value of the next field in an event that's been opened by * synth_event_trace_start(). * * The val param should be the value cast to u64. If the value points * to a string, the val param should be a char * cast to u64. * * This function assumes all the fields in an event are to be set one * after another - successive calls to this function are made, one for * each field, in the order of the fields in the event, until all * fields have been set. If you'd rather set each field individually * without regard to ordering, synth_event_add_val() can be used * instead. * * Note however that synth_event_add_next_val() and * synth_event_add_val() can't be intermixed for a given event trace - * one or the other but not both can be used at the same time. * * Note also that synth_event_trace_end() must be called after all * values have been added for each event trace, regardless of whether * adding all field values succeeded or not. * * Return: 0 on success, err otherwise.
*/ int synth_event_add_next_val(u64 val, struct synth_event_trace_state *trace_state)
{ return __synth_event_add_val(NULL, val, trace_state);
}
EXPORT_SYMBOL_GPL(synth_event_add_next_val);
/** * synth_event_add_val - Add a named field's value to an open synth trace * @field_name: The name of the synthetic event field value to set * @val: The value to set the named field to * @trace_state: A pointer to object tracking the piecewise trace state * * Set the value of the named field in an event that's been opened by * synth_event_trace_start(). * * The val param should be the value cast to u64. If the value points * to a string, the val param should be a char * cast to u64. * * This function looks up the field name, and if found, sets the field * to the specified value. This lookup makes this function more * expensive than synth_event_add_next_val(), so use that or the * none-piecewise synth_event_trace() instead if efficiency is more * important. * * Note however that synth_event_add_next_val() and * synth_event_add_val() can't be intermixed for a given event trace - * one or the other but not both can be used at the same time. * * Note also that synth_event_trace_end() must be called after all * values have been added for each event trace, regardless of whether * adding all field values succeeded or not. * * Return: 0 on success, err otherwise.
*/ int synth_event_add_val(constchar *field_name, u64 val, struct synth_event_trace_state *trace_state)
{ return __synth_event_add_val(field_name, val, trace_state);
}
EXPORT_SYMBOL_GPL(synth_event_add_val);
/** * synth_event_trace_end - End piecewise synthetic event trace * @trace_state: A pointer to object tracking the piecewise trace state * * End the trace of a synthetic event opened by * synth_event_trace__start(). * * This function 'closes' an event trace, which basically means that * it commits the reserved event and cleans up other loose ends. * * A pointer to a trace_state object is passed in, which will keep * track of the current event trace state opened with * synth_event_trace_start(). * * Note that this function must be called after all values have been * added for each event trace, regardless of whether adding all field * values succeeded or not. * * Return: 0 on success, err otherwise.
*/ int synth_event_trace_end(struct synth_event_trace_state *trace_state)
{ if (!trace_state) return -EINVAL;
staticint create_synth_event(constchar *raw_command)
{ char *fields, *p; constchar *name; int len, ret = 0;
raw_command = skip_spaces(raw_command); if (raw_command[0] == '\0') return ret;
last_cmd_set(raw_command);
name = raw_command;
/* Don't try to process if not our system */ if (name[0] != 's' || name[1] != ':') return -ECANCELED;
name += 2;
p = strpbrk(raw_command, " \t"); if (!p) {
synth_err(SYNTH_ERR_INVALID_CMD, 0); return -EINVAL;
}
fields = skip_spaces(p);
/* This interface accepts group name prefix */ if (strchr(name, '/')) {
len = str_has_prefix(name, SYNTH_SYSTEM "/"); if (len == 0) {
synth_err(SYNTH_ERR_INVALID_DYN_CMD, 0); return -EINVAL;
}
name += len;
}
len = name - raw_command;
ret = check_command(raw_command + len); if (ret) {
synth_err(SYNTH_ERR_INVALID_CMD, 0); return ret;
}
name = kmemdup_nul(raw_command + len, p - raw_command - len, GFP_KERNEL); if (!name) return -ENOMEM;
for (i = 0; i < event->n_fields; i++) {
field = event->fields[i];
type = field->type;
t = strstr(type, "__data_loc"); if (t) { /* __data_loc belongs in format but not event desc */
t += sizeof("__data_loc");
type = t;
}
/* * Register dynevent at core_initcall. This allows kernel to setup kprobe * events in postcore_initcall without tracefs.
*/ static __init int trace_events_synth_init_early(void)
{ int err = 0;
err = dyn_event_register(&synth_event_ops); if (err)
pr_warn("Could not register synth_event_ops\n");
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.