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. 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.