static findInteractionEvent(responsivenessEvent, {traceEvents}) {
    const candidates = traceEvents.filter(/** @return {evt is EventTimingEvent} */ evt => {
      // Examine only beginning/instant EventTiming events.
      return evt.name === 'EventTiming' && evt.ph !== 'e';
    });

    // If trace is from < m103, the timestamps cannot be trusted
    // <m103 traces (bad) had a   args.frame (we used to provide a fallback trace event, but not
    //                                        any more)
    // m103+ traces (good) have a args.data.frame (https://crrev.com/c/3632661)
    if (candidates.length && candidates.every(candidate => !candidate.args.data?.frame)) {
      throw new LighthouseError(
        LighthouseError.errors.UNSUPPORTED_OLD_CHROME,
        {featureName: 'detailed EventTiming trace events'}
      );
    }

    const {maxDuration, interactionType} = responsivenessEvent.args.data;
    let bestMatchEvent;
    let minDurationDiff = Number.POSITIVE_INFINITY;
    for (const candidate of candidates) {
      // Must be from same frame.
      if (candidate.args.data.frame !== responsivenessEvent.args.frame) continue;

      // TODO(bckenny): must be in same navigation as well.

      const {type, duration} = candidate.args.data;
      // Discard if type is incompatible with responsiveness interactionType.
      const matchingTypes = interactionTypeToType[interactionType];
      if (!matchingTypes) {
        throw new Error(`unexpected responsiveness interactionType '${interactionType}'`);
      }
      if (!matchingTypes.has(type)) continue;

      const durationDiff = Math.abs(duration - maxDuration);
      if (durationDiff < minDurationDiff) {
        bestMatchEvent = candidate;
        minDurationDiff = durationDiff;
      }
    }

    if (!bestMatchEvent) {
      throw new Error(`no interaction event found for responsiveness type '${interactionType}'`);
    }
    // TODO: seems to regularly happen up to 3ms and as high as 4. Allow for up to 5ms to be sure.
    if (minDurationDiff > 5) {
      throw new Error(`no interaction event found within 5ms of responsiveness maxDuration (max: ${maxDuration}, closest ${bestMatchEvent.args.data.duration})`); // eslint-disable-line max-len
    }

    return bestMatchEvent;
  }
Finds the interaction event that was probably the responsivenessEvent.maxDuration
source.
Note that (presumably due to rounding to ms), the interaction duration may not
be the same value as `maxDuration`, just the closest value. Function will throw
if the closest match is off by more than 4ms.
TODO: this doesn't try to match inputs to interactions and break ties if more than
one interaction had this duration by returning the first found.
@param {ResponsivenessEvent} responsivenessEvent
@param {LH.Trace} trace
@return {EventTimingEvent}