158 lines
6.3 KiB
Plaintext
158 lines
6.3 KiB
Plaintext
|
--- 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.
|