export default()

in desktop/flipper-ui-core/src/dispatcher/tracking.tsx [75:250]


export default (store: Store, logger: Logger) => {
  const renderHost = getRenderHostInstance();
  sideEffect(
    store,
    {
      name: 'pluginUsageTracking',
      throttleMs: 0,
      noTimeBudgetWarns: true,
      runSynchronously: true,
    },
    getSelectionInfo,
    (selection, store) => {
      const time = Date.now();
      store.dispatch(selectionChanged({selection, time}));
    },
  );

  let droppedFrames: number = 0;
  let largeFrameDrops: number = 0;

  const oldExitData = loadExitData();
  if (oldExitData) {
    const isReload =
      renderHost.serverConfig.environmentInfo.processId === oldExitData.pid;
    const timeSinceLastStartup =
      Date.now() - parseInt(oldExitData.lastSeen, 10);
    // console.log(isReload ? 'reload' : 'restart', oldExitData);
    logger.track('usage', isReload ? 'reload' : 'restart', {
      ...oldExitData,
      pid: undefined,
      timeSinceLastStartup,
    });
    // create fresh exit data
    const {selectedDevice, selectedAppId, selectedPlugin} =
      store.getState().connections;
    persistExitData(
      {
        selectedDevice,
        selectedAppId,
        selectedPlugin,
      },
      false,
    );
  }

  function droppedFrameDetection(
    past: DOMHighResTimeStamp,
    isWindowFocused: () => boolean,
  ) {
    const now = performance.now();
    requestAnimationFrame(() => droppedFrameDetection(now, isWindowFocused));
    const delta = now - past;
    const dropped = Math.round(delta / (1000 / 60) - 1);
    fpsEmitter.emit('fps', delta > 1000 ? 0 : Math.round(1000 / (now - past)));
    if (!isWindowFocused() || dropped < 1) {
      return;
    }
    droppedFrames += dropped;
    if (dropped > 3) {
      largeFrameDrops++;
    }
  }

  if (typeof window !== 'undefined') {
    droppedFrameDetection(
      performance.now(),
      () => store.getState().application.windowIsFocused,
    );
  }

  renderHost.onIpcEvent('trackUsage', (...args: any[]) => {
    let state: State;
    try {
      state = store.getState();
    } catch (e) {
      // if trackUsage is called (indirectly) through a reducer, this will utterly die Flipper. Let's prevent that and log an error instead
      console.error(
        'trackUsage triggered indirectly as side effect of a reducer',
        e,
      );
      return;
    }
    const {selectedDevice, selectedPlugin, selectedAppId, clients} =
      state.connections;

    persistExitData(
      {selectedDevice, selectedPlugin, selectedAppId},
      args[0] === 'exit',
    );

    const currentTime = Date.now();
    const usageSummary = computeUsageSummary(state.usageTracking, currentTime);

    store.dispatch(clearTimeline(currentTime));

    logger.track('usage', TIME_SPENT_EVENT, usageSummary.total);
    for (const key of Object.keys(usageSummary.plugin)) {
      logger.track(
        'usage',
        TIME_SPENT_EVENT,
        usageSummary.plugin[key],
        usageSummary.plugin[key]?.plugin ?? 'none',
      );
    }

    Object.entries(state.connections.enabledPlugins).forEach(
      ([app, plugins]) => {
        // TODO: remove "starred-plugns" event in favor of "enabled-plugins" after some transition period
        logger.track('usage', 'starred-plugins', {
          app,
          starredPlugins: plugins,
        });
        logger.track('usage', 'enabled-plugins', {
          app,
          enabledPugins: plugins,
        });
      },
    );

    const bgStats = getPluginBackgroundStats();
    logger.track('usage', 'plugin-stats', {
      cpuTime: bgStats.cpuTime,
      bytesReceived: bgStats.bytesReceived,
    });
    for (const key of Object.keys(bgStats.byPlugin)) {
      const {
        cpuTimeTotal: _a,
        messageCountTotal: _b,
        bytesReceivedTotal: _c,
        ...dataWithoutTotal
      } = bgStats.byPlugin[key];
      if (Object.values(dataWithoutTotal).some((v) => v > 0)) {
        logger.track('usage', 'plugin-stats-plugin', dataWithoutTotal, key);
      }
    }
    resetPluginBackgroundStatsDelta();

    if (
      !state.application.windowIsFocused ||
      !selectedDevice ||
      !selectedPlugin
    ) {
      return;
    }

    let app: string | null = null;
    let sdkVersion: number | null = null;

    if (selectedAppId) {
      const client = clients.get(selectedAppId);
      if (client) {
        app = client.query.app;
        sdkVersion = client.query.sdk_version || 0;
      }
    }

    const info = {
      droppedFrames,
      largeFrameDrops,
      os: selectedDevice.os,
      device: selectedDevice.title,
      plugin: selectedPlugin,
      app,
      sdkVersion,
      isForeground: state.application.windowIsFocused,
      usedJSHeapSize: (window.performance as any).memory.usedJSHeapSize,
      cpuLoad: renderHost.getPercentCPUUsage?.() ?? 0,
    };

    // reset dropped frames counter
    droppedFrames = 0;
    largeFrameDrops = 0;

    logger.track('usage', 'ping', info);
  });
};