--- Channels --- As the emulation progresses, information is written in the PRV trace to record the new states. The emulator has specific mechanism to handle the output of new states in the PRV trace via channels. A channel stores an integer that represents an state at a given point in time and corresponds to the value that will be observed in the Paraver timeline. NOTE: In general, the emulator receives events, then performs a state transition and the new state (or states) are written into the PRV file. There are two classes of channels: CPU and thread channels. Both CPU and threads have the same fixed number of channels, given by the enumeration `enum chan`. For example the CHAN_OVNI_STATE of the thread stores the execution state of the thread (running, paused ...). Whereas, the CPU channel CHAN_OVNI_NRTHREADS records how many running threads a given CPU has. The channels are used in the following way: 1) In the "pre" phase, the emulator modifies the state of the emulator based on the new event. The channels are then updated accordingly in this phase, for example when a thread goes from running to paused it must update the CHAN_OVNI_STATE channel of the thread by also the CHAN_OVNI_NRTHREADS channel of the CPU. 2) In the "emit" phase, the emulator calls the chan_emit() method on those channels that have been modified. Those have the dirty attribute set to 1. 3) The optional "post" phase is used to perform some operations before the next event is loaded, but is not commonly used. Then the emulator then loads the next event and repeats the process again. -- Disabling and enabling channels -------------------------------------------- Some channels provide information that only makes sense in some conditions. For example, the CPU channel CHAN_OVNI_TID tracks the TID of the thread currently running in the CPU. When there is no thread running or there are multiple threads running in the same CPU, this channel cannot output valid information. For those cases, the channels can be enabled or disabled as to only provide information when it is necessary. When a channel is disabled, it will emit the value stored in `badst` which by default is set to 0. Notice that if a channel was in a given state A, and was disabled, it must emit the new state is 0. When the channel is enabled again, it will emit again the state A. -- Thread tracking channels ---------------------------------------------------------- Regarding thread channels, there are two common conditions that cause the channels to become disabled. When the thread is no longer running, and then the thread is not active. For those cases, the thread channels can be configured to automatically be enabled or disabled, following the execution state of the thread. The tracking mode specifies how the tracking must be done: - CHAN_TRACK_NONE: nothing to track - CHAN_TRACK_RUNNING_TH: enable the channel only if the thread is running - CHAN_TRACK_ACTIVE_TH: enable the channel only if the thread is running, cooling or warming. This mechanism removes the complexity of detecting when a thread stops running, to update a channel of a given module. As the thread state changes as handled by the emu_ovni.c module only. -- CPU tracking channels ------------------------------------------------------ Similarly, CPU channels can also be configured to track the execution state of the threads. They become disabled when the tracking condition is not met, but also copy the state of the tracking thread channel. They share the same tracking modes, but their behavior is slightly different: In the case of tracking the running thread, if the CPU has more than one thread running, the channel will always output the error state ST_TOO_MANY_TH. If is has no threads running, will be disabled and emit a 0 state by default. Otherwise, it will emit the same value as the running thread. If the thread channel is disabled, it will emit a ST_BAD error state. Regarding the active thread tracking mode, the CPU channels behave similarly, but with the active threads instead of running ones. The CPU tracking mechanism simplify the process of updating CPU channels, as the modules don't need to worry about the execution model. Only the channels need to be configured to follow the proper execution state. -- Channel state modes -------------------------------------------------------- The channels can be updated in three ways: 1) A fixed state can be set to the channel using chan_set(), which overrides the previous state. 2) The new state can be stored in a stack with chan_push() and chan_pop(), to remember the history of the previous states. The emitted event will be the one on the top. 3) Using a punctual event. Setting the channel state is commonly used to track quantities such as the number of threads running per CPU. While the stack mode is commonly used to track functions or sections of code delimited with enter and exit events, which can call an return to the previous state. An example program may be instrumented like this: int bar() { instr("Xb["); ... instr("Xb]"); } int foo() { instr("Xf["); bar(); instr("Xf]"); } Then, in the emulator, when processing the events "Xf[" and "Xf]", we could track of the state as follows: int hook_pre_foo(struct ovni_chan *chan, int value) { switch(value) { case '[': chan_push(chan, 2); break; case ']': chan_pop(chan, 2); break; default: break; } } int hook_pre_bar(struct ovni_chan *chan, int value) { switch(value) { case '[': chan_push(chan, 1); break; case ']': chan_pop(chan, 1); break; default: break; } } The channel will emit the following sequence of states: 0, 1, 2, 1, 0. Notice that the chan_pop() function uses the same state being pop()'ed as argument. The function checks that the stack contains the expected state, forcing the emulator to always receive a matching pair of enter and exit events. -- Punctual events ------------------------------------------------------------ There are some conditions that are better mapped to events rather than to state transitions. For those cases, the channels provide punctual events which are emitted as a state than only has 1 ns of duration. When a channel is configured to emit a punctual event with chan_ev(), it will first output the new state at the current time minus 1 ns, then restore the previous channel state and emit it at the current time.