Events and Callbacks

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(type). Each event type is associated with a callback in struct utrace_engine_ops. A tracing engine can leave unused callbacks 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.