Commit 7bbab38d authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt (VMware)

tracing: Use dyn_event framework for synthetic events

Use dyn_event framework for synthetic events. This shows
synthetic events on "tracing/dynamic_events" file in addition
to tracing/synthetic_events interface.

User can also define new events via tracing/dynamic_events
with "s:" prefix. So, the new syntax is below;

  s:[synthetic/]EVENT_NAME TYPE ARG; [TYPE ARG;]...

To remove events via tracing/dynamic_events, you can use
"-:" prefix as same as other events.

Link: http://lkml.kernel.org/r/154140861301.17322.15454611233735614508.stgit@devboxReviewed-by: default avatarTom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: default avatarTom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 0597c49c
...@@ -635,6 +635,7 @@ config HIST_TRIGGERS ...@@ -635,6 +635,7 @@ config HIST_TRIGGERS
depends on ARCH_HAVE_NMI_SAFE_CMPXCHG depends on ARCH_HAVE_NMI_SAFE_CMPXCHG
select TRACING_MAP select TRACING_MAP
select TRACING select TRACING
select DYNAMIC_EVENTS
default n default n
help help
Hist triggers allow one or more arbitrary trace event fields Hist triggers allow one or more arbitrary trace event fields
......
...@@ -4620,6 +4620,9 @@ static const char readme_msg[] = ...@@ -4620,6 +4620,9 @@ static const char readme_msg[] =
"\t accepts: event-definitions (one definition per line)\n" "\t accepts: event-definitions (one definition per line)\n"
"\t Format: p[:[<group>/]<event>] <place> [<args>]\n" "\t Format: p[:[<group>/]<event>] <place> [<args>]\n"
"\t r[maxactive][:[<group>/]<event>] <place> [<args>]\n" "\t r[maxactive][:[<group>/]<event>] <place> [<args>]\n"
#ifdef CONFIG_HIST_TRIGGERS
"\t s:[synthetic/]<event> <field> [<field>]\n"
#endif
"\t -:[<group>/]<event>\n" "\t -:[<group>/]<event>\n"
#ifdef CONFIG_KPROBE_EVENTS #ifdef CONFIG_KPROBE_EVENTS
"\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n" "\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n"
...@@ -4638,6 +4641,11 @@ static const char readme_msg[] = ...@@ -4638,6 +4641,11 @@ static const char readme_msg[] =
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>,\n" "\t b<bit-width>@<bit-offset>/<container-size>,\n"
"\t <type>\\[<array-size>\\]\n" "\t <type>\\[<array-size>\\]\n"
#ifdef CONFIG_HIST_TRIGGERS
"\t field: <stype> <name>;\n"
"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"
"\t [unsigned] char/int/long\n"
#endif
#endif #endif
" events/\t\t- Directory containing all trace event subsystems:\n" " events/\t\t- Directory containing all trace event subsystems:\n"
" enable\t\t- Write 0/1 to enable/disable tracing of all events\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "tracing_map.h" #include "tracing_map.h"
#include "trace.h" #include "trace.h"
#include "trace_dynevent.h"
#define SYNTH_SYSTEM "synthetic" #define SYNTH_SYSTEM "synthetic"
#define SYNTH_FIELDS_MAX 16 #define SYNTH_FIELDS_MAX 16
...@@ -292,6 +293,21 @@ struct hist_trigger_data { ...@@ -292,6 +293,21 @@ struct hist_trigger_data {
unsigned int n_max_var_str; unsigned int n_max_var_str;
}; };
static int synth_event_create(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);
static bool synth_event_match(const char *system, const char *event,
struct dyn_event *ev);
static struct dyn_event_operations synth_event_ops = {
.create = synth_event_create,
.show = synth_event_show,
.is_busy = synth_event_is_busy,
.free = synth_event_release,
.match = synth_event_match,
};
struct synth_field { struct synth_field {
char *type; char *type;
char *name; char *name;
...@@ -301,7 +317,7 @@ struct synth_field { ...@@ -301,7 +317,7 @@ struct synth_field {
}; };
struct synth_event { struct synth_event {
struct list_head list; struct dyn_event devent;
int ref; int ref;
char *name; char *name;
struct synth_field **fields; struct synth_field **fields;
...@@ -312,6 +328,32 @@ struct synth_event { ...@@ -312,6 +328,32 @@ struct synth_event {
struct tracepoint *tp; struct tracepoint *tp;
}; };
static bool is_synth_event(struct dyn_event *ev)
{
return ev->ops == &synth_event_ops;
}
static struct synth_event *to_synth_event(struct dyn_event *ev)
{
return container_of(ev, struct synth_event, devent);
}
static bool synth_event_is_busy(struct dyn_event *ev)
{
struct synth_event *event = to_synth_event(ev);
return event->ref != 0;
}
static bool synth_event_match(const char *system, const char *event,
struct dyn_event *ev)
{
struct synth_event *sev = to_synth_event(ev);
return strcmp(sev->name, event) == 0 &&
(!system || strcmp(system, SYNTH_SYSTEM) == 0);
}
struct action_data; struct action_data;
typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, typedef void (*action_fn_t) (struct hist_trigger_data *hist_data,
...@@ -402,7 +444,6 @@ static bool have_hist_err(void) ...@@ -402,7 +444,6 @@ static bool have_hist_err(void)
return false; return false;
} }
static LIST_HEAD(synth_event_list);
static DEFINE_MUTEX(synth_event_mutex); static DEFINE_MUTEX(synth_event_mutex);
struct synth_trace_event { struct synth_trace_event {
...@@ -738,14 +779,12 @@ static void free_synth_field(struct synth_field *field) ...@@ -738,14 +779,12 @@ static void free_synth_field(struct synth_field *field)
kfree(field); kfree(field);
} }
static struct synth_field *parse_synth_field(int argc, char **argv, static struct synth_field *parse_synth_field(int argc, const char **argv,
int *consumed) int *consumed)
{ {
struct synth_field *field; struct synth_field *field;
const char *prefix = NULL; const char *prefix = NULL, *field_type = argv[0], *field_name, *array;
char *field_type = argv[0], *field_name;
int len, ret = 0; int len, ret = 0;
char *array;
if (field_type[0] == ';') if (field_type[0] == ';')
field_type++; field_type++;
...@@ -762,20 +801,31 @@ static struct synth_field *parse_synth_field(int argc, char **argv, ...@@ -762,20 +801,31 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
*consumed = 2; *consumed = 2;
} }
len = strlen(field_name);
if (field_name[len - 1] == ';')
field_name[len - 1] = '\0';
field = kzalloc(sizeof(*field), GFP_KERNEL); field = kzalloc(sizeof(*field), GFP_KERNEL);
if (!field) if (!field)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
len = strlen(field_type) + 1; len = strlen(field_name);
array = strchr(field_name, '['); array = strchr(field_name, '[');
if (array)
len -= strlen(array);
else if (field_name[len - 1] == ';')
len--;
field->name = kmemdup_nul(field_name, len, GFP_KERNEL);
if (!field->name) {
ret = -ENOMEM;
goto free;
}
if (field_type[0] == ';')
field_type++;
len = strlen(field_type) + 1;
if (array) if (array)
len += strlen(array); len += strlen(array);
if (prefix) if (prefix)
len += strlen(prefix); len += strlen(prefix);
field->type = kzalloc(len, GFP_KERNEL); field->type = kzalloc(len, GFP_KERNEL);
if (!field->type) { if (!field->type) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -786,7 +836,8 @@ static struct synth_field *parse_synth_field(int argc, char **argv, ...@@ -786,7 +836,8 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
strcat(field->type, field_type); strcat(field->type, field_type);
if (array) { if (array) {
strcat(field->type, array); strcat(field->type, array);
*array = '\0'; if (field->type[len - 1] == ';')
field->type[len - 1] = '\0';
} }
field->size = synth_field_size(field->type); field->size = synth_field_size(field->type);
...@@ -800,11 +851,6 @@ static struct synth_field *parse_synth_field(int argc, char **argv, ...@@ -800,11 +851,6 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
field->is_signed = synth_field_signed(field->type); field->is_signed = synth_field_signed(field->type);
field->name = kstrdup(field_name, GFP_KERNEL);
if (!field->name) {
ret = -ENOMEM;
goto free;
}
out: out:
return field; return field;
free: free:
...@@ -868,9 +914,13 @@ static inline void trace_synth(struct synth_event *event, u64 *var_ref_vals, ...@@ -868,9 +914,13 @@ static inline void trace_synth(struct synth_event *event, u64 *var_ref_vals,
static struct synth_event *find_synth_event(const char *name) static struct synth_event *find_synth_event(const char *name)
{ {
struct dyn_event *pos;
struct synth_event *event; struct synth_event *event;
list_for_each_entry(event, &synth_event_list, list) { for_each_dyn_event(pos) {
if (!is_synth_event(pos))
continue;
event = to_synth_event(pos);
if (strcmp(event->name, name) == 0) if (strcmp(event->name, name) == 0)
return event; return event;
} }
...@@ -921,7 +971,7 @@ static int register_synth_event(struct synth_event *event) ...@@ -921,7 +971,7 @@ static int register_synth_event(struct synth_event *event)
ret = set_synth_event_print_fmt(call); ret = set_synth_event_print_fmt(call);
if (ret < 0) { if (ret < 0) {
trace_remove_event_call(call); trace_remove_event_call_nolock(call);
goto err; goto err;
} }
out: out:
...@@ -959,7 +1009,7 @@ static void free_synth_event(struct synth_event *event) ...@@ -959,7 +1009,7 @@ static void free_synth_event(struct synth_event *event)
kfree(event); kfree(event);
} }
static struct synth_event *alloc_synth_event(char *event_name, int n_fields, static struct synth_event *alloc_synth_event(const char *name, int n_fields,
struct synth_field **fields) struct synth_field **fields)
{ {
struct synth_event *event; struct synth_event *event;
...@@ -971,7 +1021,7 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields, ...@@ -971,7 +1021,7 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields,
goto out; goto out;
} }
event->name = kstrdup(event_name, GFP_KERNEL); event->name = kstrdup(name, GFP_KERNEL);
if (!event->name) { if (!event->name) {
kfree(event); kfree(event);
event = ERR_PTR(-ENOMEM); event = ERR_PTR(-ENOMEM);
...@@ -985,6 +1035,8 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields, ...@@ -985,6 +1035,8 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields,
goto out; goto out;
} }
dyn_event_init(&event->devent, &synth_event_ops);
for (i = 0; i < n_fields; i++) for (i = 0; i < n_fields; i++)
event->fields[i] = fields[i]; event->fields[i] = fields[i];
...@@ -1008,16 +1060,11 @@ struct hist_var_data { ...@@ -1008,16 +1060,11 @@ struct hist_var_data {
struct hist_trigger_data *hist_data; struct hist_trigger_data *hist_data;
}; };
static int create_synth_event(int argc, char **argv) static int __create_synth_event(int argc, const char *name, const char **argv)
{ {
struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; struct synth_field *field, *fields[SYNTH_FIELDS_MAX];
struct synth_event *event = NULL; struct synth_event *event = NULL;
bool delete_event = false;
int i, consumed = 0, n_fields = 0, ret = 0; int i, consumed = 0, n_fields = 0, ret = 0;
char *name;
mutex_lock(&event_mutex);
mutex_lock(&synth_event_mutex);
/* /*
* Argument syntax: * Argument syntax:
...@@ -1025,43 +1072,20 @@ static int create_synth_event(int argc, char **argv) ...@@ -1025,43 +1072,20 @@ static int create_synth_event(int argc, char **argv)
* - Remove synthetic event: !<event_name> field[;field] ... * - Remove synthetic event: !<event_name> field[;field] ...
* where 'field' = type field_name * where 'field' = type field_name
*/ */
if (argc < 1) {
ret = -EINVAL;
goto out;
}
name = argv[0]; if (name[0] == '\0' || argc < 1)
if (name[0] == '!') { return -EINVAL;
delete_event = true;
name++; mutex_lock(&event_mutex);
} mutex_lock(&synth_event_mutex);
event = find_synth_event(name); event = find_synth_event(name);
if (event) { if (event) {
if (delete_event) {
if (event->ref) {
ret = -EBUSY;
goto out;
}
ret = unregister_synth_event(event);
if (!ret) {
list_del(&event->list);
free_synth_event(event);
}
} else
ret = -EEXIST; ret = -EEXIST;
goto out; goto out;
} else if (delete_event) {
ret = -ENOENT;
goto out;
}
if (argc < 2) {
ret = -EINVAL;
goto out;
} }
for (i = 1; i < argc - 1; i++) { for (i = 0; i < argc - 1; i++) {
if (strcmp(argv[i], ";") == 0) if (strcmp(argv[i], ";") == 0)
continue; continue;
if (n_fields == SYNTH_FIELDS_MAX) { if (n_fields == SYNTH_FIELDS_MAX) {
...@@ -1091,7 +1115,7 @@ static int create_synth_event(int argc, char **argv) ...@@ -1091,7 +1115,7 @@ static int create_synth_event(int argc, char **argv)
} }
ret = register_synth_event(event); ret = register_synth_event(event);
if (!ret) if (!ret)
list_add(&event->list, &synth_event_list); dyn_event_add(&event->devent);
else else
free_synth_event(event); free_synth_event(event);
out: out:
...@@ -1106,57 +1130,77 @@ static int create_synth_event(int argc, char **argv) ...@@ -1106,57 +1130,77 @@ static int create_synth_event(int argc, char **argv)
goto out; goto out;
} }
static int release_all_synth_events(void) static int create_or_delete_synth_event(int argc, char **argv)
{ {
struct synth_event *event, *e; const char *name = argv[0];
int ret = 0; struct synth_event *event = NULL;
int ret;
/* trace_run_command() ensures argc != 0 */
if (name[0] == '!') {
mutex_lock(&event_mutex); mutex_lock(&event_mutex);
mutex_lock(&synth_event_mutex); mutex_lock(&synth_event_mutex);
event = find_synth_event(name + 1);
list_for_each_entry(event, &synth_event_list, list) { if (event) {
if (event->ref) { if (event->ref)
mutex_unlock(&synth_event_mutex); ret = -EBUSY;
return -EBUSY; else {
}
}
list_for_each_entry_safe(event, e, &synth_event_list, list) {
ret = unregister_synth_event(event); ret = unregister_synth_event(event);
if (!ret) { if (!ret) {
list_del(&event->list); dyn_event_remove(&event->devent);
free_synth_event(event); free_synth_event(event);
} else
break;
} }
}
} else
ret = -ENOENT;
mutex_unlock(&synth_event_mutex); mutex_unlock(&synth_event_mutex);
mutex_unlock(&event_mutex); mutex_unlock(&event_mutex);
return ret; return ret;
} }
ret = __create_synth_event(argc - 1, name, (const char **)argv + 1);
return ret == -ECANCELED ? -EINVAL : ret;
}
static void *synth_events_seq_start(struct seq_file *m, loff_t *pos) static int synth_event_create(int argc, const char **argv)
{ {
mutex_lock(&synth_event_mutex); const char *name = argv[0];
int len;
return seq_list_start(&synth_event_list, *pos); if (name[0] != 's' || name[1] != ':')
} return -ECANCELED;
name += 2;
static void *synth_events_seq_next(struct seq_file *m, void *v, loff_t *pos) /* This interface accepts group name prefix */
{ if (strchr(name, '/')) {
return seq_list_next(v, &synth_event_list, pos); len = sizeof(SYNTH_SYSTEM "/") - 1;
if (strncmp(name, SYNTH_SYSTEM "/", len))
return -EINVAL;
name += len;
}
return __create_synth_event(argc - 1, name, argv + 1);
} }
static void synth_events_seq_stop(struct seq_file *m, void *v) static int synth_event_release(struct dyn_event *ev)
{ {
mutex_unlock(&synth_event_mutex); struct synth_event *event = to_synth_event(ev);
int ret;
if (event->ref)
return -EBUSY;
ret = unregister_synth_event(event);
if (ret)
return ret;
dyn_event_remove(ev);
free_synth_event(event);
return 0;
} }
static int synth_events_seq_show(struct seq_file *m, void *v) static int __synth_event_show(struct seq_file *m, struct synth_event *event)
{ {
struct synth_field *field; struct synth_field *field;
struct synth_event *event = v;
unsigned int i; unsigned int i;
seq_printf(m, "%s\t", event->name); seq_printf(m, "%s\t", event->name);
...@@ -1174,11 +1218,30 @@ static int synth_events_seq_show(struct seq_file *m, void *v) ...@@ -1174,11 +1218,30 @@ static int synth_events_seq_show(struct seq_file *m, void *v)
return 0; return 0;
} }
static int synth_event_show(struct seq_file *m, struct dyn_event *ev)
{
struct synth_event *event = to_synth_event(ev);
seq_printf(m, "s:%s/", event->class.system);
return __synth_event_show(m, event);
}
static int synth_events_seq_show(struct seq_file *m, void *v)
{
struct dyn_event *ev = v;
if (!is_synth_event(ev))
return 0;
return __synth_event_show(m, to_synth_event(ev));
}
static const struct seq_operations synth_events_seq_op = { static const struct seq_operations synth_events_seq_op = {
.start = synth_events_seq_start, .start = dyn_event_seq_start,
.next = synth_events_seq_next, .next = dyn_event_seq_next,
.stop = synth_events_seq_stop, .stop = dyn_event_seq_stop,
.show = synth_events_seq_show .show = synth_events_seq_show,
}; };
static int synth_events_open(struct inode *inode, struct file *file) static int synth_events_open(struct inode *inode, struct file *file)
...@@ -1186,7 +1249,7 @@ static int synth_events_open(struct inode *inode, struct file *file) ...@@ -1186,7 +1249,7 @@ static int synth_events_open(struct inode *inode, struct file *file)
int ret; int ret;
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
ret = release_all_synth_events(); ret = dyn_events_release_all(&synth_event_ops);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -1199,7 +1262,7 @@ static ssize_t synth_events_write(struct file *file, ...@@ -1199,7 +1262,7 @@ static ssize_t synth_events_write(struct file *file,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
return trace_parse_run_command(file, buffer, count, ppos, return trace_parse_run_command(file, buffer, count, ppos,
create_synth_event); create_or_delete_synth_event);
} }
static const struct file_operations synth_events_fops = { static const struct file_operations synth_events_fops = {
...@@ -5791,6 +5854,12 @@ static __init int trace_events_hist_init(void) ...@@ -5791,6 +5854,12 @@ static __init int trace_events_hist_init(void)
struct dentry *d_tracer; struct dentry *d_tracer;
int err = 0; int err = 0;
err = dyn_event_register(&synth_event_ops);
if (err) {
pr_warn("Could not register synth_event_ops\n");
return err;
}
d_tracer = tracing_init_dentry(); d_tracer = tracing_init_dentry();
if (IS_ERR(d_tracer)) { if (IS_ERR(d_tracer)) {
err = PTR_ERR(d_tracer); err = PTR_ERR(d_tracer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment