Repository: facebook/react
Pull request: [Fiber] Color Performance Track Entries by Self Time

Stacked on #30983.

This colors each component entry by its self time from light to dark depending on how long it took. If it took longer than a cut off we color it red (the error color).

<img width="435" alt="Screenshot 2024-09-16 at 11 48 15 PM" src="https://github.com/user-attachments/assets/5d0bda83-6205-40e9-bec1-b81db2d48b2d">


Changed files:
filename: packages/react-reconciler/src/ReactFiberCommitWork.js
patch: @@ -109,6 +109,7 @@ import {
   popComponentEffectStart,
   componentEffectStartTime,
   componentEffectEndTime,
+  componentEffectDuration,
 } from './ReactProfilerTimer';
 import {
   logComponentRender,
@@ -608,6 +609,7 @@ function commitLayoutEffectOnFiber(
       finishedWork,
       componentEffectStartTime,
       componentEffectEndTime,
+      componentEffectDuration,
     );
   }
 
@@ -2105,6 +2107,7 @@ function commitMutationEffectsOnFiber(
       finishedWork,
       componentEffectStartTime,
       componentEffectEndTime,
+      componentEffectDuration,
     );
   }
 
@@ -2926,6 +2929,7 @@ function commitPassiveMountOnFiber(
       finishedWork,
       componentEffectStartTime,
       componentEffectEndTime,
+      componentEffectDuration,
     );
   }
 
@@ -3444,6 +3448,7 @@ function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
       finishedWork,
       componentEffectStartTime,
       componentEffectEndTime,
+      componentEffectDuration,
     );
   }
 

filename: packages/react-reconciler/src/ReactFiberPerformanceTrack.js
patch: @@ -38,24 +38,9 @@ const reusableComponentOptions = {
   },
 };
 
-const reusableComponentEffectDevToolDetails = {
-  dataType: 'track-entry',
-  color: 'secondary',
-  track: 'Blocking', // Lane
-  trackGroup: TRACK_GROUP,
-};
-const reusableComponentEffectOptions = {
-  start: -0,
-  end: -0,
-  detail: {
-    devtools: reusableComponentEffectDevToolDetails,
-  },
-};
-
 export function setCurrentTrackFromLanes(lanes: number): void {
-  reusableComponentEffectDevToolDetails.track =
-    reusableComponentDevToolDetails.track =
-      getGroupNameOfHighestPriorityLane(lanes);
+  reusableComponentDevToolDetails.track =
+    getGroupNameOfHighestPriorityLane(lanes);
 }
 
 export function logComponentRender(
@@ -69,6 +54,20 @@ export function logComponentRender(
     return;
   }
   if (supportsUserTiming) {
+    let selfTime: number = (fiber.actualDuration: any);
+    if (fiber.alternate === null || fiber.alternate.child !== fiber.child) {
+      for (let child = fiber.child; child !== null; child = child.sibling) {
+        selfTime -= (child.actualDuration: any);
+      }
+    }
+    reusableComponentDevToolDetails.color =
+      selfTime < 0.5
+        ? 'primary-light'
+        : selfTime < 10
+          ? 'primary'
+          : selfTime < 100
+            ? 'primary-dark'
+            : 'error';
     reusableComponentOptions.start = startTime;
     reusableComponentOptions.end = endTime;
     performance.measure(name, reusableComponentOptions);
@@ -79,15 +78,24 @@ export function logComponentEffect(
   fiber: Fiber,
   startTime: number,
   endTime: number,
+  selfTime: number,
 ): void {
   const name = getComponentNameFromFiber(fiber);
   if (name === null) {
     // Skip
     return;
   }
   if (supportsUserTiming) {
-    reusableComponentEffectOptions.start = startTime;
-    reusableComponentEffectOptions.end = endTime;
-    performance.measure(name, reusableComponentEffectOptions);
+    reusableComponentDevToolDetails.color =
+      selfTime < 1
+        ? 'secondary-light'
+        : selfTime < 100
+          ? 'secondary'
+          : selfTime < 500
+            ? 'secondary-dark'
+            : 'error';
+    reusableComponentOptions.start = startTime;
+    reusableComponentOptions.end = endTime;
+    performance.measure(name, reusableComponentOptions);
   }
 }

filename: packages/react-reconciler/src/ReactProfilerTimer.js
patch: @@ -25,6 +25,7 @@ export let completeTime: number = -0;
 export let commitTime: number = -0;
 export let profilerStartTime: number = -1.1;
 export let profilerEffectDuration: number = -0;
+export let componentEffectDuration: number = -0;
 export let componentEffectStartTime: number = -1.1;
 export let componentEffectEndTime: number = -1.1;
 
@@ -72,6 +73,7 @@ export function pushComponentEffectStart(): number {
   }
   const prevEffectStart = componentEffectStartTime;
   componentEffectStartTime = -1.1; // Track the next start.
+  componentEffectDuration = -0; // Reset component level duration.
   return prevEffectStart;
 }
 
@@ -211,6 +213,7 @@ export function recordEffectDuration(fiber: Fiber): void {
     // Store duration on the next nearest Profiler ancestor
     // Or the root (for the DevTools Profiler to read)
     profilerEffectDuration += elapsedTime;
+    componentEffectDuration += elapsedTime;
 
     // Keep track of the last end time of the effects.
     componentEffectEndTime = endTime;