diff --git a/desktop/app/src/index.tsx b/desktop/app/src/index.tsx index 4b1dde559..837a5666b 100644 --- a/desktop/app/src/index.tsx +++ b/desktop/app/src/index.tsx @@ -33,7 +33,6 @@ export { } from './plugin'; export {PluginClient, Props} from './plugin'; export {default as Client} from './Client'; -export {MetricType} from './utils/exportMetrics'; export {reportUsage} from './utils/metrics'; export {default as promiseTimeout} from './utils/promiseTimeout'; export {clipboard, remote, OpenDialogOptions} from 'electron'; diff --git a/desktop/app/src/plugin.tsx b/desktop/app/src/plugin.tsx index 403ac6bbf..4495e459a 100644 --- a/desktop/app/src/plugin.tsx +++ b/desktop/app/src/plugin.tsx @@ -11,7 +11,6 @@ import {KeyboardActions} from './MenuBar'; import {Logger} from './fb-interfaces/Logger'; import Client from './Client'; import {Store} from './reducers/index'; -import {MetricType} from './utils/exportMetrics'; import {ReactNode, Component} from 'react'; import BaseDevice from './devices/BaseDevice'; import {serialize, deserialize} from './utils/serialization'; @@ -131,9 +130,6 @@ export abstract class FlipperBasePlugin< static defaultPersistedState: any; static persistedStateReducer: PersistedStateReducer | null; static maxQueueSize: number = DEFAULT_MAX_QUEUE_SIZE; - static metricsReducer: - | ((persistedState: StaticPersistedState) => Promise) - | undefined; static exportPersistedState: | (( callClient: (method: string, params?: any) => Promise, diff --git a/desktop/app/src/utils/exportMetrics.tsx b/desktop/app/src/utils/exportMetrics.tsx deleted file mode 100644 index 685d61245..000000000 --- a/desktop/app/src/utils/exportMetrics.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import {serialize} from './serialization'; -import {State as PluginStatesState} from '../reducers/pluginStates'; -import {Store} from '../reducers'; -import fs from 'fs'; -import { - ExportType, - fetchMetadata, - determinePluginsToProcess, -} from './exportData'; -import {deserializeObject} from './serialization'; -import {deconstructPluginKey} from './clientUtils'; -import {pluginsClassMap} from './pluginUtils'; -import {PluginDefinition, isSandyPlugin} from '../plugin'; - -export type MetricType = {[metricName: string]: number}; -type MetricPluginType = {[pluginID: string]: MetricType}; -export type ExportMetricType = {[clientID: string]: MetricPluginType}; - -async function exportMetrics( - pluginStates: PluginStatesState, - pluginsMap: Map, - selectedPlugins: Array, -): Promise { - const metrics: ExportMetricType = {}; - for (const key in pluginStates) { - const pluginStateData = pluginStates[key]; - - const plugin = deconstructPluginKey(key); - const pluginName = plugin.pluginName; - if ( - pluginName === undefined || - (selectedPlugins.length > 0 && !selectedPlugins.includes(pluginName)) - ) { - continue; - } - const client = plugin.client; - const pluginClass = pluginsMap.get(pluginName); - const metricsReducer: - | ((persistedState: U) => Promise) - | undefined = - pluginClass && !isSandyPlugin(pluginClass) // This feature doesn't seem to be used at all, so let's add it when needed for Sandy - ? pluginClass.metricsReducer - : undefined; - if (pluginsMap.has(pluginName) && metricsReducer) { - const metricsObject = await metricsReducer(pluginStateData); - const pluginObject: MetricPluginType = {}; - pluginObject[pluginName] = metricsObject; - if (!metrics[client]) { - metrics[client] = pluginObject; - continue; - } - const mergedMetrics = {...metrics[client], ...pluginObject}; - metrics[client] = mergedMetrics; - } - } - return Promise.resolve(await serialize(metrics)); -} - -export async function exportMetricsWithoutTrace( - store: Store, - pluginStates: PluginStatesState, -): Promise { - const pluginsMap = pluginsClassMap(store.getState().plugins); - const {clients, selectedDevice} = store.getState().connections; - const pluginsToProcess = determinePluginsToProcess( - clients, - selectedDevice, - store.getState().plugins, - ); - const metadata = await fetchMetadata( - pluginsToProcess, - pluginStates, - store.getState(), - ); - const newPluginStates = metadata.pluginStates; - const {errors} = metadata; - if (errors) { - console.error(errors); - } - - const metrics = await exportMetrics( - newPluginStates, - pluginsMap, - store.getState().plugins.selectedPlugins, - ); - return metrics; -} - -function parseJSON(str: string): any { - try { - return JSON.parse(str); - } catch (e) { - console.error(e); - return undefined; - } -} - -export async function exportMetricsFromTrace( - trace: string, - pluginsMap: Map, - selectedPlugins: Array, -): Promise { - const data = fs.readFileSync(trace, 'utf8'); - const parsedJSONData = parseJSON(data); - if (!parsedJSONData) { - return Promise.reject( - new Error('Please pass the file which has a valid JSON'), - ); - } - const importedData: ExportType = deserializeObject(parsedJSONData); - const importedStore = importedData.store; - if (!importedStore) { - return Promise.reject( - new Error( - 'No store in the imported file, thus exiting without exporting metrics.', - ), - ); - } - - const {pluginStates} = importedStore; - - if (!pluginStates) { - return Promise.reject( - new Error( - 'No pluginStates in the imported file, thus exiting without exporting metrics.', - ), - ); - } - return await exportMetrics(pluginStates, pluginsMap, selectedPlugins); -} diff --git a/desktop/headless/index.tsx b/desktop/headless/index.tsx index 821b97437..7d0dd7ccb 100644 --- a/desktop/headless/index.tsx +++ b/desktop/headless/index.tsx @@ -17,10 +17,6 @@ import dispatcher from '../app/src/dispatcher/index'; import reducers, {Actions, State} from '../app/src/reducers/index'; import {init as initLogger} from '../app/src/fb-stubs/Logger'; import {exportStore} from '../app/src/utils/exportData'; -import { - exportMetricsWithoutTrace, - exportMetricsFromTrace, -} from '../app/src/utils/exportMetrics'; import {listDevices} from '../app/src/utils/listDevices'; import setup from '../static/setup'; import { @@ -42,7 +38,7 @@ type UserArguments = { dev: boolean; exit: 'sigint' | 'disconnect'; verbose: boolean; - metrics: string; + metrics: boolean; listDevices: boolean; device: string; listPlugins: boolean; @@ -116,13 +112,6 @@ type UserArguments = { .version(global.__VERSION__) .help().argv; // http://yargs.js.org/docs/#api-argv -function shouldExportMetric(metrics: string): boolean { - if (!metrics) { - return process.argv.includes('--metrics'); - } - return true; -} - function outputAndExit(output: string | null | undefined): void { output = output || ''; console.log(`Finished. Outputting ${output.length} characters.`); @@ -161,7 +150,7 @@ async function exitActions( userArguments: UserArguments, store: Store, ): Promise { - const {metrics, exit} = userArguments; + const {exit} = userArguments; for (const exitAction of exitClosures) { try { const action = await exitAction(userArguments, store); @@ -176,20 +165,11 @@ async function exitActions( if (exit == 'sigint') { process.on('SIGINT', async () => { try { - if (shouldExportMetric(metrics) && !metrics) { - const state = store.getState(); - const payload = await exportMetricsWithoutTrace( - store, - state.pluginStates, - ); - outputAndExit(payload); - } else { - const {serializedString, fetchMetaDataErrors} = await exportStore( - store, - ); - console.error('Error while fetching metadata', fetchMetaDataErrors); - outputAndExit(serializedString); - } + const {serializedString, fetchMetaDataErrors} = await exportStore( + store, + ); + console.error('Error while fetching metadata', fetchMetaDataErrors); + outputAndExit(serializedString); } catch (e) { errorAndExit(e); } @@ -217,7 +197,7 @@ async function storeModifyingActions( } async function startFlipper(userArguments: UserArguments) { - const {verbose, metrics, exit, insecurePort, securePort} = userArguments; + const {verbose, exit, insecurePort, securePort, metrics} = userArguments; console.error(` _____ _ _ | __| |_|___ ___ ___ ___ @@ -225,6 +205,11 @@ async function startFlipper(userArguments: UserArguments) { |__| |_|_| _| _|___|_| v${global.__VERSION__} |_| |_| `); + if (metrics) { + throw new Error( + '--metrics is no longer supported, see D24332440 for details.', + ); + } // redirect all logging to stderr const overriddenMethods = ['debug', 'info', 'log', 'warn', 'error']; @@ -253,24 +238,13 @@ async function startFlipper(userArguments: UserArguments) { // TODO(T42325892): Investigate why the export stalls without exiting the // current eventloop task here. setTimeout(() => { - if (shouldExportMetric(metrics) && !metrics) { - const state = store.getState(); - exportMetricsWithoutTrace(store as Store, state.pluginStates) - .then((payload: string | null) => { - outputAndExit(payload || ''); - }) - .catch((e: Error) => { - errorAndExit(e); - }); - } else { - exportStore(store) - .then(({serializedString}) => { - outputAndExit(serializedString); - }) - .catch((e: Error) => { - errorAndExit(e); - }); - } + exportStore(store) + .then(({serializedString}) => { + outputAndExit(serializedString); + }) + .catch((e: Error) => { + errorAndExit(e); + }); }, 10); } return next(action); @@ -378,19 +352,6 @@ async function startFlipper(userArguments: UserArguments) { }); }, async (userArguments: UserArguments, store: Store) => { - const {metrics} = userArguments; - if (shouldExportMetric(metrics) && metrics && metrics.length > 0) { - try { - const payload = await exportMetricsFromTrace( - metrics, - pluginsClassMap(store.getState().plugins), - store.getState().plugins.selectedPlugins, - ); - return {exit: true, result: payload ? payload.toString() : ''}; - } catch (error) { - return {exit: true, result: error}; - } - } return Promise.resolve({exit: false}); }, ]; diff --git a/desktop/plugins/fresco/__tests__/index.node.tsx b/desktop/plugins/fresco/__tests__/index.node.tsx index 21be1c35c..fd3d5ed31 100644 --- a/desktop/plugins/fresco/__tests__/index.node.tsx +++ b/desktop/plugins/fresco/__tests__/index.node.tsx @@ -10,7 +10,7 @@ import FrescoPlugin from '../index'; import {PersistedState, ImageEventWithId} from '../index'; import {AndroidCloseableReferenceLeakEvent} from '../api'; -import {MetricType, Notification} from 'flipper'; +import {Notification} from 'flipper'; import {ImagesMap} from '../ImagePool'; type ScanDisplayTime = {[scan_number: number]: number}; @@ -63,227 +63,6 @@ function mockPersistedState( }; } -test('the metric reducer for the input having regression', () => { - const persistedState = mockPersistedState( - [ - { - width: 150, - height: 150, - }, - { - width: 150, - height: 150, - }, - { - width: 150, - height: 150, - }, - ], - { - width: 100, - height: 100, - }, - ); - expect(FrescoPlugin.metricsReducer).toBeDefined(); - const metrics = FrescoPlugin.metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({ - WASTED_BYTES: 37500, - }); -}); - -test('the metric reducer for the input having no regression', () => { - const persistedState = mockPersistedState( - [ - { - width: 50, - height: 10, - }, - { - width: 50, - height: 50, - }, - { - width: 50, - height: 50, - }, - ], - { - width: 100, - height: 100, - }, - ); - const metricsReducer = FrescoPlugin.metricsReducer; - expect(metricsReducer).toBeDefined(); - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({ - WASTED_BYTES: 0, - }); -}); - -test('the metric reducer for the default persisted state', () => { - const metricsReducer = FrescoPlugin.metricsReducer; - expect(metricsReducer).toBeDefined(); - const metrics = metricsReducer(FrescoPlugin.defaultPersistedState); - return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0}); -}); - -test('the metric reducer with the events data but with no imageData in imagesMap ', () => { - const persistedState = mockPersistedState( - [ - { - width: 50, - height: 10, - }, - { - width: 50, - height: 50, - }, - { - width: 50, - height: 50, - }, - ], - { - width: 100, - height: 100, - }, - ); - persistedState.imagesMap = {}; - const metricsReducer = FrescoPlugin.metricsReducer; - expect(metricsReducer).toBeDefined(); - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0}); -}); - -test('the metric reducer with the no viewPort data in events', () => { - const persistedState = mockPersistedState( - [ - { - width: 50, - height: 10, - }, - { - width: 50, - height: 50, - }, - { - width: 50, - height: 50, - }, - ], - { - width: 100, - height: 100, - }, - ); - delete persistedState.events[0].viewport; - const metricsReducer = FrescoPlugin.metricsReducer; - expect(metricsReducer).toBeDefined(); - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0}); -}); -test('the metric reducer with the multiple events', () => { - const scanDisplayTime: ScanDisplayTime = {}; - scanDisplayTime[1] = 3; - const events: Array = [ - { - imageIds: ['0', '1'], - eventId: 0, - attribution: [], - startTime: 1, - endTime: 2, - source: 'source', - coldStart: true, - viewport: {width: 100, height: 100, scanDisplayTime}, - }, - { - imageIds: ['2', '3'], - eventId: 1, - attribution: [], - startTime: 1, - endTime: 2, - source: 'source', - coldStart: true, - viewport: {width: 50, height: 50, scanDisplayTime}, - }, - ]; - const imageSizes = [ - { - width: 150, - height: 150, - }, - { - width: 100, - height: 100, - }, - { - width: 250, - height: 250, - }, - { - width: 300, - height: 300, - }, - ]; - const imagesMap = imageSizes.reduce((acc, val, index) => { - acc[index] = { - imageId: String(index), - width: val.width, - height: val.height, - sizeBytes: 10, - data: 'undefined', - }; - return acc; - }, {} as ImagesMap); - const persistedState = { - surfaceList: new Set(), - images: [], - nextEventId: 0, - events, - imagesMap, - closeableReferenceLeaks: [], - isLeakTrackingEnabled: true, - showDiskImages: false, - }; - const metricsReducer = FrescoPlugin.metricsReducer; - expect(metricsReducer).toBeDefined(); - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 160000}); -}); - -test('closeable reference metrics on empty state', () => { - const metricsReducer: ( - persistedState: PersistedState, - ) => Promise = FrescoPlugin.metricsReducer; - const persistedState = mockPersistedState(); - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({CLOSEABLE_REFERENCE_LEAKS: 0}); -}); - -test('closeable reference metrics on input', () => { - const metricsReducer: ( - persistedState: PersistedState, - ) => Promise = FrescoPlugin.metricsReducer; - const closeableReferenceLeaks: Array = [ - { - identityHashCode: 'deadbeef', - className: 'com.facebook.imagepipeline.memory.NativeMemoryChunk', - stacktrace: null, - }, - { - identityHashCode: 'f4c3b00c', - className: 'com.facebook.flipper.SomeMemoryAbstraction', - stacktrace: null, - }, - ]; - const persistedState = { - ...mockPersistedState(), - closeableReferenceLeaks, - }; - const metrics = metricsReducer(persistedState); - return expect(metrics).resolves.toMatchObject({CLOSEABLE_REFERENCE_LEAKS: 2}); -}); - test('notifications for leaks', () => { const notificationReducer: ( persistedState: PersistedState, diff --git a/desktop/plugins/fresco/index.tsx b/desktop/plugins/fresco/index.tsx index a36e309c7..0d6fa8847 100644 --- a/desktop/plugins/fresco/index.tsx +++ b/desktop/plugins/fresco/index.tsx @@ -19,7 +19,7 @@ import { } from './api'; import {Fragment} from 'react'; import {ImagesMap} from './ImagePool'; -import {MetricType, ReduxState} from 'flipper'; +import {ReduxState} from 'flipper'; import React from 'react'; import ImagesCacheOverview from './ImagesCacheOverview'; import { @@ -207,37 +207,6 @@ export default class FlipperImagesPlugin extends FlipperPlugin< return persistedState; }; - static metricsReducer = ( - persistedState: PersistedState, - ): Promise => { - const {events, imagesMap, closeableReferenceLeaks} = persistedState; - - const wastedBytes = (events || []).reduce((acc, event) => { - const {viewport, imageIds} = event; - if (!viewport) { - return acc; - } - return imageIds.reduce((innerAcc, imageID) => { - const imageData: ImageData = imagesMap[imageID]; - if (!imageData) { - return innerAcc; - } - const imageWidth: number = imageData.width; - const imageHeight: number = imageData.height; - const viewPortWidth: number = viewport.width; - const viewPortHeight: number = viewport.height; - const viewPortArea = viewPortWidth * viewPortHeight; - const imageArea = imageWidth * imageHeight; - return innerAcc + Math.max(0, imageArea - viewPortArea); - }, acc); - }, 0); - - return Promise.resolve({ - WASTED_BYTES: wastedBytes, - CLOSEABLE_REFERENCE_LEAKS: (closeableReferenceLeaks || []).length, - }); - }; - static getActiveNotifications = ({ closeableReferenceLeaks = [], isLeakTrackingEnabled = false,