Repository: facebook/react
Pull request: [Perf Tracks]: Clear potentially large measures

Fixes https://github.com/facebook/react/issues/34770.

We need to clear measures at some point, otherwise all these copies of props that we end up recording will allocate too much memory in Chromium.  This adds `performance.clearMeasures(...)` calls to such cases in DEV.

Validated that entries are still shown on Performance panel timeline.

Changed files:
filename: packages/react-client/src/ReactFlightPerformanceTrack.js
patch: @@ -102,6 +102,7 @@ export function logComponentRender(
     const entryName =
       isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
     const debugTask = componentInfo.debugTask;
+    const measureName = '\u200b' + entryName;
     if (__DEV__ && debugTask) {
       const properties: Array<[string, string]> = [];
       if (componentInfo.key != null) {
@@ -110,9 +111,10 @@ export function logComponentRender(
       if (componentInfo.props != null) {
         addObjectToProperties(componentInfo.props, properties, 0, '');
       }
+
       debugTask.run(
         // $FlowFixMe[method-unbinding]
-        performance.measure.bind(performance, '\u200b' + entryName, {
+        performance.measure.bind(performance, measureName, {
           start: startTime < 0 ? 0 : startTime,
           end: childrenEndTime,
           detail: {
@@ -125,9 +127,10 @@ export function logComponentRender(
           },
         }),
       );
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
-        '\u200b' + entryName,
+        measureName,
         startTime < 0 ? 0 : startTime,
         childrenEndTime,
         trackNames[trackIdx],
@@ -152,6 +155,7 @@ export function logComponentAborted(
     const isPrimaryEnv = env === rootEnv;
     const entryName =
       isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
+    const measureName = '\u200b' + entryName;
     if (__DEV__) {
       const properties: Array<[string, string]> = [
         [
@@ -165,7 +169,8 @@ export function logComponentAborted(
       if (componentInfo.props != null) {
         addObjectToProperties(componentInfo.props, properties, 0, '');
       }
-      performance.measure('\u200b' + entryName, {
+
+      performance.measure(measureName, {
         start: startTime < 0 ? 0 : startTime,
         end: childrenEndTime,
         detail: {
@@ -178,9 +183,10 @@ export function logComponentAborted(
           },
         },
       });
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
-        entryName,
+        measureName,
         startTime < 0 ? 0 : startTime,
         childrenEndTime,
         trackNames[trackIdx],
@@ -206,6 +212,7 @@ export function logComponentErrored(
     const isPrimaryEnv = env === rootEnv;
     const entryName =
       isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
+    const measureName = '\u200b' + entryName;
     if (__DEV__) {
       const message =
         typeof error === 'object' &&
@@ -222,7 +229,8 @@ export function logComponentErrored(
       if (componentInfo.props != null) {
         addObjectToProperties(componentInfo.props, properties, 0, '');
       }
-      performance.measure('\u200b' + entryName, {
+
+      performance.measure(measureName, {
         start: startTime < 0 ? 0 : startTime,
         end: childrenEndTime,
         detail: {
@@ -235,9 +243,10 @@ export function logComponentErrored(
           },
         },
       });
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
-        entryName,
+        measureName,
         startTime < 0 ? 0 : startTime,
         childrenEndTime,
         trackNames[trackIdx],
@@ -397,6 +406,7 @@ export function logComponentAwaitAborted(
           },
         }),
       );
+      performance.clearMeasures(entryName);
     } else {
       console.timeStamp(
         entryName,
@@ -453,6 +463,7 @@ export function logComponentAwaitErrored(
           },
         }),
       );
+      performance.clearMeasures(entryName);
     } else {
       console.timeStamp(
         entryName,
@@ -514,6 +525,7 @@ export function logComponentAwait(
           },
         }),
       );
+      performance.clearMeasures(entryName);
     } else {
       console.timeStamp(
         entryName,
@@ -538,6 +550,7 @@ export function logIOInfoErrored(
     const description = getIODescription(error);
     const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
     const debugTask = ioInfo.debugTask;
+    const measureName = '\u200b' + entryName;
     if (__DEV__ && debugTask) {
       const message =
         typeof error === 'object' &&
@@ -550,9 +563,10 @@ export function logIOInfoErrored(
       const properties = [['rejected with', message]];
       const tooltipText =
         getIOLongName(ioInfo, description, ioInfo.env, rootEnv) + ' Rejected';
+
       debugTask.run(
         // $FlowFixMe[method-unbinding]
-        performance.measure.bind(performance, '\u200b' + entryName, {
+        performance.measure.bind(performance, measureName, {
           start: startTime < 0 ? 0 : startTime,
           end: endTime,
           detail: {
@@ -565,9 +579,10 @@ export function logIOInfoErrored(
           },
         }),
       );
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
-        entryName,
+        measureName,
         startTime < 0 ? 0 : startTime,
         endTime,
         IO_TRACK,
@@ -590,6 +605,7 @@ export function logIOInfo(
     const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
     const color = getIOColor(entryName);
     const debugTask = ioInfo.debugTask;
+    const measureName = '\u200b' + entryName;
     if (__DEV__ && debugTask) {
       const properties: Array<[string, string]> = [];
       if (typeof value === 'object' && value !== null) {
@@ -605,7 +621,7 @@ export function logIOInfo(
       );
       debugTask.run(
         // $FlowFixMe[method-unbinding]
-        performance.measure.bind(performance, '\u200b' + entryName, {
+        performance.measure.bind(performance, measureName, {
           start: startTime < 0 ? 0 : startTime,
           end: endTime,
           detail: {
@@ -618,9 +634,10 @@ export function logIOInfo(
           },
         }),
       );
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
-        entryName,
+        measureName,
         startTime < 0 ? 0 : startTime,
         endTime,
         IO_TRACK,

filename: packages/react-reconciler/src/ReactFiberPerformanceTrack.js
patch: @@ -133,6 +133,7 @@ function logComponentTrigger(
     } else {
       performance.measure(trigger, reusableComponentOptions);
     }
+    performance.clearMeasures(trigger);
   }
 }
 
@@ -200,7 +201,7 @@ const reusableComponentOptions: PerformanceMeasureOptions = {
   },
 };
 
-const resuableChangedPropsEntry = ['Changed Props', ''];
+const reusableChangedPropsEntry = ['Changed Props', ''];
 
 const DEEP_EQUALITY_WARNING =
   'This component received deeply equal props. It might benefit from useMemo or the React Compiler in its owner.';
@@ -261,7 +262,7 @@ export function logComponentRender(
         alternate.memoizedProps !== props
       ) {
         // If this is an update, we'll diff the props and emit which ones changed.
-        const properties: Array<[string, string]> = [resuableChangedPropsEntry];
+        const properties: Array<[string, string]> = [reusableChangedPropsEntry];
         const isDeeplyEqual = addObjectDiffToProperties(
           alternate.memoizedProps,
           props,
@@ -293,18 +294,20 @@ export function logComponentRender(
           reusableComponentOptions.start = startTime;
           reusableComponentOptions.end = endTime;
 
+          const measureName = '\u200b' + name;
           if (debugTask != null) {
             debugTask.run(
               // $FlowFixMe[method-unbinding]
               performance.measure.bind(
                 performance,
-                '\u200b' + name,
+                measureName,
                 reusableComponentOptions,
               ),
             );
           } else {
-            performance.measure('\u200b' + name, reusableComponentOptions);
+            performance.measure(measureName, reusableComponentOptions);
           }
+          performance.clearMeasures(measureName);
         } else {
           if (debugTask != null) {
             debugTask.run(
@@ -421,14 +424,17 @@ export function logComponentErrored(
           },
         },
       };
+
+      const measureName = '\u200b' + name;
       if (__DEV__ && debugTask) {
         debugTask.run(
           // $FlowFixMe[method-unbinding]
-          performance.measure.bind(performance, '\u200b' + name, options),
+          performance.measure.bind(performance, measureName, options),
         );
       } else {
-        performance.measure('\u200b' + name, options);
+        performance.measure(measureName, options);
       }
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
         name,
@@ -488,14 +494,16 @@ function logComponentEffectErrored(
         },
       };
       const debugTask = fiber._debugTask;
+      const measureName = '\u200b' + name;
       if (debugTask) {
         debugTask.run(
           // $FlowFixMe[method-unbinding]
-          performance.measure.bind(performance, '\u200b' + name, options),
+          performance.measure.bind(performance, measureName, options),
         );
       } else {
-        performance.measure('\u200b' + name, options);
+        performance.measure(measureName, options);
       }
+      performance.clearMeasures(measureName);
     } else {
       console.timeStamp(
         name,
@@ -762,6 +770,7 @@ export function logBlockingStart(
         } else {
           performance.measure(label, measureOptions);
         }
+        performance.clearMeasures(label);
       } else {
         console.timeStamp(
           label,
@@ -867,6 +876,7 @@ export function logGestureStart(
         } else {
           performance.measure(label, measureOptions);
         }
+        performance.clearMeasures(label);
       } else {
         console.timeStamp(
           label,
@@ -1007,6 +1017,7 @@ export function logTransitionStart(
         } else {
           performance.measure(label, measureOptions);
         }
+        performance.clearMeasures(label);
       } else {
         console.timeStamp(
           label,
@@ -1238,6 +1249,7 @@ export function logRecoveredRenderPhase(
       } else {
         performance.measure('Recovered', options);
       }
+      performance.clearMeasures('Recovered');
     } else {
       console.timeStamp(
         'Recovered',
@@ -1449,6 +1461,7 @@ export function logCommitErrored(
       } else {
         performance.measure('Errored', options);
       }
+      performance.clearMeasures('Errored');
     } else {
       console.timeStamp(
         'Errored',