There are two modes of operation for the causality engine: throttled mode and unthrottled mode. For each run of the application, exactly one node is in throttled mode; all others are unthrottled. In both modes, each I/O is intercepted by the causality engine and stored in a trace for the respective node. This trace includes the I/O operations and their arguments. A node in throttled mode creates an I/O trace annotated with computation time (using Approach 1 or 2 from Section 3.2) and the ``signaling'' information. A node in unthrottled mode creates an I/O trace annotated with the ``waiting'' information and also the computation information if Approach 2 is used.
After runs of an application (
), each node has
traces that
must be merged. At most one of the traces per node contains the
I/O when that node is being throttled, including SIGNAL() and COMPUTE() calls;
all other traces reflect the I/O when the node is in unthrottled mode, including WAIT() calls, and also COMPUTE() calls if
Approach 2 is used.
Note that regardless of the mode, the I/O in all traces for a particular
node should be identical, as our assumption is a deterministic I/O workload.
If the I/O being issued by the application changes, we can easily detect this
and report an appropriate error (e.g., ``attempt to trace a non-deterministic
application'').