282 lines
9.0 KiB
Plaintext
282 lines
9.0 KiB
Plaintext
|
|
libtracefs(3)
|
||
|
|
=============
|
||
|
|
|
||
|
|
NAME
|
||
|
|
----
|
||
|
|
tracefs_synth_create, tracefs_synth_destroy, tracefs_synth_complete,
|
||
|
|
tracefs_synth_trace, tracefs_synth_snapshot, tracefs_synth_save
|
||
|
|
- Creation of synthetic events
|
||
|
|
|
||
|
|
SYNOPSIS
|
||
|
|
--------
|
||
|
|
[verse]
|
||
|
|
--
|
||
|
|
*#include <tracefs.h>*
|
||
|
|
|
||
|
|
int *tracefs_synth_create*(struct tracefs_synth pass:[*]_synth_);
|
||
|
|
int *tracefs_synth_destroy*(struct tracefs_synth pass:[*]_synth_);
|
||
|
|
bool *tracefs_synth_complete*(struct tracefs_synth pass:[*]_synth_);
|
||
|
|
|
||
|
|
int *tracefs_synth_trace*(struct tracefs_synth pass:[*]_synth_,
|
||
|
|
enum tracefs_synth_handler _type_, const char pass:[*]_var_);
|
||
|
|
int *tracefs_synth_snapshot*(struct tracefs_synth pass:[*]_synth_,
|
||
|
|
enum tracefs_synth_handler _type_, const char pass:[*]_var_);
|
||
|
|
int *tracefs_synth_save*(struct tracefs_synth pass:[*]_synth_,
|
||
|
|
enum tracefs_synth_handler _type_, const char pass:[*]_var_,
|
||
|
|
char pass:[**]_save_fields_);
|
||
|
|
--
|
||
|
|
|
||
|
|
DESCRIPTION
|
||
|
|
-----------
|
||
|
|
Synthetic events are dynamic events that are created by matching
|
||
|
|
two other events which triggers a synthetic event. One event is the starting
|
||
|
|
event which some field is recorded, and when the second event is executed,
|
||
|
|
if it has a field (or fields) that matches the starting event's field (or fields)
|
||
|
|
then it will trigger the synthetic event. The field values other than the matching
|
||
|
|
fields may be passed from the starting event to the end event to perform calculations
|
||
|
|
on, or to simply pass as a parameter to the synthetic event.
|
||
|
|
|
||
|
|
One common use case is to set "sched_waking" as the starting event. This event is
|
||
|
|
triggered when a process is awoken. Then set "sched_switch" as the ending event.
|
||
|
|
This event is triggered when a new task is scheduled on the CPU. By setting
|
||
|
|
the "common_pid" of both events as the matching fields, the time between the
|
||
|
|
two events is considered the wake up latency of that process. Use *TRACEFS_TIMESTAMP*
|
||
|
|
as a field for both events to calculate the delta in nanoseconds, or use
|
||
|
|
*TRACEFS_TIMESTAMP_USECS* as the compare fields for both events to calculate the
|
||
|
|
delta in microseconds. This is used as the example below.
|
||
|
|
|
||
|
|
*tracefs_synth_create*() creates the synthetic event in the system. The synthetic events apply
|
||
|
|
across all instances. A synthetic event must be created with *tracefs_synth_alloc*(3) before
|
||
|
|
it can be created.
|
||
|
|
|
||
|
|
*tracefs_synth_destroy*() destroys the synthetic event. It will attempt to stop the running of it in
|
||
|
|
its instance (top by default), but if its running in another instance this may fail as busy.
|
||
|
|
|
||
|
|
*tracefs_synth_complete*() returns true if the synthetic event _synth_ has both
|
||
|
|
a starting and ending event.
|
||
|
|
|
||
|
|
*tracefs_synth_trace*() Instead of doing just a trace on matching of the start and
|
||
|
|
end events, do the _type_ handler where *TRACEFS_SYNTH_HANDLE_MAX* will do a trace
|
||
|
|
when the given variable _var_ hits a new max for the matching keys. Or
|
||
|
|
*TRACEFS_SYNTH_HANDLE_CHANGE* for when the _var_ changes. _var_ must be one of
|
||
|
|
the _name_ elements used in *tracefs_synth_add_end_field*(3).
|
||
|
|
|
||
|
|
*tracefs_synth_snapshot*() When the given variable _var_ is either a new max if
|
||
|
|
_handler_ is *TRACEFS_SYNTH_HANDLE_MAX* or simply changed if *TRACEFS_SYNTH_HANDLE_CHANGE*
|
||
|
|
then take a "snapshot" of the buffer. The snapshot moves the normal "trace" buffer
|
||
|
|
into a "snapshot" buffer, that can be accessed via the "snapshot" file in the
|
||
|
|
top level tracefs directory, or one of the instances. _var_ changes. _var_ must be one of
|
||
|
|
the _name_ elements used in *tracefs_synth_add_end_field*(3).
|
||
|
|
|
||
|
|
*tracefs_synth_save*() When the given variable _var_ is either a new max if
|
||
|
|
_handler_ is *TRACEFS_SYNTH_HANDLE_MAX* or simpy changed if *TRACEFS_SYNTH_HANDLE_CHANGE*
|
||
|
|
then save the given _save_fields_ list. The fields will be stored in the histogram
|
||
|
|
"hist" file of the event that can be retrieved with *tracefs_event_file_read*(3).
|
||
|
|
_var_ must be one of the _name_ elements used in *tracefs_synth_add_end_field*(3).
|
||
|
|
|
||
|
|
RETURN VALUE
|
||
|
|
------------
|
||
|
|
All functions return zero on success or -1 on error.
|
||
|
|
|
||
|
|
ERRORS
|
||
|
|
------
|
||
|
|
The following errors are for all the above calls:
|
||
|
|
|
||
|
|
*EPERM* Not run as root user when required.
|
||
|
|
|
||
|
|
*EINVAL* Either a parameter is not valid (NULL when it should not be)
|
||
|
|
or a field that is not compatible for calculations.
|
||
|
|
|
||
|
|
*ENODEV* An event or one of its fields is not found.
|
||
|
|
|
||
|
|
*EBADE* The fields of the start and end events are not compatible for
|
||
|
|
either matching or comparing.
|
||
|
|
|
||
|
|
*ENOMEM* not enough memory is available.
|
||
|
|
|
||
|
|
And more errors may have happened from the system calls to the system.
|
||
|
|
|
||
|
|
EXAMPLE
|
||
|
|
-------
|
||
|
|
See *tracefs_sql*(3) for a more indepth use of some of this code.
|
||
|
|
|
||
|
|
[source,c]
|
||
|
|
--
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <tracefs.h>
|
||
|
|
|
||
|
|
#define start_event "sched_waking"
|
||
|
|
#define start_field "pid"
|
||
|
|
|
||
|
|
#define end_event "sched_switch"
|
||
|
|
#define end_field "next_pid"
|
||
|
|
|
||
|
|
#define match_name "pid"
|
||
|
|
|
||
|
|
static struct tracefs_synth *synth;
|
||
|
|
|
||
|
|
static void make_event(void)
|
||
|
|
{
|
||
|
|
struct tep_handle *tep;
|
||
|
|
|
||
|
|
/* Load all events from the system */
|
||
|
|
tep = tracefs_local_events(NULL);
|
||
|
|
|
||
|
|
/* Initialize the synthetic event */
|
||
|
|
synth = tracefs_synth_alloc(tep, "wakeup_lat",
|
||
|
|
NULL, start_event,
|
||
|
|
NULL, end_event,
|
||
|
|
start_field, end_field,
|
||
|
|
match_name);
|
||
|
|
|
||
|
|
/* The tep is no longer needed */
|
||
|
|
tep_free(tep);
|
||
|
|
|
||
|
|
|
||
|
|
/* Save the "prio" field as "prio" from the start event */
|
||
|
|
tracefs_synth_add_start_field(synth, "prio", NULL);
|
||
|
|
|
||
|
|
/* Save the "next_comm" as "comm" from the end event */
|
||
|
|
tracefs_synth_add_end_field(synth, "next_comm", "comm");
|
||
|
|
|
||
|
|
/* Save the "prev_prio" as "prev_prio" from the end event */
|
||
|
|
tracefs_synth_add_end_field(synth, "prev_prio", NULL);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Take a microsecond time difference between end and start
|
||
|
|
* and record as "delta"
|
||
|
|
*/
|
||
|
|
tracefs_synth_add_compare_field(synth, TRACEFS_TIMESTAMP_USECS,
|
||
|
|
TRACEFS_TIMESTAMP_USECS,
|
||
|
|
TRACEFS_SYNTH_DELTA_END, "delta");
|
||
|
|
|
||
|
|
/* Only record if start event "prio" is less than 100 */
|
||
|
|
tracefs_synth_append_start_filter(synth, TRACEFS_FILTER_COMPARE,
|
||
|
|
"prio", TRACEFS_COMPARE_LT, "100");
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Only record if end event "next_prio" is less than 50
|
||
|
|
* or the previous task's prio was not greater than or equal to 100.
|
||
|
|
* next_prio < 50 || !(prev_prio >= 100)
|
||
|
|
*/
|
||
|
|
tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
|
||
|
|
"next_prio", TRACEFS_COMPARE_LT, "50");
|
||
|
|
tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OR, NULL, 0, NULL);
|
||
|
|
tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_NOT, NULL, 0, NULL);
|
||
|
|
tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_OPEN_PAREN, NULL, 0, NULL);
|
||
|
|
tracefs_synth_append_end_filter(synth, TRACEFS_FILTER_COMPARE,
|
||
|
|
"prev_prio", TRACEFS_COMPARE_GE, "100");
|
||
|
|
/*
|
||
|
|
* Note, the above only added: "next_prio < 50 || !(prev_prio >= 100"
|
||
|
|
* That's because, when the synth is executed, the remaining close parenthesis
|
||
|
|
* will be added. That is, the string will end up being:
|
||
|
|
* "next_prio < 50 || !(prev_prio >= 100)" when one of tracefs_sync_create()
|
||
|
|
* or tracefs_sync_echo_cmd() is run.
|
||
|
|
*/
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Display how to create the synthetic event */
|
||
|
|
static void show_event(void)
|
||
|
|
{
|
||
|
|
struct trace_seq s;
|
||
|
|
|
||
|
|
trace_seq_init(&s);
|
||
|
|
|
||
|
|
tracefs_synth_echo_cmd(&s, synth);
|
||
|
|
trace_seq_terminate(&s);
|
||
|
|
trace_seq_do_printf(&s);
|
||
|
|
trace_seq_destroy(&s);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main (int argc, char **argv)
|
||
|
|
{
|
||
|
|
make_event();
|
||
|
|
|
||
|
|
if (argc > 1) {
|
||
|
|
if (!strcmp(argv[1], "create")) {
|
||
|
|
/* Create the synthetic event */
|
||
|
|
tracefs_synth_create(synth);
|
||
|
|
} else if (!strcmp(argv[1], "delete")) {
|
||
|
|
/* Delete the synthetic event */
|
||
|
|
tracefs_synth_destroy(synth);
|
||
|
|
} else {
|
||
|
|
printf("usage: %s [create|delete]\n", argv[0]);
|
||
|
|
exit(-1);
|
||
|
|
}
|
||
|
|
} else
|
||
|
|
show_event();
|
||
|
|
|
||
|
|
tracefs_synth_free(synth);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
--
|
||
|
|
|
||
|
|
FILES
|
||
|
|
-----
|
||
|
|
[verse]
|
||
|
|
--
|
||
|
|
*tracefs.h*
|
||
|
|
Header file to include in order to have access to the library APIs.
|
||
|
|
*-ltracefs*
|
||
|
|
Linker switch to add when building a program that uses the library.
|
||
|
|
--
|
||
|
|
|
||
|
|
SEE ALSO
|
||
|
|
--------
|
||
|
|
*libtracefs*(3),
|
||
|
|
*libtraceevent*(3),
|
||
|
|
*trace-cmd*(1),
|
||
|
|
*tracefs_hist_alloc*(3),
|
||
|
|
*tracefs_hist_alloc_2d*(3),
|
||
|
|
*tracefs_hist_alloc_nd*(3),
|
||
|
|
*tracefs_hist_free*(3),
|
||
|
|
*tracefs_hist_add_key*(3),
|
||
|
|
*tracefs_hist_add_value*(3),
|
||
|
|
*tracefs_hist_add_name*(3),
|
||
|
|
*tracefs_hist_start*(3),
|
||
|
|
*tracefs_hist_destory*(3),
|
||
|
|
*tracefs_hist_add_sort_key*(3),
|
||
|
|
*tracefs_hist_sort_key_direction*(3),
|
||
|
|
*tracefs_synth_alloc*(3),
|
||
|
|
*tracefs_synth_add_match_field*(3),
|
||
|
|
*tracefs_synth_add_compare_field*(3),
|
||
|
|
*tracefs_synth_add_start_field*(3),
|
||
|
|
*tracefs_synth_add_end_field*(3),
|
||
|
|
*tracefs_synth_append_start_filter*(3),
|
||
|
|
*tracefs_synth_append_end_filter*(3),
|
||
|
|
*tracefs_synth_free*(3),
|
||
|
|
*tracefs_synth_echo_cmd*(3),
|
||
|
|
*tracefs_synth_get_start_hist*(3),
|
||
|
|
*tracefs_synth_get_name*(3),
|
||
|
|
*tracefs_synth_raw_fmt*(3),
|
||
|
|
*tracefs_synth_show_event*(3),
|
||
|
|
*tracefs_synth_show_start_hist*(3),
|
||
|
|
*tracefs_synth_show_end_hist*(3),
|
||
|
|
*tracefs_synth_get_event*(3),
|
||
|
|
|
||
|
|
AUTHOR
|
||
|
|
------
|
||
|
|
[verse]
|
||
|
|
--
|
||
|
|
*Steven Rostedt* <rostedt@goodmis.org>
|
||
|
|
*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
|
||
|
|
*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
|
||
|
|
--
|
||
|
|
REPORTING BUGS
|
||
|
|
--------------
|
||
|
|
Report bugs to <linux-trace-devel@vger.kernel.org>
|
||
|
|
|
||
|
|
LICENSE
|
||
|
|
-------
|
||
|
|
libtracefs is Free Software licensed under the GNU LGPL 2.1
|
||
|
|
|
||
|
|
RESOURCES
|
||
|
|
---------
|
||
|
|
https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
|
||
|
|
|
||
|
|
COPYING
|
||
|
|
-------
|
||
|
|
Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
|
||
|
|
the terms of the GNU Public License (GPL).
|