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. For most events, 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.
The UTRACE_EVENT(SYSCALL_ENTRY)
event is a special
case. While other events happen in the kernel when it will return to
user mode soon, this event happens when entering the kernel before it
will proceed with the work requested from user mode. Because of this
difference, the report_syscall_entry
callback is
special in two ways. For this event, engines are called in reverse of
the normal order (this includes the report_quiesce
call that precedes a report_syscall_entry
call).
This preserves the semantics that the last engine to attach is called
"closest to user mode"--the engine that is first to see a thread's user
state when it enters the kernel is also the last to see that state when
the thread returns to user mode. For the same reason, if these
callbacks use UTRACE_STOP
(see the next section),
the thread stops immediately after callbacks rather than only when it's
ready to return to user mode; when allowed to resume, it will actually
attempt the system call indicated by the register values at that time.