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