An attached engine does nothing by default. An engine makes something
happen by requesting callbacks via utrace_set_events
and poking the thread with utrace_control
.
The synchronization issues related to these two calls
are discussed further below in the section called “Tear-down Races”.
Events are specified using the macro
UTRACE_EVENT(
.
Each event type is associated with a callback in struct
utrace_engine_ops. A tracing engine can leave unused
callbacks type
)NULL
. The only callbacks required
are those used by the event flags it sets.
Many engines can be attached to each thread. When a thread has an event, each engine gets a callback if it has set the event flag for that event type. Engines are called in the order they attached. Engines that attach after the event has occurred do not get callbacks for that event. This includes any new engines just attached by an existing engine's callback function. Once the sequence of callbacks for that one event has completed, such new engines are then eligible in the next sequence that starts when there is another event.
Event reporting callbacks have details particular to the event type,
but are all called in similar environments and have the same
constraints. Callbacks are made from safe points, where no locks
are held, no special resources are pinned (usually), and the
user-mode state of the thread is accessible. So, callback code has
a pretty free hand. But to be a good citizen, callback code should
never block for long periods. It is fine to block in
kmalloc
and the like, but never wait for i/o or
for user mode to do something. If you need the thread to wait, use
UTRACE_STOP
and return from the callback
quickly. When your i/o finishes or whatever, you can use
utrace_control
to resume the thread.