171 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# 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:
 | 
						|
 | 
						|
- 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.
 | 
						|
 | 
						|
- 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.
 | 
						|
 | 
						|
- 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.
 |