1

tracing: Add synthetic event command generation functions

Add functions used to generate synthetic event commands, built on top
of the dynevent_cmd interface.

synth_event_gen_cmd_start() is used to create a synthetic event
command using a variable arg list and
synth_event_gen_cmd_array_start() does the same thing but using an
array of field descriptors.  synth_event_add_field(),
synth_event_add_field_str() and synth_event_add_fields() can be used
to add single fields one by one or as a group.  Once all desired
fields are added, synth_event_gen_cmd_end() is used to actually
execute the command and create the event.

synth_event_create() does everything, including creating the event, in
a single call.

Link: http://lkml.kernel.org/r/38fef702fad5ef208009f459552f34a94befd860.1580323897.git.zanussi@kernel.org

Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Tom Zanussi <zanussi@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
Tom Zanussi 2020-01-29 12:59:25 -06:00 committed by Steven Rostedt (VMware)
parent 86c5426bad
commit 35ca5207c2
2 changed files with 412 additions and 4 deletions

View File

@ -357,6 +357,7 @@ extern void trace_put_event_file(struct trace_event_file *file);
#define MAX_DYNEVENT_CMD_LEN (2048)
enum dynevent_type {
DYNEVENT_TYPE_SYNTH = 1,
DYNEVENT_TYPE_NONE,
};
@ -379,6 +380,42 @@ extern int dynevent_create(struct dynevent_cmd *cmd);
extern int synth_event_delete(const char *name);
extern void synth_event_cmd_init(struct dynevent_cmd *cmd,
char *buf, int maxlen);
extern int __synth_event_gen_cmd_start(struct dynevent_cmd *cmd,
const char *name,
struct module *mod, ...);
#define synth_event_gen_cmd_start(cmd, name, mod, ...) \
__synth_event_gen_cmd_start(cmd, name, mod, ## __VA_ARGS__, NULL)
struct synth_field_desc {
const char *type;
const char *name;
};
extern int synth_event_gen_cmd_array_start(struct dynevent_cmd *cmd,
const char *name,
struct module *mod,
struct synth_field_desc *fields,
unsigned int n_fields);
extern int synth_event_create(const char *name,
struct synth_field_desc *fields,
unsigned int n_fields, struct module *mod);
extern int synth_event_add_field(struct dynevent_cmd *cmd,
const char *type,
const char *name);
extern int synth_event_add_field_str(struct dynevent_cmd *cmd,
const char *type_name);
extern int synth_event_add_fields(struct dynevent_cmd *cmd,
struct synth_field_desc *fields,
unsigned int n_fields);
#define synth_event_gen_cmd_end(cmd) \
dynevent_create(cmd)
/*
* Event file flags:
* ENABLED - The event is enabled

View File

@ -379,7 +379,7 @@ struct hist_trigger_data {
unsigned int n_save_var_str;
};
static int synth_event_create(int argc, const char **argv);
static int create_synth_event(int argc, const char **argv);
static int synth_event_show(struct seq_file *m, struct dyn_event *ev);
static int synth_event_release(struct dyn_event *ev);
static bool synth_event_is_busy(struct dyn_event *ev);
@ -387,7 +387,7 @@ static bool synth_event_match(const char *system, const char *event,
int argc, const char **argv, struct dyn_event *ev);
static struct dyn_event_operations synth_event_ops = {
.create = synth_event_create,
.create = create_synth_event,
.show = synth_event_show,
.is_busy = synth_event_is_busy,
.free = synth_event_release,
@ -412,6 +412,7 @@ struct synth_event {
struct trace_event_class class;
struct trace_event_call call;
struct tracepoint *tp;
struct module *mod;
};
static bool is_synth_event(struct dyn_event *ev)
@ -1292,6 +1293,273 @@ struct hist_var_data {
struct hist_trigger_data *hist_data;
};
static int synth_event_check_arg_fn(void *data)
{
struct dynevent_arg_pair *arg_pair = data;
int size;
size = synth_field_size((char *)arg_pair->lhs);
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, const char *type,
const char *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, synth_event_check_arg_fn, 0, ';');
arg_pair.lhs = type;
arg_pair.rhs = name;
ret = dynevent_arg_pair_add(cmd, &arg_pair);
if (ret)
return ret;
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
ret = -EINVAL;
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_add_field);
/**
* 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, const char *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, NULL, ';');
arg.str = type_name;
ret = dynevent_arg_add(cmd, &arg);
if (ret)
return ret;
if (++cmd->n_fields > SYNTH_FIELDS_MAX)
ret = -EINVAL;
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_add_field_str);
/**
* 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,
unsigned int n_fields)
{
unsigned int 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;
}
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_add_fields);
/**
* __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
* @args: 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, const char *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, NULL, 0);
arg.str = name;
ret = dynevent_arg_add(cmd, &arg);
if (ret)
return ret;
va_start(args, mod);
for (;;) {
const char *type, *name;
type = va_arg(args, const char *);
if (!type)
break;
name = va_arg(args, const char *);
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);
return ret;
}
EXPORT_SYMBOL_GPL(__synth_event_gen_cmd_start);
/**
* 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
* @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, const char *name,
struct module *mod,
struct synth_field_desc *fields,
unsigned int n_fields)
{
struct dynevent_arg arg;
unsigned int 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, NULL, 0);
arg.str = name;
ret = dynevent_arg_add(cmd, &arg);
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;
}
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_gen_cmd_array_start);
static int __create_synth_event(int argc, const char *name, const char **argv)
{
struct synth_field *field, *fields[SYNTH_FIELDS_MAX];
@ -1360,6 +1628,56 @@ static int __create_synth_event(int argc, const char *name, const char **argv)
goto out;
}
/**
* synth_event_create - Create a new synthetic event
* @name: The name of the new sythetic 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(const char *name, struct synth_field_desc *fields,
unsigned int 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;
synth_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN);
ret = synth_event_gen_cmd_array_start(&cmd, name, mod,
fields, n_fields);
if (ret)
goto out;
ret = synth_event_gen_cmd_end(&cmd);
out:
kfree(buf);
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_create);
static int destroy_synth_event(struct synth_event *se)
{
int ret;
@ -1388,14 +1706,33 @@ static int destroy_synth_event(struct synth_event *se)
int synth_event_delete(const char *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)
if (se) {
mod = se->mod;
ret = destroy_synth_event(se);
}
mutex_unlock(&event_mutex);
if (mod) {
mutex_lock(&trace_types_lock);
/*
* 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();
mutex_unlock(&trace_types_lock);
}
return ret;
}
EXPORT_SYMBOL_GPL(synth_event_delete);
@ -1420,7 +1757,41 @@ int synth_event_run_command(const char *command)
return trace_run_command(command, create_or_delete_synth_event);
}
static int synth_event_create(int argc, const char **argv)
static int synth_event_run_cmd(struct dynevent_cmd *cmd)
{
struct synth_event *se;
int ret;
ret = trace_run_command(cmd->buf, create_or_delete_synth_event);
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_cmd);
}
EXPORT_SYMBOL_GPL(synth_event_cmd_init);
static int create_synth_event(int argc, const char **argv)
{
const char *name = argv[0];
int len;