LIBTRACEFS(3)
π NAME π
okay bestie, welcome to LIBTRACEFS(3) β the libtracefs manual page, and todayβs main-character energy is all about creation of a synthetic event descriptor. Weβre talking:
tracefs_synth_alloctracefs_synth_add_match_fieldtracefs_synth_add_compare_fieldtracefs_synth_add_start_fieldtracefs_synth_add_end_fieldtracefs_synth_append_start_filtertracefs_synth_append_end_filtertracefs_synth_free
β¨ SYNOPSIS β¨
#include <tracefs.h>
struct tracefs_synth *tracefs_synth_alloc(struct tep_handle *tep,
const char *name,
const char *start_system,
const char *start_event,
const char *end_system,
const char *end_event,
const char *start_match_field,
const char *end_match_field,
const char *match_name);
int tracefs_synth_add_match_field(struct tracefs_synth *synth,
const char *start_match_field,
const char *end_match_field,
const char *name);
int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
const char *start_compare_field,
const char *end_compare_field,
enum tracefs_synth_calc calc,
const char *name);
int tracefs_synth_add_start_field(struct tracefs_synth *synth,
const char *start_field,
const char *name);
int tracefs_synth_add_end_field(struct tracefs_synth *synth,
const char *end_field,
const char *name);
int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
struct tracefs_filter type,
const char *field,
enum tracefs_synth_compare compare,
const char *val);
int tracefs_synth_append_end_filter(struct tracefs_synth *synth,
struct tracefs_filter type,
const char *field,
enum tracefs_synth_compare compare,
const char *val);
void tracefs_synth_free(struct tracefs_synth *synth);
π¦ DESCRIPTION π¦
Synthetic events are dynamic events that get created by matching two other events, and that match is what triggers the synthetic event. One event is the starting event, and some field from it is recorded. Then when the second event runs, if it has a field (or fields) that matches the starting eventβs field (or fields), boom β the synthetic event gets triggered. The field values that arenβt the matching fields can be passed from the starting event to the ending event so you can do calculations on them, or just pass them as parameters to the synthetic event. π
A super common use case is setting "sched_waking" as the starting event. That event fires when a process is awoken. Then set "sched_switch" as the ending event. That one fires 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. Thatβs the example this page is referencing below. π
tracefs_synth_alloc() allocates and initializes a synthetic event. It does not create the synthetic event β it just supplies the minimal information to do so. See tracefs_synth_create(3) for how to create the synthetic event in the system. It requires a tep handler that can be created by tracefs_local_events(3) for more information. The name holds the name of the synthetic event that will be created. The start_system is the name of the system for the starting event. It may be NULL, and the first event with the name of start_event will be chosen. The end_system is the name of the system for the ending event. It may be NULL, and the first event with the name of end_event will be chosen as the ending event. If match_name is given, then this will be the field of the created synthetic event that holds the matching keys of the starting eventβs start_match_field and the ending eventβs end_match_field. If match_name is NULL, then it will not be recorded in the created synthetic event.
tracefs_synth_add_match_field() will add a second key to match between the starting event and the ending event. If name is given, then the content of the matching field will be saved by this name in the synthetic event. The start_match_field is the field of the starting event to match with the ending eventβs end_match_field. π
tracefs_synth_add_compare_field() is used to compare the start_compare_field of the starting event with the end_compare_field of the ending event. The name must be given so that the result will be saved by the synthetic event. It makes no sense to not pass this to the synthetic event after doing the work of the compared fields, as it serves no other purpose. The calc parameter can be one of:
TRACEFS_SYNTH_DELTA_ENDβ calculate the difference between the content in theend_compare_fieldfrom the content of thestart_compare_field.
name = end_compare_field - start_compare_field
TRACEFS_SYNTH_DELTA_STARTβ calculate the difference between the content in thestart_compare_fieldfrom the content of theend_compare_field.
name = start_compare_field - end_compare_field
TRACEFS_SYNTH_ADDβ Add the content of thestart_compare_fieldto the content of theend_compare_field.
name = start_compare_field + end_compare_field
tracefs_synth_add_start_field() records the start_field of the starting event as name in the synthetic event. If name is NULL, then the name used will be the same as start_field.
tracefs_synth_add_end_field() records the end_field of the ending event as name in the synthetic event. If name is NULL, then the name used will be the same as end_field.
tracefs_synth_append_start_filter() creates a filter or appends to it for the starting event. Depending on type, it will build a string of tokens for parenthesis or logic statements, or it may add a comparison of field to val based on compare.
If type is:
TRACEFS_FILTER_COMPAREβ See the compare list below.TRACEFS_FILTER_ANDβ Append"&&"to the filter.TRACEFS_FILTER_ORβ Append"||"to the filter.TRACEFS_FILTER_NOTβ Append"!"to the filter.TRACEFS_FILTER_OPEN_PARENβ Append"("to the filter.TRACEFS_FILTER_CLOSE_PARENβ Append")"to the filter.
field, compare, and val are ignored unless type is equal to TRACEFS_FILTER_COMPARE. When it is, compare will be used for the following:
TRACEFS_COMPARE_EQβfield == valTRACEFS_COMPARE_NEβfield != valTRACEFS_COMPARE_GTβfield > valTRACEFS_COMPARE_GEβfield >= valTRACEFS_COMPARE_LTβfield < valTRACEFS_COMPARE_LEβfield <= valTRACEFS_COMPARE_REβfield ~ "val": wherefieldis a string.TRACEFS_COMPARE_ANDβfield & val: wherefieldis a flags field.
tracefs_synth_append_end_filter() is the same as tracefs_synth_append_start_filter but filters on the ending event.
tracefs_synth_free() frees the allocated descriptor returned by tracefs_synth_alloc().
π RETURN VALUE π
tracefs_synth_alloc() returns an allocated struct tracefs_synth descriptor on success or NULL on error.
All other functions that return an integer 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 (NULLwhen 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 β¨
okay bestie, here's the whole vibe in one actual program β a synthetic event that measures wakeup latency. no cap, it's a lot, but it slays π
#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;
}
β¨ little heads-up about that filter β the comment in the code literally spells it out, so iykyk: the appended filter bits only build up to "next_prio < 50 || !(prev_prio >= 100" at first. the closing paren is added when the synth actually runs, so the final string ends up as "next_prio < 50 || !(prev_prio >= 100)" once either tracefs_sync_create() or tracefs_sync_echo_cmd() is run. iconic behavior, honestly π
πΈ FILES πΈ
tracefs.hβ the header file you include to get access to all the library APIs. your entry ticket, bestie ποΈ-ltracefsβ the linker switch you add when building a program that uses the library. don't forget it or nothing will link and you'll be sad π
π¦ SEE ALSO π¦
tracefs_synth_create(3), tracefs_synth_destroy(3), tracfes_synth_echo_cmd(3), 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_create(3), tracefs_synth_destroy(3), tracefs_synth_complete(3), tracefs_synth_trace(3), tracefs_synth_snapshot(3), tracefs_synth_save(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),
(yes, tracfes_synth_echo_cmd(3) and tracefs_hist_destory(3) are spelled exactly like that up there β i'm not fixing them, that's what the page says π)
π AUTHOR π
- Steven Rostedt
<rostedt@goodmis.org[1]> - Tzvetomir Stoyanov
<tz.stoyanov@gmail.com[2]> - sameeruddin shaik
<sameeruddin.shaik8@gmail.com[3]>
legends, all of them π«Ά
π REPORTING BUGS π
found a bug? report bugs to <linux-trace-devel@vger.kernel.org[4]>. they want to know, fr fr π
β LICENSE β
libtracefs is Free Software licensed under the GNU LGPL 2.1.
π RESOURCES π
https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
go browse, bestie π§
π COPYING π
Copyright (C) 2020 VMware, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL).
π NOTES π
rostedt@goodmis.orgmailto:rostedt@goodmis.orgtz.stoyanov@gmail.commailto:tz.stoyanov@gmail.comsameeruddin.shaik8@gmail.commailto:sameeruddin.shaik8@gmail.comlinux-trace-devel@vger.kernel.orgmailto:linux-trace-devel@vger.kernel.org
and that's the whole page, bestie β you ate it π
β¨ this one's from libtracefs 1.8.4, dated 10/21/2026, and it's LIBTRACEFS(3) in case you forgot where you were. go make some synthetic events, you got this ππ¦π₯
π¦ where this page comes from
- arch rolling: libtracefs 1.8.4-1 Β· GPL-2.0-only AND LGPL-2.1-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms