From 942d41808aa701413bb72bc02bd360b83dc4d71a Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Thu, 19 Aug 2021 01:44:05 +0300 Subject: [PATCH 01/18] Enable Onyx.captureMetrics with canCapturePerformanceMetrics --- src/components/OnyxProvider.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js index bdcd335bb784..504e5c6f320b 100644 --- a/src/components/OnyxProvider.js +++ b/src/components/OnyxProvider.js @@ -7,11 +7,13 @@ import ComposeProviders from './ComposeProviders'; import CONST from '../CONST'; import Log from '../libs/Log'; import listenToStorageEvents from '../libs/listenToStorageEvents'; +import canCapturePerformanceMetrics from '../libs/canCapturePerformanceMetrics'; // Initialize the store when the app loads for the first time Onyx.init({ keys: ONYXKEYS, safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + captureMetrics: canCapturePerformanceMetrics(), initialKeyStates: { // Clear any loading and error messages so they do not appear on app startup From b30fde0da2cb11f50b0b0110be53fc8d85690533 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Thu, 19 Aug 2021 13:58:01 +0300 Subject: [PATCH 02/18] add react-native-performance-flipper-reporter --- package-lock.json | 18 +++++++++++++++--- package.json | 4 +++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca7595744909..b197eca71118 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23850,6 +23850,12 @@ "integrity": "sha512-KdJcKbgnFfkdhQ8+dOLQtpeeQDFkLjKC21fM20iZ1lpZg+XJ5EHRZWhqzwJMgt0pDFLA7Ws2itmZESYtjyyvKA==", "dev": true }, + "flipper-plugin-react-native-performance": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/flipper-plugin-react-native-performance/-/flipper-plugin-react-native-performance-0.6.0.tgz", + "integrity": "sha512-2l1oFYYyloyg2AtsaY7rGaolsgvbF1xrbdBfJFuP3581hM8tz/GxziymzUOR6OuFu6wRJy0nxUVKx2D5UCV6vA==", + "dev": true + }, "flow-parser": { "version": "0.121.0", "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz", @@ -36021,9 +36027,9 @@ "integrity": "sha512-XMSDibp1GX0UMlVdsmAgjmf4/FJ+TCvVLWdKjV4QkTIO3TbDKsWSAS1+9jgUYcqIwQpO87SFBkvJ5cjOwx9vNA==" }, "react-native-flipper": { - "version": "0.100.0", - "resolved": "https://registry.npmjs.org/react-native-flipper/-/react-native-flipper-0.100.0.tgz", - "integrity": "sha512-UsRlT9UAPHs9K+w1gVi/QEZgYem+W1Z2YXwr5NToAwNMsmTCfl0OpfWS0Xw9FiNQIOcpT2MTlPDHzL3sAQH44A==", + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/react-native-flipper/-/react-native-flipper-0.103.0.tgz", + "integrity": "sha512-F49O8FAPLt9BsuKQYre3ATJ8oaghc9xJu/S4WFhKqAboesFoUyB3i8EAkBFbXEgA1+Foolct3QTWQLc7KTI/tA==", "dev": true }, "react-native-gesture-handler": { @@ -36174,6 +36180,12 @@ "resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-2.0.0.tgz", "integrity": "sha512-jKM9Qg0SkL9D9ad377nxb1VV+OXJSyYyIrBHKmM6CABNxfrLVA5xkQMEibjmZQde7b0ndJOZoQAiObgJjjc4VQ==" }, + "react-native-performance-flipper-reporter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-native-performance-flipper-reporter/-/react-native-performance-flipper-reporter-2.0.0.tgz", + "integrity": "sha512-ccOgq99eK3OvrNNhpJDC4ydNk/1JGgWZPo2FLrPDLUHXAR4EcE9cUAtb46oGOpvHk5ZOb5aEDofc/CS9OEGcag==", + "dev": true + }, "react-native-permissions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-3.0.1.tgz", diff --git a/package.json b/package.json index 564e7caaca76..78daca60fa8b 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "eslint-plugin-detox": "^1.0.0", "eslint-plugin-jest": "^24.1.0", "flipper-plugin-bridgespy-client": "^0.1.9", + "flipper-plugin-react-native-performance": "^0.6.0", "html-webpack-plugin": "^4.3.0", "jest": "^26.5.2", "jest-circus": "^26.5.2", @@ -159,7 +160,8 @@ "portfinder": "^1.0.28", "pusher-js-mock": "^0.3.3", "react-hot-loader": "^4.12.21", - "react-native-flipper": "^0.100.0", + "react-native-flipper": "^0.103.0", + "react-native-performance-flipper-reporter": "^2.0.0", "react-native-svg-transformer": "^0.14.3", "react-test-renderer": "16.13.1", "rn-async-storage-flipper": "0.0.10", From 8d1524a2962c651784f2358af35a610133b68e01 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Thu, 19 Aug 2021 23:19:30 +0300 Subject: [PATCH 03/18] Add performance capturing sugar --- src/libs/Performance.js | 172 ++++++++++++++++++++++++++++++++-------- 1 file changed, 141 insertions(+), 31 deletions(-) diff --git a/src/libs/Performance.js b/src/libs/Performance.js index ba6805fd2e57..27ca7ba4bb07 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -1,6 +1,14 @@ import _ from 'underscore'; import lodashTransform from 'lodash/transform'; +import React, {Profiler, forwardRef} from 'react'; +import {Alert} from 'react-native'; + import canCapturePerformanceMetrics from './canCapturePerformanceMetrics'; +import getComponentDisplayName from './getComponentDisplayName'; +import CONST from '../CONST'; + +/** @type {import('react-native-performance').Performance} */ +let performance; /** * Deep diff between two objects. Useful for figuring out what changed about an object from one render to the next so @@ -25,43 +33,145 @@ function diffObject(object, base) { return changes(object, base); } -/** - * Sets up an observer to capture events recorded in the native layer before the app fully initializes. - */ -function setupPerformanceObserver() { - if (!canCapturePerformanceMetrics()) { - return; - } +// When performance monitoring is disabled the implementations are blank +/* eslint-disable import/no-mutable-exports */ +let setupPerformanceObserver = () => {}; +let printPerformanceMetrics = () => {}; +let markStart = () => {}; +let markEnd = () => {}; +let traceRender = () => {}; +let withRenderTrace = () => Component => Component; +/* eslint-enable import/no-mutable-exports */ - const performance = require('react-native-performance').default; - const PerformanceObserver = require('react-native-performance').PerformanceObserver; - new PerformanceObserver((list) => { - if (list.getEntries().find(entry => entry.name === 'nativeLaunchEnd')) { - performance.measure('nativeLaunch', 'nativeLaunchStart', 'nativeLaunchEnd'); - - // eslint-disable-next-line no-undef - if (__DEV__) { - performance.measure('jsBundleDownload', 'downloadStart', 'downloadEnd'); - } else { - performance.measure('runJsBundle', 'runJsBundleStart', 'runJsBundleEnd'); - } - } - }).observe({type: 'react-native-mark', buffered: true}); -} +if (canCapturePerformanceMetrics()) { + /** + * Sets up an observer to capture events recorded in the native layer before the app fully initializes. + */ + setupPerformanceObserver = () => { + const performanceReported = require('react-native-performance-flipper-reporter'); + performanceReported.setupDefaultFlipperReporter(); -/** - * Outputs performance stats. We alert these so that they are easy to access in release builds. - */ -function printPerformanceMetrics() { - const performance = require('react-native-performance').default; - const entries = _.map(performance.getEntriesByType('measure'), entry => ({ - name: entry.name, duration: Math.floor(entry.duration), - })); - alert(JSON.stringify(entries, null, 4)); + const perfModule = require('react-native-performance'); + perfModule.setResourceLoggingEnabled(true); + performance = perfModule.default; + + // Monitor some native marks that we want to put on the timeline + new perfModule.PerformanceObserver((list, observer) => { + list.getEntries() + .forEach((entry) => { + if (entry.name === 'nativeLaunchEnd') { + performance.measure('nativeLaunch', 'nativeLaunchStart', 'nativeLaunchEnd'); + } + if (entry.name === 'downloadEnd') { + performance.measure('jsBundleDownload', 'downloadStart', 'downloadEnd'); + + // We don't need to keep the observer past this point + observer.disconnect(); + } + }); + }).observe({type: 'react-native-mark', buffered: true}); + + new perfModule.PerformanceObserver((list) => { + list.getEntriesByType('mark') + .forEach((mark) => { + if (mark.name.endsWith('_end')) { + const end = mark.name; + const name = end.replace(/_end$/, ''); + const start = `${name}_start`; + performance.measure(name, start, end); + } + + if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) { + performance.measure('TTI', 'nativeLaunchStart', mark.name); + printPerformanceMetrics(); + } + }); + }).observe({type: 'mark', buffered: true}); + }; + + /** + * Outputs performance stats. We alert these so that they are easy to access in release builds. + */ + printPerformanceMetrics = () => { + const stats = [...performance.getEntriesByType('measure'), ...performance.getEntriesByType('react-native-mark')] + .filter(entry => entry.duration > 0) + .map(entry => `\u2022 ${entry.name}: ${entry.value || entry.duration.toFixed(1)}ms`); + + Alert.alert('Performance', stats.join('\n')); + }; + + /** + * Adds a start mark to the performance entries + * @param {string} name + * @param {Object} [detail] + * @returns {PerformanceMark} + */ + markStart = (name, detail) => performance.mark(`${name}_start`, {detail}); + + /** + * Adds an end mark to the performance entries + * This would also automatically create a measure between start and end + * @param {string} name + * @param {Object} [detail] + * @returns {PerformanceMark} + */ + markEnd = (name, detail) => performance.mark(`${name}_end`, {detail}); + + /** + * Put data emitted by Profiler components on the timeline + * @param {string} id the "id" prop of the Profiler tree that has just committed + * @param {'mount'|'update'} phase either "mount" (if the tree just mounted) or "update" (if it re-rendered) + * @param {number} actualDuration time spent rendering the committed update + * @param {number} baseDuration estimated time to render the entire subtree without memoization + * @param {number} startTime when React began rendering this update + * @param {number} commitTime when React committed this update + * @param {Set} interactions the Set of interactions belonging to this update + * @returns {PerformanceMeasure} + */ + traceRender = ( + id, + phase, + actualDuration, + baseDuration, + startTime, + commitTime, + interactions, + ) => performance.measure(id, { + start: startTime, + duration: actualDuration, + detail: { + phase, + baseDuration, + commitTime, + interactions, + }, + }); + + /** + * A HOC that captures render timings of the Wrapped component + * @param {object} config + * @param {string} config.id + * @returns {function(React.Component): React.FunctionComponent} + */ + withRenderTrace = ({id}) => (WrappedComponent) => { + const WithRenderTrace = forwardRef((props, ref) => ( + + {/* eslint-disable-next-line react/jsx-props-no-spreading */} + + + )); + + WithRenderTrace.displayName = `withRenderTrace(${getComponentDisplayName(WrappedComponent)})`; + return WithRenderTrace; + }; } export { diffObject, printPerformanceMetrics, setupPerformanceObserver, + markStart, + markEnd, + traceRender, + withRenderTrace, }; From 23f0ad469f5de415983b3df2a996cf03009200fb Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 00:01:23 +0300 Subject: [PATCH 04/18] Capture performance marks for init and chat switches --- src/libs/actions/App.js | 14 +++----------- src/pages/home/report/ReportActionsView.js | 6 ++++++ src/pages/home/sidebar/SidebarLinks.js | 2 ++ src/pages/home/sidebar/SidebarScreen.js | 2 ++ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 9686a5282316..63cc1d86072a 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -7,8 +7,7 @@ import CONST from '../../CONST'; import Log from '../Log'; import CONFIG from '../../CONFIG'; import ROUTES from '../../ROUTES'; -import {printPerformanceMetrics} from '../Performance'; -import canCapturePerformanceMetrics from '../canCapturePerformanceMetrics'; +import {markEnd, markStart} from '../Performance'; import Timing from './Timing'; let currentUserAccountID; @@ -61,15 +60,8 @@ function setSidebarLoaded() { Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true); Timing.end(CONST.TIMING.SIDEBAR_LOADED); - - if (!canCapturePerformanceMetrics()) { - return; - } - - const performance = require('react-native-performance').default; - performance.mark('sidebarLoadEnd'); - performance.measure('timeToInteractive', 'nativeLaunchStart', 'sidebarLoadEnd'); - printPerformanceMetrics(); + markEnd(CONST.TIMING.SIDEBAR_LOADED); + markStart(CONST.TIMING.SWITCH_REPORT); } let appState; diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index bdbe22ed467b..31a26c826768 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -36,6 +36,7 @@ import {contextMenuRef} from './ContextMenu/ReportActionContextMenu'; import PopoverReportActionContextMenu from './ContextMenu/PopoverReportActionContextMenu'; import variables from '../../../styles/variables'; import MarkerBadge from './MarkerBadge'; +import {markEnd, markStart, withRenderTrace} from '../../../libs/Performance'; const propTypes = { /** The ID of the report actions will be created for */ @@ -86,6 +87,7 @@ class ReportActionsView extends React.Component { constructor(props) { super(props); + markStart('ReportActionsView Mounting'); this.renderItem = this.renderItem.bind(this); this.renderCell = this.renderCell.bind(this); this.scrollToListBottom = this.scrollToListBottom.bind(this); @@ -137,6 +139,8 @@ class ReportActionsView extends React.Component { setNewMarkerPosition(this.props.reportID, oldestUnreadSequenceNumber); fetchActions(this.props.reportID); + + markEnd('ReportActionsView Mounting'); } shouldComponentUpdate(nextProps, nextState) { @@ -405,6 +409,7 @@ class ReportActionsView extends React.Component { this.didLayout = true; Timing.end(CONST.TIMING.SWITCH_REPORT, CONST.TIMING.COLD); + markEnd(CONST.TIMING.SWITCH_REPORT); } /** @@ -510,6 +515,7 @@ ReportActionsView.propTypes = propTypes; ReportActionsView.defaultProps = defaultProps; export default compose( + withRenderTrace({id: ' rendering'}), withWindowDimensions, withDrawerState, withLocalize, diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index e74800d3a71a..4c807f72794d 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -21,6 +21,7 @@ import {participantPropTypes} from './optionPropTypes'; import themeColors from '../../../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import * as App from '../../../libs/actions/App'; +import {markStart} from '../../../libs/Performance'; const propTypes = { /** Toggles the navigation menu open and closed */ @@ -171,6 +172,7 @@ class SidebarLinks extends React.Component { option => option.reportID === activeReportID ))} onSelectRow={(option) => { + markStart(CONST.TIMING.SWITCH_REPORT, option); Navigation.navigate(ROUTES.getReportRoute(option.reportID)); this.props.onLinkClick(); }} diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js index faa88d72d24b..5cfacdea0f93 100755 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -24,6 +24,7 @@ import { import Permissions from '../../../libs/Permissions'; import ONYXKEYS from '../../../ONYXKEYS'; import {create} from '../../../libs/actions/Policy'; +import {markStart} from '../../../libs/Performance'; const propTypes = { /** Beta features list */ @@ -49,6 +50,7 @@ class SidebarScreen extends Component { } componentDidMount() { + markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED, true); } From 9bceb08fd7f8fc1fdbc1dd07a5f1fd734ad16a30 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 00:07:06 +0300 Subject: [PATCH 05/18] Update onyx hash to see more data on the perf timeline --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b197eca71118..9682a1dad063 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36098,8 +36098,8 @@ } }, "react-native-onyx": { - "version": "git+https://github.com/Expensify/react-native-onyx.git#f3b24dd2c947bb4b9b60dc3718dc170f93f751f0", - "from": "git+https://github.com/Expensify/react-native-onyx.git#f3b24dd2c947bb4b9b60dc3718dc170f93f751f0", + "version": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", + "from": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", "requires": { "ascii-table": "0.0.9", "expensify-common": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", diff --git a/package.json b/package.json index 78daca60fa8b..123ebb40894f 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "react-native-image-picker": "^4.0.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-modal": "^11.10.0", - "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#f3b24dd2c947bb4b9b60dc3718dc170f93f751f0", + "react-native-onyx": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", "react-native-pdf": "^6.2.2", "react-native-performance": "^2.0.0", "react-native-permissions": "^3.0.1", From acc391f83ab930f32034c801b22005ceeaa670d5 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 00:14:55 +0300 Subject: [PATCH 06/18] Alert only key metrics Onyx captures a lot of measures. We can inspect those on the Flipper Timeline --- src/libs/Performance.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libs/Performance.js b/src/libs/Performance.js index 27ca7ba4bb07..17b55f7a6c42 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -64,8 +64,13 @@ if (canCapturePerformanceMetrics()) { } if (entry.name === 'downloadEnd') { performance.measure('jsBundleDownload', 'downloadStart', 'downloadEnd'); + } + if (entry.name === 'runJsBundleEnd') { + performance.measure('runJsBundle', 'runJsBundleStart', 'runJsBundleEnd'); + } - // We don't need to keep the observer past this point + // We don't need to keep the observer past this point + if (entry.name === 'runJsBundleEnd' || entry.name === 'downloadEnd') { observer.disconnect(); } }); @@ -93,9 +98,14 @@ if (canCapturePerformanceMetrics()) { * Outputs performance stats. We alert these so that they are easy to access in release builds. */ printPerformanceMetrics = () => { - const stats = [...performance.getEntriesByType('measure'), ...performance.getEntriesByType('react-native-mark')] + const stats = [ + ...performance.getEntriesByName('nativeLaunch'), + ...performance.getEntriesByName('runJsBundle'), + ...performance.getEntriesByName('jsBundleDownload'), + ...performance.getEntriesByName('TTI'), + ] .filter(entry => entry.duration > 0) - .map(entry => `\u2022 ${entry.name}: ${entry.value || entry.duration.toFixed(1)}ms`); + .map(entry => `\u2022 ${entry.name}: ${entry.duration.toFixed(1)}ms`); Alert.alert('Performance', stats.join('\n')); }; From 9a9dc38fe5b02ace2f3ef8834eb87b7d4dd0b349 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 00:18:49 +0300 Subject: [PATCH 07/18] Allow using the in release Update the metro configuration so that when `CAPTURE_METRICS` is `true` we pass aliases to allow capturing data with the Profiler component Otherwise React would just skip capturing metrics in release --- babel.config.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/babel.config.js b/babel.config.js index 5b2fad594735..2d43f1d54e14 100644 --- a/babel.config.js +++ b/babel.config.js @@ -34,6 +34,26 @@ const metro = { ], }; +const {CAPTURE_METRICS} = require('dotenv').config().parsed; + +/* When the CAPTURE_METRICS env var is set we add these aliases to also capture + * React.Profiler metrics for release builds */ +if (CAPTURE_METRICS) { + const path = require('path'); + const profilingRenderer = path.resolve( + __dirname, + './node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-profiling', + ); + + metro.plugins.push(['module-resolver', { + root: ['./'], + alias: { + 'ReactNativeRenderer-prod': profilingRenderer, + 'scheduler/tracing': 'scheduler/tracing-profiling', + }, + }]); +} + module.exports = ({caller}) => { // For `react-native` (iOS/Android) caller will be "metro" // For `webpack` (Web) caller will be "@babel-loader" From ff179e4230665db4deedb96d7e8ca2ea6cb63cd0 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 10:05:15 +0300 Subject: [PATCH 08/18] Update docs in libs/Performance --- src/libs/Performance.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/Performance.js b/src/libs/Performance.js index 17b55f7a6c42..c498e7d9276f 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -76,6 +76,7 @@ if (canCapturePerformanceMetrics()) { }); }).observe({type: 'react-native-mark', buffered: true}); + // Monitor for "_end" marks and capture "_start" to "_end" measures new perfModule.PerformanceObserver((list) => { list.getEntriesByType('mark') .forEach((mark) => { @@ -86,6 +87,7 @@ if (canCapturePerformanceMetrics()) { performance.measure(name, start, end); } + // Capture any custom measures or metrics below if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) { performance.measure('TTI', 'nativeLaunchStart', mark.name); printPerformanceMetrics(); @@ -111,7 +113,7 @@ if (canCapturePerformanceMetrics()) { }; /** - * Adds a start mark to the performance entries + * Add a start mark to the performance entries * @param {string} name * @param {Object} [detail] * @returns {PerformanceMark} @@ -119,8 +121,8 @@ if (canCapturePerformanceMetrics()) { markStart = (name, detail) => performance.mark(`${name}_start`, {detail}); /** - * Adds an end mark to the performance entries - * This would also automatically create a measure between start and end + * Add an end mark to the performance entries + * A measure between start and end is captured automatically * @param {string} name * @param {Object} [detail] * @returns {PerformanceMark} From 6e20694617a34d7c3e8808c96a544977a3e7b3a8 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 10:21:09 +0300 Subject: [PATCH 09/18] Update Podfile.lock after flipper updates --- ios/Podfile.lock | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3f434d1f589b..a9758020438c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -105,6 +105,8 @@ PODS: - OpenSSL-Universal (= 1.1.180) - Flipper-Glog (0.3.6) - Flipper-PeerTalk (0.0.4) + - flipper-plugin-react-native-performance (0.6.0): + - React - Flipper-RSocket (1.3.1): - Flipper-Folly (~> 2.5) - FlipperKit (0.75.1): @@ -412,7 +414,7 @@ PODS: - React-Core - react-native-document-picker (5.1.0): - React-Core - - react-native-flipper (0.100.0): + - react-native-flipper (0.103.0): - React-Core - react-native-image-picker (4.0.3): - React-Core @@ -598,6 +600,7 @@ DEPENDENCIES: - Flipper-Folly (~> 2.5.3) - Flipper-Glog (= 0.3.6) - Flipper-PeerTalk (~> 0.0.4) + - flipper-plugin-react-native-performance (from `../node_modules/flipper-plugin-react-native-performance/ios`) - Flipper-RSocket (~> 1.3) - FlipperKit (~> 0.75.1) - FlipperKit/Core (~> 0.75.1) @@ -731,6 +734,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/FBLazyVector" FBReactNativeSpec: :path: "../node_modules/react-native/React/FBReactNativeSpec" + flipper-plugin-react-native-performance: + :path: "../node_modules/flipper-plugin-react-native-performance/ios" glog: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" onfido-react-native-sdk: @@ -877,7 +882,7 @@ SPEC CHECKSUMS: DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de EXHaptics: 337c160c148baa6f0e7166249f368965906e346b FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53 - FBReactNativeSpec: 884d4cc2b011759361797a4035c47e10099393b5 + FBReactNativeSpec: d2bbf7ed8374a5ef7e82afdce19c4e50731f1f0e Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f FirebaseABTesting: 4cb61aeeb50f60680af1c01fff781dfaf9293916 FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9 @@ -892,6 +897,7 @@ SPEC CHECKSUMS: Flipper-Folly: 755929a4f851b2fb2c347d533a23f191b008554c Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + flipper-plugin-react-native-performance: c639bbaf0e0444bab8eeb86dad93651c2e13291e Flipper-RSocket: 127954abe8b162fcaf68d2134d34dc2bd7076154 FlipperKit: 8a20b5c5fcf9436cac58551dc049867247f64b00 glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 @@ -923,7 +929,7 @@ SPEC CHECKSUMS: React-jsinspector: 500a59626037be5b3b3d89c5151bc3baa9abf1a9 react-native-config: d8b45133fd13d4f23bd2064b72f6e2c08b2763ed react-native-document-picker: 0e3602a4064da040321bafad6848d8b0edcb1d55 - react-native-flipper: 1943b82f2e494c77b741eb1ed257b6734a334b83 + react-native-flipper: 169e8ba429b73ad637ce007337ce4b415e783799 react-native-image-picker: 4089335b89b625d4e34d53fb249c48a7a791b3ea react-native-netinfo: 52cf0ee8342548a485e28f4b09e56b477567244d react-native-pdf: 4b5a9e4465a6a3b399e91dc4838eb44ddf716d1f From fdd1ee157d0591e0c6239b31f3ddaf26427ae486 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Fri, 20 Aug 2021 10:58:16 +0300 Subject: [PATCH 10/18] Update babel.config ENV handling Usages through `process.env` cover missing .env files --- babel.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/babel.config.js b/babel.config.js index 2d43f1d54e14..ab54aad74e6b 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,5 @@ +require('dotenv').config(); + const defaultPresets = ['@babel/preset-react', '@babel/preset-env', '@babel/preset-flow']; const defaultPlugins = [ // Adding the commonjs: true option to react-native-web plugin can cause styling conflicts @@ -34,11 +36,9 @@ const metro = { ], }; -const {CAPTURE_METRICS} = require('dotenv').config().parsed; - -/* When the CAPTURE_METRICS env var is set we add these aliases to also capture +/* When CAPTURE_METRICS is set we add these aliases to also capture * React.Profiler metrics for release builds */ -if (CAPTURE_METRICS) { +if (process.env.CAPTURE_METRICS) { const path = require('path'); const profilingRenderer = path.resolve( __dirname, From 78373e25be3dd22d4d8bf4381d58328757eb0f1d Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 18:50:57 +0300 Subject: [PATCH 11/18] Performance: add exception handling to cover errors due to missing start marks --- src/libs/Performance.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libs/Performance.js b/src/libs/Performance.js index c498e7d9276f..c70d98d0bba2 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -80,17 +80,22 @@ if (canCapturePerformanceMetrics()) { new perfModule.PerformanceObserver((list) => { list.getEntriesByType('mark') .forEach((mark) => { - if (mark.name.endsWith('_end')) { - const end = mark.name; - const name = end.replace(/_end$/, ''); - const start = `${name}_start`; - performance.measure(name, start, end); - } - - // Capture any custom measures or metrics below - if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) { - performance.measure('TTI', 'nativeLaunchStart', mark.name); - printPerformanceMetrics(); + try { + if (mark.name.endsWith('_end')) { + const end = mark.name; + const name = end.replace(/_end$/, ''); + const start = `${name}_start`; + performance.measure(name, start, end); + } + + // Capture any custom measures or metrics below + if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) { + performance.measure('TTI', 'nativeLaunchStart', mark.name); + printPerformanceMetrics(); + } + } catch (error) { + // Sometimes there might be no start mark recorded and the measure will fail with an error + console.debug(error.message); } }); }).observe({type: 'mark', buffered: true}); From 87dc0fded2e98d74c3e5098c83591c5299f5b25f Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 18:54:12 +0300 Subject: [PATCH 12/18] Measure initial report render and report switches separately --- src/CONST.js | 1 + src/libs/actions/App.js | 2 +- src/pages/home/report/ReportActionsView.js | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 481f6a6b7721..f055faaa97e6 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -198,6 +198,7 @@ const CONST = { TIMING: { SEARCH_RENDER: 'search_render', HOMEPAGE_INITIAL_RENDER: 'homepage_initial_render', + REPORT_INITIAL_RENDER: 'report_initial_render', HOMEPAGE_REPORTS_LOADED: 'homepage_reports_loaded', SWITCH_REPORT: 'switch_report', SIDEBAR_LOADED: 'sidebar_loaded', diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 63cc1d86072a..9c9368af5c83 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -61,7 +61,7 @@ function setSidebarLoaded() { Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true); Timing.end(CONST.TIMING.SIDEBAR_LOADED); markEnd(CONST.TIMING.SIDEBAR_LOADED); - markStart(CONST.TIMING.SWITCH_REPORT); + markStart(CONST.TIMING.REPORT_INITIAL_RENDER); } let appState; diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 31a26c826768..fcea0070b001 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -409,7 +409,14 @@ class ReportActionsView extends React.Component { this.didLayout = true; Timing.end(CONST.TIMING.SWITCH_REPORT, CONST.TIMING.COLD); - markEnd(CONST.TIMING.SWITCH_REPORT); + + // Capture the init measurement only once not per each chat switch as the value gets overwritten + if (!ReportActionsView.initMeasured) { + markEnd(CONST.TIMING.REPORT_INITIAL_RENDER); + ReportActionsView.initMeasured = true; + } else { + markEnd(CONST.TIMING.SWITCH_REPORT); + } } /** From a5e623a8124093d6884bd4424abc67f27b3edecd Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 19:11:46 +0300 Subject: [PATCH 13/18] Add ONYX_METRICS to .env and expose checks --- .env.example | 1 + src/CONFIG.js | 1 + src/components/OnyxProvider.js | 4 ++-- src/libs/Performance.js | 2 +- src/libs/canCaptureMetrics/index.js | 6 ++++++ src/libs/canCaptureMetrics/index.native.js | 15 +++++++++++++++ src/libs/canCapturePerformanceMetrics/index.js | 2 -- .../canCapturePerformanceMetrics/index.native.js | 10 ---------- 8 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 src/libs/canCaptureMetrics/index.js create mode 100644 src/libs/canCaptureMetrics/index.native.js delete mode 100644 src/libs/canCapturePerformanceMetrics/index.js delete mode 100644 src/libs/canCapturePerformanceMetrics/index.native.js diff --git a/.env.example b/.env.example index d884526f76df..127b7147230c 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,4 @@ USE_NGROK=false USE_WEB_PROXY=false USE_WDYR=false CAPTURE_METRICS=false +ONYX_METRICS=false diff --git a/src/CONFIG.js b/src/CONFIG.js index 2b6a840690b6..dcbf3d8ea4fe 100644 --- a/src/CONFIG.js +++ b/src/CONFIG.js @@ -61,4 +61,5 @@ export default { UNREAD: '/favicon-unread.png', }, CAPTURE_METRICS: lodashGet(Config, 'CAPTURE_METRICS', false), + ONYX_METRICS: lodashGet(Config, 'ONYX_METRICS', false), }; diff --git a/src/components/OnyxProvider.js b/src/components/OnyxProvider.js index 504e5c6f320b..0c20ce2cdc66 100644 --- a/src/components/OnyxProvider.js +++ b/src/components/OnyxProvider.js @@ -7,13 +7,13 @@ import ComposeProviders from './ComposeProviders'; import CONST from '../CONST'; import Log from '../libs/Log'; import listenToStorageEvents from '../libs/listenToStorageEvents'; -import canCapturePerformanceMetrics from '../libs/canCapturePerformanceMetrics'; +import {canCaptureOnyxMetrics} from '../libs/canCaptureMetrics'; // Initialize the store when the app loads for the first time Onyx.init({ keys: ONYXKEYS, safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], - captureMetrics: canCapturePerformanceMetrics(), + captureMetrics: canCaptureOnyxMetrics(), initialKeyStates: { // Clear any loading and error messages so they do not appear on app startup diff --git a/src/libs/Performance.js b/src/libs/Performance.js index c70d98d0bba2..3d0cacdd8c83 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -3,7 +3,7 @@ import lodashTransform from 'lodash/transform'; import React, {Profiler, forwardRef} from 'react'; import {Alert} from 'react-native'; -import canCapturePerformanceMetrics from './canCapturePerformanceMetrics'; +import {canCapturePerformanceMetrics} from './canCaptureMetrics'; import getComponentDisplayName from './getComponentDisplayName'; import CONST from '../CONST'; diff --git a/src/libs/canCaptureMetrics/index.js b/src/libs/canCaptureMetrics/index.js new file mode 100644 index 000000000000..420dd2898aee --- /dev/null +++ b/src/libs/canCaptureMetrics/index.js @@ -0,0 +1,6 @@ +import CONFIG from '../../CONFIG'; + +// We don't capture performance metrics on web as there are enough tools available +export const canCapturePerformanceMetrics = () => false; + +export const canCaptureOnyxMetrics = () => Boolean(CONFIG.ONYX_METRICS); diff --git a/src/libs/canCaptureMetrics/index.native.js b/src/libs/canCaptureMetrics/index.native.js new file mode 100644 index 000000000000..30c839b1b231 --- /dev/null +++ b/src/libs/canCaptureMetrics/index.native.js @@ -0,0 +1,15 @@ +import CONFIG from '../../CONFIG'; + +/** + * Is capturing performance stats enabled. + * + * @returns {Boolean} + */ +export const canCapturePerformanceMetrics = () => Boolean(CONFIG.CAPTURE_METRICS); + +/** + * Is capturing Onyx stats enabled. + * + * @returns {Boolean} + */ +export const canCaptureOnyxMetrics = () => Boolean(CONFIG.ONYX_METRICS); diff --git a/src/libs/canCapturePerformanceMetrics/index.js b/src/libs/canCapturePerformanceMetrics/index.js deleted file mode 100644 index 1dc112f46dec..000000000000 --- a/src/libs/canCapturePerformanceMetrics/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// We don't capture performance metrics on web as there are enough tools available -export default () => false; diff --git a/src/libs/canCapturePerformanceMetrics/index.native.js b/src/libs/canCapturePerformanceMetrics/index.native.js deleted file mode 100644 index a324c28a607c..000000000000 --- a/src/libs/canCapturePerformanceMetrics/index.native.js +++ /dev/null @@ -1,10 +0,0 @@ -import CONFIG from '../../CONFIG'; - -/** - * Enables capturing performance stats. - * - * @returns {Boolean} - */ -export default function canCapturePerformanceMetrics() { - return Boolean(CONFIG.CAPTURE_METRICS); -} From f2b27a3b69e2eeab828250cc1f86bf318f08cc3b Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 19:12:29 +0300 Subject: [PATCH 14/18] Better document babel.config aliasing change --- babel.config.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/babel.config.js b/babel.config.js index ab54aad74e6b..b10d59fa8934 100644 --- a/babel.config.js +++ b/babel.config.js @@ -36,8 +36,11 @@ const metro = { ], }; -/* When CAPTURE_METRICS is set we add these aliases to also capture - * React.Profiler metrics for release builds */ +/* + * We use Flipper, and react-native-performance to capture/monitor stats + * By default is disabled in production as it adds small overhead + * When CAPTURE_METRICS is set we're explicitly saying that we want to capture metrics + * To enable the for release builds we add these aliases */ if (process.env.CAPTURE_METRICS) { const path = require('path'); const profilingRenderer = path.resolve( From 36c62c2425b3fb71a0edbae23eaeaba256fa3855 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 19:13:10 +0300 Subject: [PATCH 15/18] Remove `mount` time measures from ReportActionsView - covered by withRenderTrace --- src/pages/home/report/ReportActionsView.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index fcea0070b001..8a952c6d6ef4 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -36,7 +36,7 @@ import {contextMenuRef} from './ContextMenu/ReportActionContextMenu'; import PopoverReportActionContextMenu from './ContextMenu/PopoverReportActionContextMenu'; import variables from '../../../styles/variables'; import MarkerBadge from './MarkerBadge'; -import {markEnd, markStart, withRenderTrace} from '../../../libs/Performance'; +import {markEnd, withRenderTrace} from '../../../libs/Performance'; const propTypes = { /** The ID of the report actions will be created for */ @@ -87,7 +87,6 @@ class ReportActionsView extends React.Component { constructor(props) { super(props); - markStart('ReportActionsView Mounting'); this.renderItem = this.renderItem.bind(this); this.renderCell = this.renderCell.bind(this); this.scrollToListBottom = this.scrollToListBottom.bind(this); @@ -139,8 +138,6 @@ class ReportActionsView extends React.Component { setNewMarkerPosition(this.props.reportID, oldestUnreadSequenceNumber); fetchActions(this.props.reportID); - - markEnd('ReportActionsView Mounting'); } shouldComponentUpdate(nextProps, nextState) { From 4427cd9432a26218187e0cbf05f260323ebc01fe Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 19:18:52 +0300 Subject: [PATCH 16/18] Move mark capture to SidebarScreen.js --- src/pages/home/sidebar/SidebarLinks.js | 2 -- src/pages/home/sidebar/SidebarScreen.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 4c807f72794d..e74800d3a71a 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -21,7 +21,6 @@ import {participantPropTypes} from './optionPropTypes'; import themeColors from '../../../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import * as App from '../../../libs/actions/App'; -import {markStart} from '../../../libs/Performance'; const propTypes = { /** Toggles the navigation menu open and closed */ @@ -172,7 +171,6 @@ class SidebarLinks extends React.Component { option => option.reportID === activeReportID ))} onSelectRow={(option) => { - markStart(CONST.TIMING.SWITCH_REPORT, option); Navigation.navigate(ROUTES.getReportRoute(option.reportID)); this.props.onLinkClick(); }} diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js index 5cfacdea0f93..add39dc30099 100755 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -86,6 +86,7 @@ class SidebarScreen extends Component { */ startTimer() { Timing.start(CONST.TIMING.SWITCH_REPORT); + markStart(CONST.TIMING.SWITCH_REPORT); } render() { From 9ea80ba768f87b9be857ae574e39bb2019334cd3 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Mon, 23 Aug 2021 19:34:51 +0300 Subject: [PATCH 17/18] Convert Performance to single default export --- src/libs/Performance.js | 69 ++++++++++------------ src/libs/actions/App.js | 6 +- src/pages/home/report/ReportActionsView.js | 8 +-- src/pages/home/sidebar/SidebarScreen.js | 6 +- src/setup/index.native.js | 4 +- 5 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/libs/Performance.js b/src/libs/Performance.js index 3d0cacdd8c83..7eb5166eb2fa 100644 --- a/src/libs/Performance.js +++ b/src/libs/Performance.js @@ -8,7 +8,7 @@ import getComponentDisplayName from './getComponentDisplayName'; import CONST from '../CONST'; /** @type {import('react-native-performance').Performance} */ -let performance; +let rnPerformance; /** * Deep diff between two objects. Useful for figuring out what changed about an object from one render to the next so @@ -33,40 +33,41 @@ function diffObject(object, base) { return changes(object, base); } -// When performance monitoring is disabled the implementations are blank -/* eslint-disable import/no-mutable-exports */ -let setupPerformanceObserver = () => {}; -let printPerformanceMetrics = () => {}; -let markStart = () => {}; -let markEnd = () => {}; -let traceRender = () => {}; -let withRenderTrace = () => Component => Component; -/* eslint-enable import/no-mutable-exports */ +const Performance = { + // When performance monitoring is disabled the implementations are blank + diffObject, + setupPerformanceObserver: () => {}, + printPerformanceMetrics: () => {}, + markStart: () => {}, + markEnd: () => {}, + traceRender: () => {}, + withRenderTrace: () => Component => Component, +}; if (canCapturePerformanceMetrics()) { /** * Sets up an observer to capture events recorded in the native layer before the app fully initializes. */ - setupPerformanceObserver = () => { + Performance.setupPerformanceObserver = () => { const performanceReported = require('react-native-performance-flipper-reporter'); performanceReported.setupDefaultFlipperReporter(); const perfModule = require('react-native-performance'); perfModule.setResourceLoggingEnabled(true); - performance = perfModule.default; + rnPerformance = perfModule.default; // Monitor some native marks that we want to put on the timeline new perfModule.PerformanceObserver((list, observer) => { list.getEntries() .forEach((entry) => { if (entry.name === 'nativeLaunchEnd') { - performance.measure('nativeLaunch', 'nativeLaunchStart', 'nativeLaunchEnd'); + rnPerformance.measure('nativeLaunch', 'nativeLaunchStart', 'nativeLaunchEnd'); } if (entry.name === 'downloadEnd') { - performance.measure('jsBundleDownload', 'downloadStart', 'downloadEnd'); + rnPerformance.measure('jsBundleDownload', 'downloadStart', 'downloadEnd'); } if (entry.name === 'runJsBundleEnd') { - performance.measure('runJsBundle', 'runJsBundleStart', 'runJsBundleEnd'); + rnPerformance.measure('runJsBundle', 'runJsBundleStart', 'runJsBundleEnd'); } // We don't need to keep the observer past this point @@ -85,13 +86,13 @@ if (canCapturePerformanceMetrics()) { const end = mark.name; const name = end.replace(/_end$/, ''); const start = `${name}_start`; - performance.measure(name, start, end); + rnPerformance.measure(name, start, end); } // Capture any custom measures or metrics below if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) { - performance.measure('TTI', 'nativeLaunchStart', mark.name); - printPerformanceMetrics(); + rnPerformance.measure('TTI', 'nativeLaunchStart', mark.name); + Performance.printPerformanceMetrics(); } } catch (error) { // Sometimes there might be no start mark recorded and the measure will fail with an error @@ -104,12 +105,12 @@ if (canCapturePerformanceMetrics()) { /** * Outputs performance stats. We alert these so that they are easy to access in release builds. */ - printPerformanceMetrics = () => { + Performance.printPerformanceMetrics = () => { const stats = [ - ...performance.getEntriesByName('nativeLaunch'), - ...performance.getEntriesByName('runJsBundle'), - ...performance.getEntriesByName('jsBundleDownload'), - ...performance.getEntriesByName('TTI'), + ...rnPerformance.getEntriesByName('nativeLaunch'), + ...rnPerformance.getEntriesByName('runJsBundle'), + ...rnPerformance.getEntriesByName('jsBundleDownload'), + ...rnPerformance.getEntriesByName('TTI'), ] .filter(entry => entry.duration > 0) .map(entry => `\u2022 ${entry.name}: ${entry.duration.toFixed(1)}ms`); @@ -123,7 +124,7 @@ if (canCapturePerformanceMetrics()) { * @param {Object} [detail] * @returns {PerformanceMark} */ - markStart = (name, detail) => performance.mark(`${name}_start`, {detail}); + Performance.markStart = (name, detail) => rnPerformance.mark(`${name}_start`, {detail}); /** * Add an end mark to the performance entries @@ -132,7 +133,7 @@ if (canCapturePerformanceMetrics()) { * @param {Object} [detail] * @returns {PerformanceMark} */ - markEnd = (name, detail) => performance.mark(`${name}_end`, {detail}); + Performance.markEnd = (name, detail) => rnPerformance.mark(`${name}_end`, {detail}); /** * Put data emitted by Profiler components on the timeline @@ -145,7 +146,7 @@ if (canCapturePerformanceMetrics()) { * @param {Set} interactions the Set of interactions belonging to this update * @returns {PerformanceMeasure} */ - traceRender = ( + Performance.traceRender = ( id, phase, actualDuration, @@ -153,7 +154,7 @@ if (canCapturePerformanceMetrics()) { startTime, commitTime, interactions, - ) => performance.measure(id, { + ) => rnPerformance.measure(id, { start: startTime, duration: actualDuration, detail: { @@ -170,9 +171,9 @@ if (canCapturePerformanceMetrics()) { * @param {string} config.id * @returns {function(React.Component): React.FunctionComponent} */ - withRenderTrace = ({id}) => (WrappedComponent) => { + Performance.withRenderTrace = ({id}) => (WrappedComponent) => { const WithRenderTrace = forwardRef((props, ref) => ( - + {/* eslint-disable-next-line react/jsx-props-no-spreading */} @@ -183,12 +184,4 @@ if (canCapturePerformanceMetrics()) { }; } -export { - diffObject, - printPerformanceMetrics, - setupPerformanceObserver, - markStart, - markEnd, - traceRender, - withRenderTrace, -}; +export default Performance; diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 9c9368af5c83..e6a4ffb7cd5c 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -7,7 +7,7 @@ import CONST from '../../CONST'; import Log from '../Log'; import CONFIG from '../../CONFIG'; import ROUTES from '../../ROUTES'; -import {markEnd, markStart} from '../Performance'; +import Performance from '../Performance'; import Timing from './Timing'; let currentUserAccountID; @@ -60,8 +60,8 @@ function setSidebarLoaded() { Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true); Timing.end(CONST.TIMING.SIDEBAR_LOADED); - markEnd(CONST.TIMING.SIDEBAR_LOADED); - markStart(CONST.TIMING.REPORT_INITIAL_RENDER); + Performance.markEnd(CONST.TIMING.SIDEBAR_LOADED); + Performance.markStart(CONST.TIMING.REPORT_INITIAL_RENDER); } let appState; diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 8a952c6d6ef4..548be1289c00 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -36,7 +36,7 @@ import {contextMenuRef} from './ContextMenu/ReportActionContextMenu'; import PopoverReportActionContextMenu from './ContextMenu/PopoverReportActionContextMenu'; import variables from '../../../styles/variables'; import MarkerBadge from './MarkerBadge'; -import {markEnd, withRenderTrace} from '../../../libs/Performance'; +import Performance from '../../../libs/Performance'; const propTypes = { /** The ID of the report actions will be created for */ @@ -409,10 +409,10 @@ class ReportActionsView extends React.Component { // Capture the init measurement only once not per each chat switch as the value gets overwritten if (!ReportActionsView.initMeasured) { - markEnd(CONST.TIMING.REPORT_INITIAL_RENDER); + Performance.markEnd(CONST.TIMING.REPORT_INITIAL_RENDER); ReportActionsView.initMeasured = true; } else { - markEnd(CONST.TIMING.SWITCH_REPORT); + Performance.markEnd(CONST.TIMING.SWITCH_REPORT); } } @@ -519,7 +519,7 @@ ReportActionsView.propTypes = propTypes; ReportActionsView.defaultProps = defaultProps; export default compose( - withRenderTrace({id: ' rendering'}), + Performance.withRenderTrace({id: ' rendering'}), withWindowDimensions, withDrawerState, withLocalize, diff --git a/src/pages/home/sidebar/SidebarScreen.js b/src/pages/home/sidebar/SidebarScreen.js index add39dc30099..e8cef02b567a 100755 --- a/src/pages/home/sidebar/SidebarScreen.js +++ b/src/pages/home/sidebar/SidebarScreen.js @@ -24,7 +24,7 @@ import { import Permissions from '../../../libs/Permissions'; import ONYXKEYS from '../../../ONYXKEYS'; import {create} from '../../../libs/actions/Policy'; -import {markStart} from '../../../libs/Performance'; +import Performance from '../../../libs/Performance'; const propTypes = { /** Beta features list */ @@ -50,7 +50,7 @@ class SidebarScreen extends Component { } componentDidMount() { - markStart(CONST.TIMING.SIDEBAR_LOADED); + Performance.markStart(CONST.TIMING.SIDEBAR_LOADED); Timing.start(CONST.TIMING.SIDEBAR_LOADED, true); } @@ -86,7 +86,7 @@ class SidebarScreen extends Component { */ startTimer() { Timing.start(CONST.TIMING.SWITCH_REPORT); - markStart(CONST.TIMING.SWITCH_REPORT); + Performance.markStart(CONST.TIMING.SWITCH_REPORT); } render() { diff --git a/src/setup/index.native.js b/src/setup/index.native.js index e2c92bd9108d..20d60f785f03 100644 --- a/src/setup/index.native.js +++ b/src/setup/index.native.js @@ -1,4 +1,4 @@ -import {setupPerformanceObserver} from '../libs/Performance'; +import Performance from '../libs/Performance'; // Setup Flipper plugins when on dev export default function () { @@ -10,5 +10,5 @@ export default function () { RNAsyncStorageFlipper(AsyncStorage); } - setupPerformanceObserver(); + Performance.setupPerformanceObserver(); } From 5201038db27c0d070bc1c03f510e6c0926437473 Mon Sep 17 00:00:00 2001 From: Peter Velkov Date: Tue, 24 Aug 2021 00:02:44 +0300 Subject: [PATCH 18/18] Update Onyx hash - perf metrics --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9682a1dad063..4a6ca8e260e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36098,8 +36098,8 @@ } }, "react-native-onyx": { - "version": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", - "from": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", + "version": "git+https://github.com/Expensify/react-native-onyx.git#6eadd7f73fca87e6551e43607ec78f645e17ef50", + "from": "git+https://github.com/Expensify/react-native-onyx.git#6eadd7f73fca87e6551e43607ec78f645e17ef50", "requires": { "ascii-table": "0.0.9", "expensify-common": "git+https://github.com/Expensify/expensify-common.git#2e5cff552cf132da90a3fb9756e6b4fb6ae7b40c", diff --git a/package.json b/package.json index 123ebb40894f..547b43f8b2cc 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "react-native-image-picker": "^4.0.3", "react-native-keyboard-spacer": "^0.4.1", "react-native-modal": "^11.10.0", - "react-native-onyx": "git+https://github.com/kidroca/react-native-onyx.git#4c17f3d305b26792a252b14b959d112b8d524081", + "react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#6eadd7f73fca87e6551e43607ec78f645e17ef50", "react-native-pdf": "^6.2.2", "react-native-performance": "^2.0.0", "react-native-permissions": "^3.0.1",