tracing: Use dyn_event framework for synthetic events
commit 7bbab38d07f3185fddf6fce126e2239010efdfce upstream. 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@devbox Reviewed-by: Tom Zanussi <tom.zanussi@linux.intel.com> Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> Signed-off-by: George Guo <guodongtai@kylinos.cn> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
7d00580499
commit
73b24eeb0e
@@ -633,6 +633,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
|
||||||
|
|||||||
@@ -4681,6 +4681,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"
|
||||||
@@ -4694,6 +4697,11 @@ static const char readme_msg[] =
|
|||||||
"\t $stack<index>, $stack, $retval, $comm\n"
|
"\t $stack<index>, $stack, $retval, $comm\n"
|
||||||
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string,\n"
|
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string,\n"
|
||||||
"\t b<bit-width>@<bit-offset>/<container-size>\n"
|
"\t b<bit-width>@<bit-offset>/<container-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 @@
|
|||||||
|
|
||||||
#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
|
||||||
@@ -291,6 +292,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;
|
||||||
@@ -300,7 +316,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;
|
||||||
@@ -311,6 +327,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,
|
||||||
@@ -401,7 +443,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 {
|
||||||
@@ -758,14 +799,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++;
|
||||||
@@ -782,20 +821,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;
|
||||||
@@ -806,7 +856,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);
|
||||||
@@ -820,11 +871,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:
|
||||||
@@ -888,9 +934,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;
|
||||||
}
|
}
|
||||||
@@ -941,7 +991,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:
|
||||||
@@ -979,7 +1029,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;
|
||||||
@@ -991,7 +1041,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);
|
||||||
@@ -1005,6 +1055,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];
|
||||||
|
|
||||||
@@ -1028,16 +1080,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:
|
||||||
@@ -1045,43 +1092,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) {
|
for (i = 0; i < argc - 1; i++) {
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; 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) {
|
||||||
@@ -1111,7 +1135,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:
|
||||||
@@ -1126,57 +1150,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 int synth_event_create(int argc, const char **argv)
|
||||||
static void *synth_events_seq_start(struct seq_file *m, loff_t *pos)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
|
/* This interface accepts group name prefix */
|
||||||
|
if (strchr(name, '/')) {
|
||||||
|
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_next(struct seq_file *m, void *v, loff_t *pos)
|
static int synth_event_release(struct dyn_event *ev)
|
||||||
{
|
{
|
||||||
return seq_list_next(v, &synth_event_list, pos);
|
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 void synth_events_seq_stop(struct seq_file *m, void *v)
|
static int __synth_event_show(struct seq_file *m, struct synth_event *event)
|
||||||
{
|
|
||||||
mutex_unlock(&synth_event_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int synth_events_seq_show(struct seq_file *m, void *v)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
@@ -1194,11 +1238,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)
|
||||||
@@ -1206,7 +1269,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;
|
||||||
}
|
}
|
||||||
@@ -1219,7 +1282,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 = {
|
||||||
@@ -5913,6 +5976,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);
|
||||||
|
|||||||
Reference in New Issue
Block a user