Expand description
§EventHeader-encoded Linux Tracepoints
The eventheader
crate provides a simple and efficient way to log
EventHeader-encoded
Tracepoints
via the Linux user_events
system.
This crate uses macros to generate event metadata at compile-time, improving runtime performance and minimizing dependencies. To enable compile-time metadata generation, the event schema must be specified at compile-time. For example, event name and field names must be string literals, not variables.
In rare cases, you might not know what events you want to log until runtime. For
example, you might be implementing a middle-layer library providing event support to a
dynamic top-layer or a scripting language like JavaScript or Python. In these cases,
you might use the eventheader_dynamic
crate instead of this crate.
§Configuration
- Linux kernel 6.4 or later, with
user_events
support enabled (CONFIG_USER_EVENTS=y
). - Must have either
tracefs
ordebugfs
mounted. For example, you might add the following line to your/etc/fstab
file:tracefs /sys/kernel/tracing tracefs defaults 0 0
- The user that will generate events must have
x
access to thetracing
directory andw
access to thetracing/user_events_data
file. One possible implementation is to create atracers
group, then:chgrp tracers /sys/kernel/tracing
chgrp tracers /sys/kernel/tracing/user_events_data
chmod g+x /sys/kernel/tracing
chmod g+w /sys/kernel/tracing/user_events_data
- Collect traces using a tool like
perf
. - Decode traces using a tool like
decode-perf
.
§Overview
-
Use
define_provider!
to create a static symbol for yourProvider
, e.g.define_provider!(MY_PROVIDER, "MyCompany_MyComponent");
-
During component initialization, register the provider, e.g.
unsafe { MY_PROVIDER.register(); }
- Safety:
Provider::register
is unsafe because all providers registered by an unloadable shared object must be properly unregistered before the shared object unloads. Since the provider is static, Rust will not automatically drop it. If you are writing an application (not a shared object), this is not a safety issue because the process exits before the application binary unloads.
- Safety:
-
As needed, use
write_event!
to send events to user_events. -
During component shutdown, unregister the provider, e.g.
MY_PROVIDER.unregister();
Note that provider symbol is not pub so it is not visible outside the module. To share the symbol with multiple modules, put the define_provider in the parent module, e.g. in lib.rs.
§Example
use eventheader as eh;
// Define a static variable for the "MyCompany_MyComponent" provider.
eh::define_provider!(
MY_PROVIDER, // The static symbol to use for this provider.
"MyCompany_MyComponent"); // The provider's name (string literal).
// Register the provider at component initialization. If you don't register (or if
// register fails) then MY_PROVIDER.enabled() will always return false, the
// write_event! macro will be a no-op, and MY_PROVIDER.unregister() will be a no-op.
// Safety: If this is a shared object, you MUST call MY_PROVIDER.unregister() before unload.
unsafe { MY_PROVIDER.register(); }
// As necessary, call write_event! to send events to user_events.
let field1_value = "String Value";
let field2_value = 42u32;
eh::write_event!(
MY_PROVIDER, // The provider to use for the event.
"MyEventName", // The event's name (string literal).
level(Warning), // Event's severity level.
keyword(0x23), // Event category bits.
str8("Field1", field1_value), // Add a string field to the event.
u32("Field2", &field2_value), // Add an integer field to the event.
);
// Before module unload, unregister the provider.
MY_PROVIDER.unregister();
§Notes
Field value expressions will only be evaluated if the event is enabled, i.e. only if one or more perf logging sessions have enabled the tracepoint that corresponds to the provider + level + keyword combination of the event.
Perf events are limited in size (event size = headers + metadata + data). The system will ignore any event that is larger than 64KB.
Collect the events using a tool like perf
.
Decode the events using a tool like
decode-perf
.
Note that you cannot enable a tracepoint until the tracepoint has been registered.
Most programs will register all of their tracepoints when they start running, so
you can run the program once and then create the session to collect the events.
As an alternative, you can pre-register eventheader-based tracepoints using a tool
like eventheader-register
.
For example, to collect and decode events with level=5 and keyword=1 from a provider
that was defined as define_provider!(MY_PROVIDER, "MyCompany_MyComponent")
:
perf record -e user_events:MyCompany_MyComponent_L5K1
<run your program>
<Ctrl-C to stop the perf tool>
decode-perf perf.data > perf.json
Modules§
- changelog
- Release history
Macros§
- define_
provider - Creates a static symbol representing a tracepoint provider.
- provider_
enabled - Returns true if a
write_event!
macro using the specified PROVIDER_SYMBOL, level, and keyword would be enabled, false if it would be disabled. - time_
from_ systemtime - Converts a
std::time::SystemTime
into atime_t
i64
value. (Usually not needed - thesystemtime
field type does this automatically.) - write_
event - Sends an event to
user_events
via the specified provider.
Structs§
- Field
Encoding - Values for the encoding byte of a field definition.
- Field
Format - Values for the format byte of a field definition.
- Guid
- UUID with big-endian in-memory representation.
- Level
- Indicates the severity of an event. Use Verbose if unsure.
- Opcode
- Values for
EventHeader::opcode
indicating special semantics to be used by the event decoder for grouping and organizing events, e.g. for activities. - Provider
- A connection for writing compile-time-defined Linux tracepoints.
Enums§
- Native
Implementation - Possible configurations under which this crate can be compiled:
LinuxUserEvents
orOther
.
Constants§
- NATIVE_
IMPLEMENTATION - The configuration under which this crate was compiled:
LinuxUserEvents
orOther
.