tracing: Add support for in-kernel dynamic event API
Subsystems (2)
v4 cover tracing: Add support for in-kernel dynamic event API
Hi,
This is v4 of 'tracing: Add support for in-kernel dynamic event API', incorporating changes based on suggestions from Masami.
This patchset is the same as the v3 version but add Masami's acks for all but '[PATCH 04/12] tracing: Add dynamic event command creation interface', which moves the dynevent command interface code to trace_dynevent.{c,.h}.
Thanks,
Tom
Text from the v3 posting:
This is v3 of 'tracing: Add support for in-kernel dynamic event API', incorporating changes based on suggestions from Masami and Steven.
- Rebased to trace/for-next
- Regularized the entire API to use synth_event_*, kprobe_event_*, dynevent_*, and added some new macros and functions to make things more consistent
- Introduced trace_array_find_get() and used it in trace_array_get_file() as suggested
- Removed exports from dynevent_cmd functions that didn't need to be exported
- Switched the param order of __kprobe_event_gen_cmd_start() to fix a problem found building with clang. Apparently varargs and implicit promotion of types like bool don't mix. Thanks to Nick Desaulniers for pointing that out.
- Updated the documentation for all of the above
Text from the v2 posting:
This is v2 of the previous 'tracing: Add support for in-kernel synthetic event API', and is largely a rewrite based on suggestions from Masami to expand the scope to include other types of dynamic events such as kprobe events, in addition to the original sythetic event focus.
The functionality of the original API remains available, though it's in a slightly different form due to the use of the new dynevent_cmd API it's now based on. The dynevent_cmd API provides a common dynamic event command generation capability that both the synthetic event API and the kprobe event API are now based on, and there are now test modules demonstrating both the synthetic event and kprobes APIs.
A couple of the patches are snippets from Masami's 'tracing:
bootconfig: Boot-time tracing and Extra boot config' series, and the patch implementing the dynevent_cmd API includes some of the spnprintf() generating code from that patchset.
Because I used Masami's gen_*_cmd() naming suggestion for generating the commands, the previous patchset's generate_*() functions were renamed to trace_*() to avoid confusion, and probably is better naming anyway.
An overview of the user-visible changes in comparison to v1:
- create_synth_event() using an array of synth_desc_fields remains unchanged and works the same way as previously
- gen_synth_cmd() takes a variable-length number of args which represent 'type field;' pairs. Using this with no field args basically replaces the previous 'create_empty_synth_event()'
- The 'add_synth_field()' and 'add_synth_fields()' function from v1 are essentially the same except that they now take a dynevent_cmd instead of a synth_event pointer
- The finalize_synth_event() from v1 is replaced by create_dynevent() in the new version.
- The new v2 API includes some additional functions to initialize the dynevent_cmd - synth_dynevent_cmd() is used to do that. While it's an extra step, it makes it easier to do error handling.
- There's a new trace_synth_event() function that traces a synthetic event using a variable-arg list of values.
- The original generate_synth_event() using an array of values is now called trace_synth_event_array().
- For piecewise event tracing, the original generate_synth_event_start() and generate_synth_event_end() have now been renamed to trace_synth_event_end().
- add_next_synth_val() and add_synth_val() remain the same.
- A similar API and test module demonstrating the API has been added for kprobe events
- Both the synthetic events and kprobe events API is based on the dynevent_cmd API, newly added
- The Documentation for all of the above has been updated
Text from the orginal v1 posting:
I've recently had several requests and suggestions from users to add support for the creation and generation of synthetic events from kernel code such as modules, and not just from the available command line commands.
This patchset adds support for that. The first three patches add some minor preliminary setup, followed by the two main patches that add the ability to create and generate synthetic events from the kernel. The next patch adds a test module that demonstrates actual use of the API and verifies that it works as intended, followed by Documentation.
Special thanks to Artem Bityutskiy, who worked with me over several iterations of the API, and who had many great suggestions on the details of the interface, and pointed out several problems with the code itself.
The following changes since commit 34f71a4a2de84dde52ccfcb96ce25240ea7981a8:
tracing: Add new testcases for hist trigger parsing errors (2020-01-28 23:17:31 -0500)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/zanussi/linux-trace.git ftrace/synth-event-gen-v4
Tom Zanussi (12):
tracing: Add trace_array_find/_get() to find instance trace arrays
tracing: Add trace_get/put_event_file()
tracing: Add synth_event_delete()
tracing: Add dynamic event command creation interface
tracing: Add synthetic event command generation functions
tracing: Change trace_boot to use synth_event interface
tracing: Add synth_event_trace() and related functions
tracing: Add synth event generation test module
tracing: Add kprobe event command generation functions
tracing: Change trace_boot to use kprobe_event interface
tracing: Add kprobe event command generation test module
tracing: Documentation for in-kernel synthetic event API
Documentation/trace/events.rst | 515 ++++++++++++++++++++ include/linux/trace_events.h | 124 +++++ kernel/trace/Kconfig | 25 + kernel/trace/Makefile | 2 + kernel/trace/kprobe_event_gen_test.c | 225 +++++++++ kernel/trace/synth_event_gen_test.c | 523 ++++++++++++++++++++ kernel/trace/trace.c | 43 +- kernel/trace/trace.h | 2 + kernel/trace/trace_boot.c | 66 ++- kernel/trace/trace_dynevent.c | 240 ++++++++++ kernel/trace/trace_dynevent.h | 33 ++ kernel/trace/trace_events.c | 85 ++++ kernel/trace/trace_events_hist.c | 896 ++++++++++++++++++++++++++++++++++- kernel/trace/trace_kprobe.c | 160 ++++++- 14 files changed, 2867 insertions(+), 72 deletions(-) create mode 100644 kernel/trace/kprobe_event_gen_test.c create mode 100644 kernel/trace/synth_event_gen_test.c
Signature · 1 line
2.14.1
PATCH v4 1/12 tracing: Add trace_array_find/_get() to find instance trace arrays
Add a new trace_array_find() function that can be used to find a trace array given the instance name, and replace existing code that does the same thing with it. Also add trace_array_find_get() which does the same but returns the trace array after upping its refcount.
Also make both available for use outside of trace.c.
--- kernel/trace/trace.c | 43 +++++++++++++++++++++++++++++++++---------- kernel/trace/trace.h | 2 ++ 2 files changed, 35 insertions(+), 10 deletions(-)
kernel/trace/trace.c +33 -10
@@ -8501,6 +8501,34 @@ static void update_tracer_options(struct trace_array *tr)mutex_unlock (&trace_types_lock); } +/* Must have trace_types_lock held */ +struct trace_array *trace_array_find (const char *instance) +{ +struct trace_array *tr, *found =NULL ; + +list_for_each_entry (tr, &ftrace_trace_arrays, list) { +if (tr->name &&strcmp (tr->name, instance) ==0 ) { + found = tr; +break ; + } + } + +return found; +} + +struct trace_array *trace_array_find_get (const char *instance) +{ +struct trace_array *tr; + +mutex_lock (&trace_types_lock); + tr =trace_array_find (instance); +if (tr) + tr->ref++; +mutex_unlock (&trace_types_lock); + +return tr; +} +static struct trace_array *trace_array_create (const char *name) {struct trace_array *tr;
@@ -8577,10 +8605,8 @@ static int instance_mkdir(const char *name)mutex_lock (&trace_types_lock); ret = -EEXIST ; -list_for_each_entry (tr, &ftrace_trace_arrays, list) { -if (tr->name &&strcmp (tr->name, name) ==0 ) - goto out_unlock; - } +if (trace_array_find (name)) + goto out_unlock; tr =trace_array_create (name);
@@ -8708,12 +8734,9 @@ static int instance_rmdir(const char *name)mutex_lock (&trace_types_lock); ret = -ENODEV ; -list_for_each_entry (tr, &ftrace_trace_arrays, list) { -if (tr->name &&strcmp (tr->name, name) ==0 ) { - ret =__remove_instance (tr); -break ; - } - } + tr =trace_array_find (name); +if (tr) + ret =__remove_instance (tr);mutex_unlock (&trace_types_lock);mutex_unlock (&event_mutex);
kernel/trace/trace.h +2 -0
@@ -358,6 +358,8 @@ extern struct mutex trace_types_lock;extern int trace_array_get (struct trace_array *tr);extern int tracing_check_open_get_tr (struct trace_array *tr); +extern struct trace_array *trace_array_find (const char *instance); +extern struct trace_array *trace_array_find_get (const char *instance);extern int tracing_set_time_stamp_abs (struct trace_array *tr,bool abs);extern int tracing_set_clock (struct trace_array *tr,const char *clockstr);
Signature · 1 line
2.14.1
Steven Rostedt
maintainerRe: [PATCH v4 01/12] tracing: Add trace_array_find/_get() to find instance trace arrays
Quoted 2 lines · On Wed, 29 Jan 2020 12:59:21 -0600 Tom Zanussi <[email protected]> wrote:
Signed-off-by Tom Zanussi <[email protected]>Acked-by Masami Hiramatsu <[email protected]>
FYI, I prefer the "Signed-off-by" to come at the end.
I switched them all.
-- Steve
PATCH v4 2/12 tracing: Add trace_get/put_event_file()
In reply to Tom ZanussiAdd a function to get an event file and prevent it from going away on module or instance removal.
trace_get_event_file() will find an event file in a given instance (if instance is NULL, it assumes the top trace array) and return it, pinning the instance's trace array as well as the event's module, if applicable, so they won't go away while in use.
trace_put_event_file() does the matching release.
--- include/linux/trace_events.h | 5 +++ kernel/trace/trace_events.c | 85 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+)
include/linux/trace_events.h +5 -0
@@ -349,6 +349,11 @@ enum { EVENT_FILE_FL_WAS_ENABLED_BIT, }; +extern struct trace_event_file *trace_get_event_file (const char *instance, +const char *system, +const char *event); +extern void trace_put_event_file (struct trace_event_file *file); +/* * Event file flags: * ENABLED - The event is enabled
kernel/trace/trace_events.c +85 -0
91 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 3/12 tracing: Add synth_event_delete()
In reply to Tom Zanussicreate_or_delete_synth_event() contains code to delete a synthetic event, which would be useful on its own - specifically, it would be useful to allow event-creating modules to call it separately.
Separate out the delete code from that function and create an exported function named synth_event_delete().
--- include/linux/trace_events.h | 2 ++ kernel/trace/trace_events_hist.c | 57 +++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 16 deletions(-)
include/linux/trace_events.h +2 -0
@@ -354,6 +354,8 @@ extern struct trace_event_file *trace_get_event_file(const char *instance, const char *event);extern void trace_put_event_file (struct trace_event_file *file); +extern int synth_event_delete (const char *name); +/* * Event file flags: * ENABLED - The event is enabled
kernel/trace/trace_events_hist.c +41 -16
70 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 4/12 tracing: Add dynamic event command creation interface
In reply to Tom ZanussiAdd an interface used to build up dynamic event creation commands, such as synthetic and kprobe events. Interfaces specific to those particular types of events and others can be built on top of this interface.
Command creation is started by first using the dynevent_cmd_init() function to initialize the dynevent_cmd object. Following that, args are appended and optionally checked by the dynevent_arg_add() and dynevent_arg_pair_add() functions, which use objects representing arguments and pairs of arguments, initialized respectively by dynevent_arg_init() and dynevent_arg_pair_init(). Finally, once all args have been successfully added, the command is finalized and actually created using dynevent_create().
The code here for actually printing into the dyn_event->cmd buffer using snprintf() etc was adapted from v4 of Masami's 'tracing/boot:
Add synthetic event support' patch.
--- include/linux/trace_events.h | 23 ++++ kernel/trace/trace_dynevent.c | 240 ++++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace_dynevent.h | 33 ++++++ 3 files changed, 296 insertions(+)
include/linux/trace_events.h +23 -0
@@ -354,6 +354,29 @@ extern struct trace_event_file *trace_get_event_file(const char *instance, const char *event);extern void trace_put_event_file (struct trace_event_file *file); +#define MAX_DYNEVENT_CMD_LEN (2048) + +enum dynevent_type { +DYNEVENT_TYPE_NONE , +}; + +struct dynevent_cmd ; + +typedef int (*dynevent_create_fn_t )(struct dynevent_cmd *cmd); + +struct dynevent_cmd { +char *buf; +const char *event_name; +int maxlen; +int remaining; +unsigned int n_fields; +enum dynevent_type type; +dynevent_create_fn_t run_command; +void *private_data; +}; + +extern int dynevent_create (struct dynevent_cmd *cmd); +extern int synth_event_delete (const char *name);/*
kernel/trace/trace_dynevent.c +240 -0
243 lines not on this page · raw message
kernel/trace/trace_dynevent.h +33 -0
@@ -117,4 +117,37 @@ int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type);#define for_each_dyn_event_safe (pos, n) \ list_for_each_entry_safe(pos, n, &dyn_event_list, list) +extern void dynevent_cmd_init (struct dynevent_cmd *cmd,char *buf,int maxlen, +enum dynevent_type type, +dynevent_create_fn_t run_command); + +typedef int (*dynevent_check_arg_fn_t )(void *data); + +struct dynevent_arg { +const char *str; +char separator;/* e.g. ';', ',', or nothing */ +dynevent_check_arg_fn_t check_arg; +}; + +extern void dynevent_arg_init (struct dynevent_arg *arg, +dynevent_check_arg_fn_t check_arg, +char separator); +extern int dynevent_arg_add (struct dynevent_cmd *cmd, +struct dynevent_arg *arg); + +struct dynevent_arg_pair { +const char *lhs; +const char *rhs; +char operator;/* e.g. '=' or nothing */ +char separator;/* e.g. ';', ',', or nothing */ +dynevent_check_arg_fn_t check_arg; +}; + +extern void dynevent_arg_pair_init (struct dynevent_arg_pair *arg_pair, +dynevent_check_arg_fn_t check_arg, +char operator,char separator); +extern int dynevent_arg_pair_add (struct dynevent_cmd *cmd, +struct dynevent_arg_pair *arg_pair); +extern int dynevent_str_add (struct dynevent_cmd *cmd,const char *str); +#endif
Signature · 1 line
2.14.1
Masami Hiramatsu
maintainerRe: [PATCH v4 04/12] tracing: Add dynamic event command creation interface
Hi Tom
Quoted 84 lines · On Wed, 29 Jan 2020 12:59:24 -0600 Tom Zanussi <[email protected]> wrote:
Add an interface used to build up dynamic event creation commands, such as synthetic and kprobe events. Interfaces specific to those particular types of events and others can be built on top of this interface.
Command creation is started by first using the
dynevent_cmd_init()function to initialize thedynevent_cmdobject. Following that, args are appended and optionally checked by thedynevent_arg_add()anddynevent_arg_pair_add()functions, which use objects representing arguments and pairs of arguments, initialized respectively bydynevent_arg_init()anddynevent_arg_pair_init(). Finally, once all args have been successfully added, the command is finalized and actually created usingdynevent_create().The code here for actually printing into the
dyn_event->cmdbuffer usingsnprintf()etc was adapted from v4 of Masami's 'tracing/boot:
Add synthetic event support' patch.Signed-off-by Tom Zanussi <[email protected]>Signed-off-by Masami Hiramatsu <[email protected]>--- include/linux/trace_events.h | 23 ++++ kernel/trace/trace_dynevent.c | 240 ++++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace_dynevent.h | 33 ++++++ 3 files changed, 296 insertions(+)include/linux/trace_events.h +23
-0@@ -354,6 +354,29 @@ extern struct trace_event_file *trace_get_event_file(const char *instance, const char *event);extern void trace_put_event_file (struct trace_event_file *file); +#define MAX_DYNEVENT_CMD_LEN (2048) + +enum dynevent_type { +DYNEVENT_TYPE_NONE , +}; + +struct dynevent_cmd ; + +typedef int (*dynevent_create_fn_t )(struct dynevent_cmd *cmd); + +struct dynevent_cmd { +char *buf; +const char *event_name; +int maxlen; +int remaining; +unsigned int n_fields; +enum dynevent_type type; +dynevent_create_fn_t run_command; +void *private_data; +}; + +extern int dynevent_create (struct dynevent_cmd *cmd); +extern int synth_event_delete (const char *name);/* kernel/trace/trace_dynevent.c +16
-0@@ -223,3 +223,243 @@ static __init int init_dynamic_event(void)return 0 ; }fs_initcall (init_dynamic_event); + +/** +* dynevent_arg_add - Add an arg to a dynevent_cmd +* @cmd: A pointer to the dynevent_cmd struct representing the new event cmd +* @arg: The argument to append to the current cmd +* +* Append an argument to a dynevent_cmd. The argument string will be +* appended to the current cmd string, followed by a separator, if +* applicable. Before the argument is added, the check_arg() +* function, if defined, is called. +* +* The cmd string, separator, and check_arg() function should be set +* using the dynevent_arg_init() before any arguments are added using +* this function. +* +* Return: 0 if successful, error otherwise.
kernel/trace/trace_dynevent.c static __init int init_dynamic_event(void) · line 251
Quoted 10 lines
kernel/trace/trace_dynevent.c +10
-0+*/ +int dynevent_arg_add (struct dynevent_cmd *cmd, +struct dynevent_arg *arg) +{ +int ret =0 ; +int delta; +char *q; + +if (arg->check_arg) { + ret = arg->check_arg (arg);
Hmm, this seems a bit odd. The check_arg() arg object member check the validity of the object itself. What about moving those checker into dynevent_cmd instead of dynevent_arg?
(Or, just pass the appropriate checker as the 3rd parameter of
this function)
Quoted 37 lines
kernel/trace/trace_dynevent.c +37
-0+if (ret) +return ret; + } + + q = cmd->buf + (cmd->maxlen - cmd->remaining); + + delta =snprintf (q, cmd->remaining," %s%c" , arg->str, arg->separator); +if (delta >= cmd->remaining) { +pr_err ("String is too long: %s\n" , arg->str); +return -E2BIG ; + } + cmd->remaining -= delta; + +return ret; +} + +/** +* dynevent_arg_pair_add - Add an arg pair to a dynevent_cmd +* @cmd: A pointer to the dynevent_cmd struct representing the new event cmd +* @arg_pair: The argument pair to append to the current cmd +* +* Append an argument pair to a dynevent_cmd. An argument pair +* consists of a left-hand-side argument and a right-hand-side +* argument separated by an operator, which can be whitespace, all +* followed by a separator, if applicable. This can be used to add +* arguments of the form 'type variable_name;' or 'x+y'. +* +* The lhs argument string will be appended to the current cmd string, +* followed by an operator, if applicable, followd by the rhs string, +* followed finally by a separator, if applicable. Before anything is +* added, the check_arg() function, if defined, is called. +* +* The cmd strings, operator, separator, and check_arg() function +* should be set using the dynevent_arg_pair_init() before any arguments +* are added using this function. +* +* Return: 0 if successful, error otherwise.
kernel/trace/trace_dynevent.c static __init int init_dynamic_event(void) · line 298
Quoted 10 lines
kernel/trace/trace_dynevent.c +10
-0+*/ +int dynevent_arg_pair_add (struct dynevent_cmd *cmd, +struct dynevent_arg_pair *arg_pair) +{ +int ret =0 ; +int delta; +char *q; + +if (arg_pair->check_arg) { + ret = arg_pair->check_arg (arg_pair);
Ditto.
Quoted 11 lines
kernel/trace/trace_dynevent.c +11
-0+if (ret) +return ret; + } + + q = cmd->buf + (cmd->maxlen - cmd->remaining); + + delta =snprintf (q, cmd->remaining," %s%c" , arg_pair->lhs, + arg_pair->operator); +if (delta >= cmd->remaining) { +pr_err ("field string is too long: %s\n" , arg_pair->lhs); +return -E2BIG ;
kernel/trace/trace_dynevent.c static __init int init_dynamic_event(void) · line 319
Quoted 10 lines
kernel/trace/trace_dynevent.c +10
-0+ } + cmd->remaining -= delta; q += delta; + + delta =snprintf (q, cmd->remaining,"%s%c" , arg_pair->rhs, + arg_pair->separator); +if (delta >= cmd->remaining) { +pr_err ("field string is too long: %s\n" , arg_pair->rhs); +return -E2BIG ; + } + cmd->remaining -= delta; q += delta;
Here, q += delta is redundant, because q is not used anymore.
kernel/trace/trace_dynevent.c static __init int init_dynamic_event(void) · line 255
Quoted 4 lines
kernel/trace/trace_dynevent.c +4
-0+ +return ret; +} +
Thank you,
Signature · 1 line
Masami Hiramatsu <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v4 04/12] tracing: Add dynamic event command creation interface
Quoted 7 lines · On Thu, 30 Jan 2020 11:40:28 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 7 lines
kernel/trace/trace_dynevent.c +7
-0+int dynevent_arg_add (struct dynevent_cmd *cmd, +struct dynevent_arg *arg) +{ +int ret =0 ; +int delta; +char *q; +
Quoted 8 lines
kernel/trace/trace_dynevent.c static __init int init_dynamic_event(void) · line 251
Quoted 2 lines
kernel/trace/trace_dynevent.c +2
-0+if (arg->check_arg) { + ret = arg->check_arg (arg);Hmm, this seems a bit odd. The
check_arg()arg object member check the validity of the object itself. What about moving those checker intodynevent_cmdinstead ofdynevent_arg?
(Or, just pass the appropriate checker as the 3rd parameter of
this function)
Masami,
Thanks for reviewing these patches.
Tom,
Please send updates on top of this series. I've already pulled them in my queue and there going through my test suite now. It will be easier just to add changes on top than to send another version at this point.
You can see what I'm testing via my repo on branch ftrace/core.
-- Steve
PATCH v4 5/12 tracing: Add synthetic event command generation functions
In reply to Tom ZanussiAdd 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.
--- include/linux/trace_events.h | 37 ++++ kernel/trace/trace_events_hist.c | 379 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 412 insertions(+), 4 deletions(-)
include/linux/trace_events.h +37 -0
@@ -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
kernel/trace/trace_events_hist.c +375 -4
428 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 6/12 tracing: Change trace_boot to use synth_event interface
In reply to Tom ZanussiHave trace_boot_add_synth_event() use the synth_event interface.
Also, rename synth_event_run_cmd() to synth_event_run_command() now that trace_boot's version is gone.
--- kernel/trace/trace_boot.c | 31 ++++++++++++------------------- kernel/trace/trace_events_hist.c | 9 ++------- 2 files changed, 14 insertions(+), 26 deletions(-)
kernel/trace/trace_boot.c +12 -19
@@ -133,38 +133,31 @@ trace_boot_add_kprobe_event(struct xbc_node *node, const char *event)#endif #ifdef CONFIG_HIST_TRIGGERS -extern int synth_event_run_command (const char *command); -static int __init trace_boot_add_synth_event (struct xbc_node *node,const char *event) { +struct dynevent_cmd cmd;struct xbc_node *anode; -char buf[MAX_BUF_LEN ], *q; +char buf[MAX_BUF_LEN ];const char *p; -int len, delta, ret; +int ret; - len =ARRAY_SIZE (buf); - delta =snprintf (buf, len,"%s" , event); -if (delta >= len) { -pr_err ("Event name is too long: %s\n" , event); -return -E2BIG ; - } - len -= delta; q = buf + delta; +synth_event_cmd_init (&cmd, buf,MAX_BUF_LEN ); + + ret =synth_event_gen_cmd_start (&cmd, event,NULL ); +if (ret) +return ret;xbc_node_for_each_array_value (node,"fields" , anode, p) { - delta =snprintf (q, len," %s;" , p); -if (delta >= len) { -pr_err ("fields string is too long: %s\n" , p); -return -E2BIG ; - } - len -= delta; q += delta; + ret =synth_event_add_field_str (&cmd, p); +if (ret) +return ret; } - ret =synth_event_run_command (buf); + ret =synth_event_gen_cmd_end (&cmd);if (ret <0 )pr_err ("Failed to add synthetic event: %s\n" , buf); -return ret; }#else
kernel/trace/trace_events_hist.c +2 -7
@@ -1752,12 +1752,7 @@ static int create_or_delete_synth_event(int argc, char **argv)return ret == -ECANCELED ? -EINVAL : ret; } -int synth_event_run_command (const char *command) -{ -return trace_run_command (command, create_or_delete_synth_event); -} - -static int synth_event_run_cmd (struct dynevent_cmd *cmd) +static int synth_event_run_command (struct dynevent_cmd *cmd) {struct synth_event *se;int ret;
@@ -1787,7 +1782,7 @@ static int synth_event_run_cmd(struct dynevent_cmd *cmd)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); + synth_event_run_command); }EXPORT_SYMBOL_GPL (synth_event_cmd_init);
Signature · 1 line
2.14.1
Re: [PATCH v4 06/12] tracing: Change trace_boot to use synth_event interface
Hi Steve,
It looks like you missed this patch when you pulled the other ones into
for-next.
Tom
Quoted 102 lines · Tom Zanussi
Have
trace_boot_add_synth_event()use thesynth_eventinterface.Also, rename
synth_event_run_cmd()tosynth_event_run_command()now that trace_boot's version is gone.Signed-off-by Tom Zanussi <[email protected]>Acked-by Masami Hiramatsu <[email protected]>--- kernel/trace/trace_boot.c | 31 ++++++++++++---------------- --- kernel/trace/trace_events_hist.c | 9 ++------- 2 files changed, 14 insertions(+), 26 deletions(-)kernel/trace/trace_boot.c +12
-19@@ -133,38 +133,31 @@ trace_boot_add_kprobe_event(struct xbc_node *node, const char *event)#endif #ifdef CONFIG_HIST_TRIGGERS -extern int synth_event_run_command (const char *command); -static int __init trace_boot_add_synth_event (struct xbc_node *node,const char *event) { +struct dynevent_cmd cmd;struct xbc_node *anode; -char buf[MAX_BUF_LEN ], *q; +char buf[MAX_BUF_LEN ];const char *p; -int len, delta, ret; +int ret; - len =ARRAY_SIZE (buf); - delta =snprintf (buf, len,"%s" , event); -if (delta >= len) { -pr_err ("Event name is too long: %s\n" , event); -return -E2BIG ; - } - len -= delta; q = buf + delta; +synth_event_cmd_init (&cmd, buf,MAX_BUF_LEN ); + + ret =synth_event_gen_cmd_start (&cmd, event,NULL ); +if (ret) +return ret;xbc_node_for_each_array_value (node,"fields" , anode, p) { - delta =snprintf (q, len," %s;" , p); -if (delta >= len) { -pr_err ("fields string is too long: %s\n" , p); -return -E2BIG ; - } - len -= delta; q += delta; + ret =synth_event_add_field_str (&cmd, p); +if (ret) +return ret; } - ret =synth_event_run_command (buf); + ret =synth_event_gen_cmd_end (&cmd);if (ret <0 )pr_err ("Failed to add synthetic event: %s\n" , buf); -return ret;}
#elsekernel/trace/trace_events_hist.c +1
-6@@ -1752,12 +1752,7 @@ static int create_or_delete_synth_event(int argc, char **argv)return ret == -ECANCELED ? -EINVAL : ret; } -int synth_event_run_command (const char *command) -{ -return trace_run_command (command, create_or_delete_synth_event); -} - -static int synth_event_run_cmd (struct dynevent_cmd *cmd) +static int synth_event_run_command (struct dynevent_cmd *cmd) {kernel/trace/trace_events_hist.c +1
-1struct synth_event *se;int ret;@@ -1787,7 +1782,7 @@ static int synth_event_run_cmd(struct dynevent_cmd *cmd)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); + synth_event_run_command); }
EXPORT_SYMBOL_GPL(synth_event_cmd_init);
Steven Rostedt
maintainerRe: [PATCH v4 06/12] tracing: Change trace_boot to use synth_event interface
Quoted 5 lines · On Fri, 31 Jan 2020 11:49:01 -0600 Tom Zanussi <[email protected]> wrote:
Hi Steve,
It looks like you missed this patch when you pulled the other ones into
for-next.
Ah, you're right! And my INBOX patchwork is informing me of it too (I have it read my email, and when I post the 'for-next' patches, it switches the status from "new" to "Under review", and this one is still "new").
Thanks for letting me know. I'll apply it now.
-- Steve
Steven Rostedt
maintainerRe: [PATCH v4 06/12] tracing: Change trace_boot to use synth_event interface
Quoted 9 lines · On Fri, 31 Jan 2020 12:55:39 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 5 lines · On Fri, 31 Jan 2020 11:49:01 -0600 Tom Zanussi <[email protected]> wrote:
Hi Steve,
It looks like you missed this patch when you pulled the other ones into
for-next.
Quoted 6 lines
Ah, you're right! And my INBOX patchwork is informing me of it too (I have it read my email, and when I post the 'for-next' patches, it switches the status from "new" to "Under review", and this one is still "new").
Thanks for letting me know. I'll apply it now.
My latest has been pushed to ftrace/core.
-- Steve
Re: [PATCH v4 06/12] tracing: Change trace_boot to use synth_event interface
Quoted 20 lines · Steven Rostedt
Quoted 17 lines · On Fri, 31 Jan 2020 12:55:39 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 6 lines · On Fri, 31 Jan 2020 11:49:01 -0600 Tom Zanussi <[email protected]> wrote:
Hi Steve,
It looks like you missed this patch when you pulled the other ones into
for-next.Ah, you're right! And my INBOX patchwork is informing me of it too (I
have it read my email, and when I post the 'for-next' patches, it switches the status from "new" to "Under review", and this one is still
"new").
Quoted 3 lines
Quoted 1 line
Thanks for letting me know. I'll apply it now.
My latest has been pushed to ftrace/core.
Thanks!
Tom
Quoted 2 lines
-- Steve
PATCH v4 7/12 tracing: Add synth_event_trace() and related functions
In reply to Tom ZanussiAdd an exported function named synth_event_trace(), allowing modules or other kernel code to trace synthetic events.
Also added are several functions that allow the same functionality to be broken out in a piecewise fashion, which are useful in situations where tracing an event from a full array of values would be cumbersome. Those functions are synth_event_trace_start/end() and synth_event_add_(next)_val().
--- include/linux/trace_events.h | 26 +++ kernel/trace/trace_events_hist.c | 463 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 489 insertions(+)
include/linux/trace_events.h +26 -0
@@ -416,6 +416,32 @@ extern int synth_event_add_fields(struct dynevent_cmd *cmd,#define synth_event_gen_cmd_end (cmd) \ dynevent_create(cmd) +struct synth_event ; + +struct synth_event_trace_state { +struct trace_event_buffer fbuffer; +struct synth_trace_event *entry; +struct trace_buffer *buffer; +struct synth_event *event; +unsigned int cur_field; +unsigned int n_u64; +bool enabled; +bool add_next; +bool add_name; +}; + +extern int synth_event_trace (struct trace_event_file *file, +unsigned int n_vals, ...); +extern int synth_event_trace_array (struct trace_event_file *file,u64 *vals, +unsigned int n_vals); +extern int synth_event_trace_start (struct trace_event_file *file, +struct synth_event_trace_state *trace_state); +extern int synth_event_add_next_val (u64 val, +struct synth_event_trace_state *trace_state); +extern int synth_event_add_val (const char *field_name,u64 val, +struct synth_event_trace_state *trace_state); +extern int synth_event_trace_end (struct synth_event_trace_state *trace_state); +/* * Event file flags: * ENABLED - The event is enabled
kernel/trace/trace_events_hist.c +463 -0
481 lines not on this page · raw message
Signature · 1 line
2.14.1
Steven Rostedt
maintainerRe: [PATCH v4 07/12] tracing: Add synth_event_trace() and related functions
Quoted 4 lines · On Wed, 29 Jan 2020 12:59:27 -0600 Tom Zanussi <[email protected]> wrote:
kernel/trace/trace_events_hist.c +4
-0+static struct synth_field *find_synth_field (struct synth_event *event, +const char *field_name) +{ +struct synth_field *field =NULL ;
kernel/trace/trace_events_hist.c void synth_event_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen) · line 2151
Quoted 10 lines
kernel/trace/trace_events_hist.c +10
-0+unsigned int i; + +for (i =0 ; i < event->n_fields; i++) { + field = event->fields[i]; +if (strcmp (field->name, field_name) ==0 ) +return field; + } + +return NULL ; +}
Why duplicate all theses functions and not use them in the synth_event_trace() directly?
-- Steve
Re: [PATCH v4 07/12] tracing: Add synth_event_trace() and related functions
Hi Steve,
Quoted 14 lines · Steven Rostedt
Quoted 11 lines · On Wed, 29 Jan 2020 12:59:27 -0600 Tom Zanussi <[email protected]> wrote:
+static struct
synth_field*find_synth_field(structsynth_event*event,(quoted diff) +1
-0+ const char
*field_name)kernel/trace/trace_events_hist.c +7
-0+{ +struct synth_field *field =NULL ; +unsigned int i; + +for (i =0 ; i < event->n_fields; i++) { + field = event->fields[i]; +if (strcmp (field->name, field_name) ==0 )
Quoted 9 lines
kernel/trace/trace_events_hist.c void synth_event_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen) · line 2151
Quoted 5 lines
kernel/trace/trace_events_hist.c +5
-0+return field; + } + +return NULL ; +}Why duplicate all theses functions and not use them in the
synth_event_trace()directly?
Yes, find_synth_field() is used only once and is short, so I can just add that into synth_event_add_val() directly. And looking at synth_event_add_val() and synth_event_add_next_val(), they're almost identical and so can be made into a single function with a param for the different parts (but still need to be exported separately so they can be used with the piecewise API).
It would also be possible to have synth_event_trace() and synth_event_trace_array() use synth_event_add_next_val() instead of writing the fields directly but that would be more overhead for those functions, which is why I avoided doing that.
Let me know if it's something else you're referring to, or if you want me to do a v5 or a follow-on patch to do the first part above.
Thanks,
Tom
Quoted 1 line
-- Steve
Steven Rostedt
maintainerRe: [PATCH v4 07/12] tracing: Add synth_event_trace() and related functions
Quoted 3 lines · On Wed, 29 Jan 2020 16:58:44 -0600 Tom Zanussi <[email protected]> wrote:
Let me know if it's something else you're referring to, or if you want me to do a v5 or a follow-on patch to do the first part above.
At this point, please send a follow-on patch.
Thanks!
-- Steve
PATCH v4 8/12 tracing: Add synth event generation test module
In reply to Tom ZanussiAdd a test module that checks the basic functionality of the in-kernel synthetic event generation API by generating and tracing synthetic events from a module.
--- kernel/trace/Kconfig | 13 + kernel/trace/Makefile | 1 + kernel/trace/synth_event_gen_test.c | 523 ++++++++++++++++++++++++++++++++++++ 3 files changed, 537 insertions(+) create mode 100644 kernel/trace/synth_event_gen_test.c
kernel/trace/Kconfig +13 -0
@@ -774,6 +774,19 @@ config PREEMPTIRQ_DELAY_TEST If unsure, say N +config SYNTH_EVENT_GEN_TEST + tristate "Test module for in-kernel synthetic event generation" + depends on HIST_TRIGGERS + help + This option creates a test module to check the base + functionality of in-kernel synthetic event definition and + generation. + + To test, insert the module, and then check the trace buffer + for the generated sample events. + + If unsure, say N. + config TRACE_EVAL_MAP_FILE bool "Show eval mappings for trace events" depends on TRACING
kernel/trace/Makefile +1 -0
@@ -44,6 +44,7 @@ obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_TRACING_MAP) += tracing_map.o obj-$(CONFIG_PREEMPTIRQ_DELAY_TEST) += preemptirq_delay_test.o +obj-$(CONFIG_SYNTH_EVENT_GEN_TEST) += synth_event_gen_test.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o obj-$(CONFIG_PREEMPTIRQ_TRACEPOINTS) += trace_preemptirq.o
kernel/trace/synth_event_gen_test.c new file +523 -0
523 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 9/12 tracing: Add kprobe event command generation functions
In reply to Tom ZanussiAdd functions used to generate kprobe event commands, built on top of the dynevent_cmd interface.
kprobe_event_gen_cmd_start() is used to create a kprobe event command using a variable arg list, and kretprobe_event_gen_cmd_start() does the same for kretprobe event commands. kprobe_event_add_fields() can be used to add single fields one by one or as a group. Once all desired fields are added, kprobe_event_gen_cmd_end() or kretprobe_event_gen_cmd_end() respectively are used to actually execute the command and create the event.
--- include/linux/trace_events.h | 31 +++++++++ kernel/trace/trace_kprobe.c | 161 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+)
include/linux/trace_events.h +31 -0
@@ -358,6 +358,7 @@ extern void trace_put_event_file(struct trace_event_file *file);enum dynevent_type {DYNEVENT_TYPE_SYNTH =1 , +DYNEVENT_TYPE_KPROBE ,DYNEVENT_TYPE_NONE , };
@@ -442,6 +443,36 @@ extern int synth_event_add_val(const char *field_name, u64 val, struct synth_event_trace_state *trace_state);extern int synth_event_trace_end (struct synth_event_trace_state *trace_state); +extern int kprobe_event_delete (const char *name); + +extern void kprobe_event_cmd_init (struct dynevent_cmd *cmd, +char *buf,int maxlen); + +#define kprobe_event_gen_cmd_start (cmd, name, loc, ...) \ + __kprobe_event_gen_cmd_start(cmd, false, name, loc, ## __VA_ARGS__, NULL) + +#define kretprobe_event_gen_cmd_start (cmd, name, loc, ...) \ + __kprobe_event_gen_cmd_start(cmd, true, name, loc, ## __VA_ARGS__, NULL) + +extern int __kprobe_event_gen_cmd_start (struct dynevent_cmd *cmd, +bool kretprobe, +const char *name, +const char *loc, ...); + +#define kprobe_event_add_fields (cmd, ...) \ + __kprobe_event_add_fields(cmd, ## __VA_ARGS__, NULL) + +#define kprobe_event_add_field (cmd, field) \ + __kprobe_event_add_fields(cmd, field, NULL) + +extern int __kprobe_event_add_fields (struct dynevent_cmd *cmd, ...); + +#define kprobe_event_gen_cmd_end (cmd) \ + dynevent_create(cmd) + +#define kretprobe_event_gen_cmd_end (cmd) \ + dynevent_create(cmd) +/* * Event file flags: * ENABLED - The event is enabled
kernel/trace/trace_kprobe.c +161 -0
167 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 10/12 tracing: Change trace_boot to use kprobe_event interface
In reply to Tom ZanussiHave trace_boot_add_kprobe_event() use the kprobe_event interface.
Also, rename kprobe_event_run_cmd() to kprobe_event_run_command() now that trace_boot's version is gone.
--- kernel/trace/trace_boot.c | 35 +++++++++++++++-------------------- kernel/trace/trace_kprobe.c | 9 ++------- 2 files changed, 17 insertions(+), 27 deletions(-)
kernel/trace/trace_boot.c +15 -20
@@ -91,37 +91,32 @@ trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node) }#ifdef CONFIG_KPROBE_EVENTS -extern int trace_kprobe_run_command (const char *command); -static int __init trace_boot_add_kprobe_event (struct xbc_node *node,const char *event) { +struct dynevent_cmd cmd;struct xbc_node *anode;char buf[MAX_BUF_LEN ];const char *val; -char *p; -int len; +int ret; - len =snprintf (buf,ARRAY_SIZE (buf) -1 ,"p:kprobes/%s " , event); -if (len >=ARRAY_SIZE (buf)) { -pr_err ("Event name is too long: %s\n" , event); -return -E2BIG ; - } - p = buf + len; - len =ARRAY_SIZE (buf) - len; +kprobe_event_cmd_init (&cmd, buf,MAX_BUF_LEN ); + + ret =kprobe_event_gen_cmd_start (&cmd, event,NULL ); +if (ret) +return ret;xbc_node_for_each_array_value (node,"probes" , anode, val) { -if (strlcpy (p, val, len) >= len) { -pr_err ("Probe definition is too long: %s\n" , val); -return -E2BIG ; - } -if (trace_kprobe_run_command (buf) <0 ) { -pr_err ("Failed to add probe: %s\n" , buf); -return -EINVAL ; - } + ret =kprobe_event_add_field (&cmd, val); +if (ret) +return ret; } -return 0 ; + ret =kprobe_event_gen_cmd_end (&cmd); +if (ret) +pr_err ("Failed to add probe: %s\n" , buf); + +return ret; }#else static inline int __init
kernel/trace/trace_kprobe.c +2 -7
@@ -901,12 +901,7 @@ static int create_or_delete_trace_kprobe(int argc, char **argv)return ret == -ECANCELED ? -EINVAL : ret; } -int trace_kprobe_run_command (const char *command) -{ -return trace_run_command (command, create_or_delete_trace_kprobe); -} - -static int trace_kprobe_run_cmd (struct dynevent_cmd *cmd) +static int trace_kprobe_run_command (struct dynevent_cmd *cmd) {return trace_run_command (cmd->buf, create_or_delete_trace_kprobe); }
@@ -923,7 +918,7 @@ static int trace_kprobe_run_cmd(struct dynevent_cmd *cmd)void kprobe_event_cmd_init (struct dynevent_cmd *cmd,char *buf,int maxlen) {dynevent_cmd_init (cmd, buf, maxlen,DYNEVENT_TYPE_KPROBE , - trace_kprobe_run_cmd); + trace_kprobe_run_command); }EXPORT_SYMBOL_GPL (kprobe_event_cmd_init);
Signature · 1 line
2.14.1
PATCH v4 11/12 tracing: Add kprobe event command generation test module
In reply to Tom ZanussiAdd a test module that checks the basic functionality of the in-kernel kprobe event command generation API by creating kprobe events from a module.
--- kernel/trace/Kconfig | 12 ++ kernel/trace/Makefile | 1 + kernel/trace/kprobe_event_gen_test.c | 225 +++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 kernel/trace/kprobe_event_gen_test.c
kernel/trace/Kconfig +12 -0
@@ -787,6 +787,18 @@ config SYNTH_EVENT_GEN_TEST If unsure, say N. +config KPROBE_EVENT_GEN_TEST + tristate "Test module for in-kernel kprobe event generation" + depends on KPROBE_EVENTS + help + This option creates a test module to check the base + functionality of in-kernel kprobe event definition. + + To test, insert the module, and then check the trace buffer + for the generated kprobe events. + + If unsure, say N. + config TRACE_EVAL_MAP_FILE bool "Show eval mappings for trace events" depends on TRACING
kernel/trace/Makefile +1 -0
@@ -45,6 +45,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_TRACING_MAP) += tracing_map.o obj-$(CONFIG_PREEMPTIRQ_DELAY_TEST) += preemptirq_delay_test.o obj-$(CONFIG_SYNTH_EVENT_GEN_TEST) += synth_event_gen_test.o +obj-$(CONFIG_KPROBE_EVENT_GEN_TEST) += kprobe_event_gen_test.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o obj-$(CONFIG_PREEMPTIRQ_TRACEPOINTS) += trace_preemptirq.o
kernel/trace/kprobe_event_gen_test.c new file +225 -0
225 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH v4 12/12 tracing: Documentation for in-kernel synthetic event API
In reply to Tom ZanussiAdd Documentation for creating and generating synthetic events from modules.
--- Documentation/trace/events.rst | 515 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+)
Documentation/trace/events.rst +515 -0
@@ -525,3 +525,518 @@ The following commands are supported: event counts (hitcount). See Documentation/trace/histogram.rst for details and examples. + +6.3 In-kernel trace event API +----------------------------- + +In most cases, the command-line interface to trace events is more than +sufficient. Sometimes, however, applications might find the need for +more complex relationships than can be expressed through a simple +series of linked command-line expressions, or putting together sets of +commands may be simply too cumbersome. An example might be an +application that needs to 'listen' to the trace stream in order to +maintain an in-kernel state machine detecting, for instance, when an +illegal kernel state occurs in the scheduler. + +The trace event subsystem provides an in-kernel API allowing modules +or other kernel code to generate user-defined 'synthetic' events at +will, which can be used to either augment the existing trace stream +and/or signal that a particular important state has occurred. + +A similar in-kernel API is also available for creating kprobe and +kretprobe events. + +Both the synthetic event and k/ret/probe event APIs are built on top +of a lower-level "dynevent_cmd" event command API, which is also +available for more specialized applications, or as the basis of other +higher-level trace event APIs. + +The API provided for these purposes is describe below and allows the +following: + + - dynamically creating synthetic event definitions + - dynamically creating kprobe and kretprobe event definitions + - tracing synthetic events from in-kernel code + - the low-level "dynevent_cmd" API + +6.3.1 Dyamically creating synthetic event definitions +----------------------------------------------------- + +There are a couple ways to create a new synthetic event from a kernel +module or other kernel code. + +The first creates the event in one step, using synth_event_create(). +In this method, the name of the event to create and an array defining +the fields is supplied to synth_event_create(). If successful, a +synthetic event with that name and fields will exist following that +call. For example, to create a new "schedtest" synthetic event: + + ret = synth_event_create("schedtest", sched_fields, + ARRAY_SIZE(sched_fields), THIS_MODULE); + +The sched_fields param in this example points to an array of struct +synth_field_desc, each of which describes an event field by type and +name: + + static struct synth_field_desc sched_fields[] = { + { .type = "pid_t", .name = "next_pid_field" }, + { .type = "char[16]", .name = "next_comm_field" }, + { .type = "u64", .name = "ts_ns" }, + { .type = "u64", .name = "ts_ms" }, + { .type = "unsigned int", .name = "cpu" }, + { .type = "char[64]", .name = "my_string_field" }, + { .type = "int", .name = "my_int_field" }, + }; + +See synth_field_size() for available types. If field_name contains [n] +the field is considered to be an array. + +If the event is created from within a module, a pointer to the module +must be passed to synth_event_create(). This will ensure that the +trace buffer won't contain unreadable events when the module is +removed. + +At this point, the event object is ready to be used for generating new +events. + +In the second method, the event is created in several steps. This +allows events to be created dynamically and without the need to create +and populate an array of fields beforehand. + +To use this method, an empty or partially empty synthetic event should +first be created using synth_event_gen_cmd_start() or +synth_event_gen_cmd_array_start(). For synth_event_gen_cmd_start(), +the name of the event along with one or more pairs of args each pair +representing a 'type field_name;' field specification should be +supplied. For synth_event_gen_cmd_array_start(), the name of the +event along with an array of struct synth_field_desc should be +supplied. Before calling synth_event_gen_cmd_start() or +synth_event_gen_cmd_array_start(), the user should create and +initialize a dynevent_cmd object using synth_event_cmd_init(). + +For example, to create a new "schedtest" synthetic event with two +fields: + + struct dynevent_cmd cmd; + char *buf; + + /* Create a buffer to hold the generated command */ + buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); + + /* Before generating the command, initialize the cmd object */ + synth_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); + + ret = synth_event_gen_cmd_start(&cmd, "schedtest", THIS_MODULE, + "pid_t", "next_pid_field", + "u64", "ts_ns"); + +Alternatively, using an array of struct synth_field_desc fields +containing the same information: + + ret = synth_event_gen_cmd_array_start(&cmd, "schedtest", THIS_MODULE, + fields, n_fields); + +Once the synthetic event object has been created, it can then be +populated with more fields. Fields are added one by one using +synth_event_add_field(), supplying the dynevent_cmd object, a field +type, and a field name. For example, to add a new int field named +"intfield", the following call should be made: + + ret = synth_event_add_field(&cmd, "int", "intfield"); + +See synth_field_size() for available types. If field_name contains [n] +the field is considered to be an array. + +A group of fields can also be added all at once using an array of +synth_field_desc with add_synth_fields(). For example, this would add +just the first four sched_fields: + + ret = synth_event_add_fields(&cmd, sched_fields, 4); + +If you already have a string of the form 'type field_name', +synth_event_add_field_str() can be used to add it as-is; it will +also automatically append a ';' to the string. + +Once all the fields have been added, the event should be finalized and +registered by calling the synth_event_gen_cmd_end() function: + + ret = synth_event_gen_cmd_end(&cmd); + +At this point, the event object is ready to be used for tracing new +events. + +6.3.3 Tracing synthetic events from in-kernel code +-------------------------------------------------- + +To trace a synthetic event, there are several options. The first +option is to trace the event in one call, using synth_event_trace() +with a variable number of values, or synth_event_trace_array() with an +array of values to be set. A second option can be used to avoid the +need for a pre-formed array of values or list of arguments, via +synth_event_trace_start() and synth_event_trace_end() along with +synth_event_add_next_val() or synth_event_add_val() to add the values +piecewise. + +6.3.3.1 Tracing a synthetic event all at once +--------------------------------------------- + +To trace a synthetic event all at once, the synth_event_trace() or +synth_event_trace_array() functions can be used. + +The synth_event_trace() function is passed the trace_event_file +representing the synthetic event (which can be retrieved using +trace_get_event_file() using the synthetic event name, "synthetic" as +the system name, and the trace instance name (NULL if using the global +trace array)), along with an variable number of u64 args, one for each +synthetic event field, and the number of values being passed. + +So, to trace an event corresponding to the synthetic event definition +above, code like the following could be used: + + ret = synth_event_trace(create_synth_test, 7, /* number of values */ + 444, /* next_pid_field */ + (u64)"clackers", /* next_comm_field */ + 1000000, /* ts_ns */ + 1000, /* ts_ms */ + smp_processor_id(),/* cpu */ + (u64)"Thneed", /* my_string_field */ + 999); /* my_int_field */ + +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. + +Alternatively, the synth_event_trace_array() function can be used to +accomplish the same thing. It is passed the trace_event_file +representing the synthetic event (which can be retrieved using +trace_get_event_file() using the synthetic event name, "synthetic" as +the system name, and the trace instance name (NULL if using the global +trace array)), along with an array of u64, one for each synthetic +event field. + +To trace an event corresponding to the synthetic event definition +above, code like the following could be used: + + u64 vals[7]; + + vals[0] = 777; /* next_pid_field */ + vals[1] = (u64)"tiddlywinks"; /* next_comm_field */ + vals[2] = 1000000; /* ts_ns */ + vals[3] = 1000; /* ts_ms */ + vals[4] = smp_processor_id(); /* cpu */ + vals[5] = (u64)"thneed"; /* my_string_field */ + vals[6] = 398; /* my_int_field */ + +The 'vals' array is just an array of u64, the number of which must +match the number of field in the synthetic event, and which 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. + +In order to trace a synthetic event, a pointer to the trace event file +is needed. The trace_get_event_file() function can be used to get +it - it will find the file in the given trace instance (in this case +NULL since the top trace array is being used) while at the same time +preventing the instance containing it from going away: + + schedtest_event_file = trace_get_event_file(NULL, "synthetic", + "schedtest"); + +Before tracing the event, it should be enabled in some way, otherwise +the synthetic event won't actually show up in the trace buffer. + +To enable a synthetic event from the kernel, trace_array_set_clr_event() +can be used (which is not specific to synthetic events, so does need +the "synthetic" system name to be specified explicitly). + +To enable the event, pass 'true' to it: + + trace_array_set_clr_event(schedtest_event_file->tr, + "synthetic", "schedtest", true); + +To disable it pass false: + + trace_array_set_clr_event(schedtest_event_file->tr, + "synthetic", "schedtest", false); + +Finally, synth_event_trace_array() can be used to actually trace the +event, which should be visible in the trace buffer afterwards: + + ret = synth_event_trace_array(schedtest_event_file, vals, + ARRAY_SIZE(vals)); + +To remove the synthetic event, the event should be disabled, and the +trace instance should be 'put' back using trace_put_event_file(): + + trace_array_set_clr_event(schedtest_event_file->tr, + "synthetic", "schedtest", false); + trace_put_event_file(schedtest_event_file); + +If those have been successful, synth_event_delete() can be called to +remove the event: + + ret = synth_event_delete("schedtest"); + +6.3.3.1 Tracing a synthetic event piecewise +------------------------------------------- + +To trace a synthetic using the piecewise method described above, the +synth_event_trace_start() function is used to 'open' the synthetic +event trace: + + struct synth_trace_state trace_state; + + ret = synth_event_trace_start(schedtest_event_file, &trace_state); + +It's passed the trace_event_file representing the synthetic event +using the same methods as described above, along with a pointer to a +struct synth_trace_state object, which will be zeroed before use and +used to maintain state between this and following calls. + +Once the event has been opened, which means space for it has been +reserved in the trace buffer, the individual fields can be set. There +are two ways to do that, either one after another for each field in +the event, which requires no lookups, or by name, which does. The +tradeoff is flexibility in doing the assignments vs the cost of a +lookup per field. + +To assign the values one after the other without lookups, +synth_event_add_next_val() should be used. Each call is passed the +same synth_trace_state object used in the synth_event_trace_start(), +along with the value to set the next field in the event. After each +field is set, the 'cursor' points to the next field, which will be set +by the subsequent call, continuing until all the fields have been set +in order. The same sequence of calls as in the above examples using +this method would be (without error-handling code): + + /* next_pid_field */ + ret = synth_event_add_next_val(777, &trace_state); + + /* next_comm_field */ + ret = synth_event_add_next_val((u64)"slinky", &trace_state); + + /* ts_ns */ + ret = synth_event_add_next_val(1000000, &trace_state); + + /* ts_ms */ + ret = synth_event_add_next_val(1000, &trace_state); + + /* cpu */ + ret = synth_event_add_next_val(smp_processor_id(), &trace_state); + + /* my_string_field */ + ret = synth_event_add_next_val((u64)"thneed_2.01", &trace_state); + + /* my_int_field */ + ret = synth_event_add_next_val(395, &trace_state); + +To assign the values in any order, synth_event_add_val() should be +used. Each call is passed the same synth_trace_state object used in +the synth_event_trace_start(), along with the field name of the field +to set and the value to set it to. The same sequence of calls as in +the above examples using this method would be (without error-handling +code): + + ret = synth_event_add_val("next_pid_field", 777, &trace_state); + ret = synth_event_add_val("next_comm_field", (u64)"silly putty", + &trace_state); + ret = synth_event_add_val("ts_ns", 1000000, &trace_state); + ret = synth_event_add_val("ts_ms", 1000, &trace_state); + ret = synth_event_add_val("cpu", smp_processor_id(), &trace_state); + ret = synth_event_add_val("my_string_field", (u64)"thneed_9", + &trace_state); + ret = synth_event_add_val("my_int_field", 3999, &trace_state); + +Note that synth_event_add_next_val() and synth_event_add_val() are +incompatible if used within the same trace of an event - either one +can be used but not both at the same time. + +Finally, the event won't be actually traced until it's 'closed', +which is done using synth_event_trace_end(), which takes only the +struct synth_trace_state object used in the previous calls: + + ret = synth_event_trace_end(&trace_state); + +Note that synth_event_trace_end() must be called at the end regardless +of whether any of the add calls failed (say due to a bad field name +being passed in). + +6.3.4 Dyamically creating kprobe and kretprobe event definitions +---------------------------------------------------------------- + +To create a kprobe or kretprobe trace event from kernel code, the +kprobe_event_gen_cmd_start() or kretprobe_event_gen_cmd_start() +functions can be used. + +To create a kprobe event, an empty or partially empty kprobe event +should first be created using kprobe_event_gen_cmd_start(). The name +of the event and the probe location should be specfied along with one +or args each representing a probe field should be supplied to this +function. Before calling kprobe_event_gen_cmd_start(), the user +should create and initialize a dynevent_cmd object using +kprobe_event_cmd_init(). + +For example, to create a new "schedtest" kprobe event with two fields: + + struct dynevent_cmd cmd; + char *buf; + + /* Create a buffer to hold the generated command */ + buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); + + /* Before generating the command, initialize the cmd object */ + kprobe_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); + + /* + * Define the gen_kprobe_test event with the first 2 kprobe + * fields. + */ + ret = kprobe_event_gen_cmd_start(&cmd, "gen_kprobe_test", "do_sys_open", + "dfd=%ax", "filename=%dx"); + +Once the kprobe event object has been created, it can then be +populated with more fields. Fields can be added using +kprobe_event_add_fields(), supplying the dynevent_cmd object along +with a variable arg list of probe fields. For example, to add a +couple additional fields, the following call could be made: + + ret = kprobe_event_add_fields(&cmd, "flags=%cx", "mode=+4($stack)"); + +Once all the fields have been added, the event should be finalized and +registered by calling the kprobe_event_gen_cmd_end() or +kretprobe_event_gen_cmd_end() functions, depending on whether a kprobe +or kretprobe command was started: + + ret = kprobe_event_gen_cmd_end(&cmd); + +or + + ret = kretprobe_event_gen_cmd_end(&cmd); + +At this point, the event object is ready to be used for tracing new +events. + +Similarly, a kretprobe event can be created using +kretprobe_event_gen_cmd_start() with a probe name and location and +additional params such as $retval: + + ret = kretprobe_event_gen_cmd_start(&cmd, "gen_kretprobe_test", + "do_sys_open", "$retval"); + +Similar to the synthetic event case, code like the following can be +used to enable the newly created kprobe event: + + gen_kprobe_test = trace_get_event_file(NULL, "kprobes", "gen_kprobe_test"); + + ret = trace_array_set_clr_event(gen_kprobe_test->tr, + "kprobes", "gen_kprobe_test", true); + +Finally, also similar to synthetic events, the following code can be +used to give the kprobe event file back and delete the event: + + trace_put_event_file(gen_kprobe_test); + + ret = kprobe_event_delete("gen_kprobe_test"); + +6.3.4 The "dynevent_cmd" low-level API +-------------------------------------- + +Both the in-kernel synthetic event and kprobe interfaces are built on +top of a lower-level "dynevent_cmd" interface. This interface is +meant to provide the basis for higher-level interfaces such as the +synthetic and kprobe interfaces, which can be used as examples. + +The basic idea is simple and amounts to providing a general-purpose +layer that can be used to generate trace event commands. The +generated command strings can then be passed to the command-parsing +and event creation code that already exists in the trace event +subystem for creating the corresponding trace events. + +In a nutshell, the way it works is that the higher-level interface +code creates a struct dynevent_cmd object, then uses a couple +functions, dynevent_arg_add() and dynevent_arg_pair_add() to build up +a command string, which finally causes the command to be executed +using the dynevent_create() function. The details of the interface +are described below. + +The first step in building a new command string is to create and +initialize an instance of a dynevent_cmd. Here, for instance, we +create a dynevent_cmd on the stack and initialize it: + + struct dynevent_cmd cmd; + char *buf; + int ret; + + buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); + + dynevent_cmd_init(cmd, buf, maxlen, DYNEVENT_TYPE_FOO, + foo_event_run_command); + +The dynevent_cmd initialization needs to be given a user-specified +buffer and the length of the buffer (MAX_DYNEVENT_CMD_LEN can be used +for this purpose - at 2k it's generally too big to be comfortably put +on the stack, so is dynamically allocated), a dynevent type id, which +is meant to be used to check that further API calls are for the +correct command type, and a pointer to an event-specific run_command() +callback that will be called to actually execute the event-specific +command function. + +Once that's done, the command string can by built up by successive +calls to argument-adding functions. + +To add a single argument, define and initialize a struct dynevent_arg +or struct dynevent_arg_pair object. Here's an example of the simplest +possible arg addition, which is simply to append the given string as +a whitespace-separated argument to the command: + + struct dynevent_arg arg; + + dynevent_arg_init(&arg, NULL, 0); + + arg.str = name; + + ret = dynevent_arg_add(cmd, &arg); + +The arg object is first initialized using dynevent_arg_init() and in +this case the parameters are NULL or 0, which means there's no +optional sanity-checking function or separator appended to the end of +the arg. + +Here's another more complicated example using an 'arg pair', which is +used to create an argument that consists of a couple components added +together as a unit, for example, a 'type field_name;' arg or a simple +expression arg e.g. 'flags=%cx': + + struct dynevent_arg_pair arg_pair; + + dynevent_arg_pair_init(&arg_pair, dynevent_foo_check_arg_fn, 0, ';'); + + arg_pair.lhs = type; + arg_pair.rhs = name; + + ret = dynevent_arg_pair_add(cmd, &arg_pair); + +Again, the arg_pair is first initialized, in this case with a callback +function used to check the sanity of the args (for example, that +neither part of the pair is NULL), along with a character to be used +to add an operator between the pair (here none) and a separator to be +appended onto the end of the arg pair (here ';'). + +There's also a dynevent_str_add() function that can be used to simply +add a string as-is, with no spaces, delimeters, or arg check. + +Any number of dynevent_*_add() calls can be made to build up the string +(until its length surpasses cmd->maxlen). When all the arguments have +been added and the command string is complete, the only thing left to +do is run the command, which happens by simply calling +dynevent_create(): + + ret = dynevent_create(&cmd); + +At that point, if the return value is 0, the dynamic event has been +created and is ready to use. + +See the dynevent_cmd function definitions themselves for the details +of the API.
Signature · 1 line
2.14.1