Skip to content

Commit

Permalink
Add commit and post-commit durations to DevTools Profiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Mar 11, 2021
1 parent 7642d82 commit 1b7ef51
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 15 deletions.
46 changes: 46 additions & 0 deletions packages/react-devtools-shared/src/backend/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,25 @@ export function attach(
// Checking root.memoizedInteractions handles multi-renderer edge-case-
// where some v16 renderers support profiling and others don't.
if (isProfiling && root.memoizedInteractions != null) {
// Profiling durations are only available for certain builds.
// If available, they'll be stored on the HostRoot.
let effectDuration = null;
let passiveEffectDuration = null;
const hostRoot = root.current;
if (hostRoot != null) {
const stateNode = hostRoot.stateNode;
if (stateNode != null) {
effectDuration =
stateNode.effectDuration != null
? stateNode.effectDuration
: null;
passiveEffectDuration =
stateNode.passiveEffectDuration != null
? stateNode.passiveEffectDuration
: null;
}
}

// If profiling is active, store commit time and duration, and the current interactions.
// The frontend may request this information after profiling has stopped.
currentCommitProfilingMetadata = {
Expand All @@ -2111,6 +2130,8 @@ export function attach(
),
maxActualDuration: 0,
priorityLevel: null,
effectDuration,
passiveEffectDuration,
};
}

Expand Down Expand Up @@ -2149,6 +2170,23 @@ export function attach(
const isProfilingSupported = root.memoizedInteractions != null;

if (isProfiling && isProfilingSupported) {
// Profiling durations are only available for certain builds.
// If available, they'll be stored on the HostRoot.
let effectDuration = null;
let passiveEffectDuration = null;
const hostRoot = root.current;
if (hostRoot != null) {
const stateNode = hostRoot.stateNode;
if (stateNode != null) {
effectDuration =
stateNode.effectDuration != null ? stateNode.effectDuration : null;
passiveEffectDuration =
stateNode.passiveEffectDuration != null
? stateNode.passiveEffectDuration
: null;
}
}

// If profiling is active, store commit time and duration, and the current interactions.
// The frontend may request this information after profiling has stopped.
currentCommitProfilingMetadata = {
Expand All @@ -2164,6 +2202,8 @@ export function attach(
maxActualDuration: 0,
priorityLevel:
priorityLevel == null ? null : formatPriorityLevel(priorityLevel),
effectDuration,
passiveEffectDuration,
};
}

Expand Down Expand Up @@ -3294,8 +3334,10 @@ export function attach(
changeDescriptions: Map<number, ChangeDescription> | null,
commitTime: number,
durations: Array<number>,
effectDuration: number | null,
interactions: Array<Interaction>,
maxActualDuration: number,
passiveEffectDuration: number | null,
priorityLevel: string | null,
|};

Expand Down Expand Up @@ -3349,8 +3391,10 @@ export function attach(
const {
changeDescriptions,
durations,
effectDuration,
interactions,
maxActualDuration,
passiveEffectDuration,
priorityLevel,
commitTime,
} = commitProfilingData;
Expand Down Expand Up @@ -3386,9 +3430,11 @@ export function attach(
? Array.from(changeDescriptions.entries())
: null,
duration: maxActualDuration,
effectDuration,
fiberActualDurations,
fiberSelfDurations,
interactionIDs,
passiveEffectDuration,
priorityLevel,
timestamp: commitTime,
});
Expand Down
4 changes: 4 additions & 0 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,15 @@ export type CommitDataBackend = {|
// Tuple of fiber ID and change description
changeDescriptions: Array<[number, ChangeDescription]> | null,
duration: number,
// Only available in certain (newer) React builds,
effectDuration: number | null,
// Tuple of fiber ID and actual duration
fiberActualDurations: Array<[number, number]>,
// Tuple of fiber ID and computed "self" duration
fiberSelfDurations: Array<[number, number]>,
interactionIDs: Array<number>,
// Only available in certain (newer) React builds,
passiveEffectDuration: number | null,
priorityLevel: string | null,
timestamp: number,
|};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ export default class ProfilerStore extends EventEmitter<{|
};

onBridgeProfilingData = (dataBackend: ProfilingDataBackend) => {
console.log('onBridgeProfilingData()', dataBackend);
if (this._isProfiling) {
// This should never happen, but if it does- ignore previous profiling data.
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export default function SidebarCommitInfo(_: Props) {
const {interactions} = profilerStore.getDataForRoot(rootID);
const {
duration,
effectDuration,
interactionIDs,
passiveEffectDuration,
priorityLevel,
timestamp,
} = profilerStore.getCommitData(rootID, selectedCommitIndex);
Expand Down Expand Up @@ -63,6 +65,22 @@ export default function SidebarCommitInfo(_: Props) {
<label className={styles.Label}>Render duration</label>:{' '}
<span className={styles.Value}>{formatDuration(duration)}ms</span>
</li>
{effectDuration !== null && (
<li className={styles.ListItem}>
<label className={styles.Label}>Layout effects duration</label>:{' '}
<span className={styles.Value}>
{formatDuration(effectDuration)}ms
</span>
</li>
)}
{passiveEffectDuration !== null && (
<li className={styles.ListItem}>
<label className={styles.Label}>Passive effects duration</label>:{' '}
<span className={styles.Value}>
{formatDuration(passiveEffectDuration)}ms
</span>
</li>
)}
<li className={styles.Interactions}>
<label className={styles.Label}>Interactions</label>:
<div className={styles.InteractionList}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,30 @@
*/
overflow-x: hidden !important;
}

.Tooltip {
margin-top: 2.5rem;
}

.TooltipList {
list-style: none;
padding: 0;
margin: 0;
}

.TooltipListItem {
display: flex;
}

.TooltipLabel {
font-weight: bold;
margin-right: 0.25rem;
}
.TooltipLabel:after {
content: ':';
}

.TooltipValue {
flex-grow: 1;
text-align: end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* @flow
*/

import type {CommitDataFrontend} from './types';

import * as React from 'react';
import {useEffect, useMemo, useRef, useState} from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
Expand All @@ -31,6 +33,7 @@ export type ItemData = {|
|};

type Props = {|
commitData: CommitDataFrontend,
commitDurations: Array<number>,
commitTimes: Array<number>,
filteredCommitIndices: Array<number>,
Expand All @@ -40,6 +43,7 @@ type Props = {|
|};

export default function SnapshotCommitList({
commitData,
commitDurations,
commitTimes,
filteredCommitIndices,
Expand All @@ -51,6 +55,7 @@ export default function SnapshotCommitList({
<AutoSizer>
{({height, width}) => (
<List
commitData={commitData}
commitDurations={commitDurations}
commitTimes={commitTimes}
height={height}
Expand All @@ -66,6 +71,7 @@ export default function SnapshotCommitList({
}

type ListProps = {|
commitData: CommitDataFrontend,
commitDurations: Array<number>,
commitTimes: Array<number>,
height: number,
Expand All @@ -83,6 +89,7 @@ type DragState = {
};

function List({
commitData,
commitDurations,
selectedCommitIndex,
commitTimes,
Expand Down Expand Up @@ -200,15 +207,68 @@ function List({

let tooltipLabel = null;
if (hoveredCommitIndex !== null) {
const commitDuration = commitDurations[hoveredCommitIndex];
const commitTime = commitTimes[hoveredCommitIndex];
tooltipLabel = `${formatDuration(commitDuration)}ms at ${formatTime(
commitTime,
)}s`;
const {
duration,
effectDuration,
passiveEffectDuration,
priorityLevel,
timestamp,
} = commitData[hoveredCommitIndex];

// Only some React versions include commit durations.
// Show a richer tooltip only for builds that have that info.
if (effectDuration > 0 || passiveEffectDuration > 0) {
tooltipLabel = (
<ul className={styles.TooltipList}>
{priorityLevel !== null && (
<li className={styles.TooltipListItem}>
<label className={styles.TooltipLabel}>Priority</label>
<span className={styles.TooltipValue}>{priorityLevel}</span>
</li>
)}
<li className={styles.TooltipListItem}>
<label className={styles.TooltipLabel}>Committed at</label>
<span className={styles.TooltipValue}>
{formatTime(timestamp)}s
</span>
</li>
<li className={styles.TooltipListItem}>
<label className={styles.TooltipLabel}>Render duration</label>
<span className={styles.TooltipValue}>
{formatDuration(duration)}ms
</span>
</li>
{effectDuration !== null && (
<li className={styles.TooltipListItem}>
<label className={styles.TooltipLabel}>
Layout effects duration
</label>
<span className={styles.TooltipValue}>
{formatDuration(effectDuration)}ms
</span>
</li>
)}
{passiveEffectDuration !== null && (
<li className={styles.TooltipListItem}>
<label className={styles.TooltipLabel}>
Passive effects duration
</label>
<span className={styles.TooltipValue}>
{formatDuration(passiveEffectDuration)}ms
</span>
</li>
)}
</ul>
);
} else {
tooltipLabel = `${formatDuration(duration)}ms at ${formatTime(
timestamp,
)}s`;
}
}

return (
<Tooltip label={tooltipLabel}>
<Tooltip className={styles.Tooltip} label={tooltipLabel}>
<div
ref={divRef}
style={{height, width}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
background-color: var(--color-background);
}

.Inner {
.Inner,
.InnerSelected {
width: 100%;
min-height: 2px;
background-color: var(--color-commit-did-not-render-fill);
color: var(--color-commit-did-not-render-fill-text);
}

.InnerSelected {
background-color: var(--color-button-active);
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
}
};

let backgroundColor;
if (!isSelected && commitDuration > 0) {
backgroundColor = getGradientColor(colorScale);
}

return (
<div
className={styles.Outer}
Expand All @@ -87,11 +92,10 @@ function SnapshotCommitListItem({data: itemData, index, style}: Props) {
commitTime,
)}s`}>
<div
className={styles.Inner}
className={isSelected ? styles.InnerSelected : styles.Inner}
style={{
height: `${Math.round(heightScale * 100)}%`,
backgroundColor:
commitDuration > 0 ? getGradientColor(colorScale) : undefined,
backgroundColor,
}}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export default function SnapshotSelector(_: Props) {
const commitDurations: Array<number> = [];
const commitTimes: Array<number> = [];
commitData.forEach(commitDatum => {
commitDurations.push(commitDatum.duration);
commitDurations.push(
commitDatum.duration +
(commitDatum.effectDuration || 0) +
(commitDatum.passiveEffectDuration || 0),
);
commitTimes.push(commitDatum.timestamp);
});

Expand Down Expand Up @@ -151,6 +155,7 @@ export default function SnapshotSelector(_: Props) {
tabIndex={0}>
{numFilteredCommits > 0 && (
<SnapshotCommitList
commitData={commitData}
commitDurations={commitDurations}
commitTimes={commitTimes}
filteredCommitIndices={filteredCommitIndices}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import styles from './Tooltip.css';

const initialTooltipState = {height: 0, mouseX: 0, mouseY: 0, width: 0};

export default function Tooltip({children, label}: any) {
export default function Tooltip({children, className, label, style}: any) {
const containerRef = useRef(null);
const tooltipRef = useRef(null);

Expand Down Expand Up @@ -36,7 +36,10 @@ export default function Tooltip({children, label}: any) {
className={styles.Container}
onMouseMove={onMouseMove}
ref={containerRef}>
<div ref={tooltipRef} className={`${styles.Tooltip} ${tooltipClassName}`}>
<div
className={`${styles.Tooltip} ${tooltipClassName} ${className || ''}`}
ref={tooltipRef}
style={style}>
{label}
</div>
{children}
Expand Down
Loading

0 comments on commit 1b7ef51

Please sign in to comment.