Skip to content

Commit

Permalink
DevTools scheduling profiler: Add React component measures (facebook#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn authored and zhengjitf committed Apr 15, 2022
1 parent 2c17149 commit d745733
Show file tree
Hide file tree
Showing 24 changed files with 603 additions and 39 deletions.
53 changes: 53 additions & 0 deletions packages/react-devtools-scheduling-profiler/src/CanvasPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
zeroPoint,
} from './view-base';
import {
ComponentMeasuresView,
FlamechartView,
NativeEventsView,
ReactMeasuresView,
Expand Down Expand Up @@ -132,6 +133,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
const nativeEventsViewRef = useRef(null);
const schedulingEventsViewRef = useRef(null);
const suspenseEventsViewRef = useRef(null);
const componentMeasuresViewRef = useRef(null);
const reactMeasuresViewRef = useRef(null);
const flamechartViewRef = useRef(null);
const syncedHorizontalPanAndZoomViewsRef = useRef<HorizontalPanAndZoomView[]>(
Expand Down Expand Up @@ -259,6 +261,17 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
true,
);

let componentMeasuresViewWrapper = null;
if (data.componentMeasures.length > 0) {
const componentMeasuresView = new ComponentMeasuresView(
surface,
defaultFrame,
data,
);
componentMeasuresViewRef.current = componentMeasuresView;
componentMeasuresViewWrapper = createViewHelper(componentMeasuresView);
}

const flamechartView = new FlamechartView(
surface,
defaultFrame,
Expand Down Expand Up @@ -293,6 +306,9 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
rootView.addSubview(suspenseEventsViewWrapper);
}
rootView.addSubview(reactMeasuresViewWrapper);
if (componentMeasuresViewWrapper !== null) {
rootView.addSubview(componentMeasuresViewWrapper);
}
rootView.addSubview(flamechartViewWrapper);

// If subviews are less than the available height, fill remaining height with a solid color.
Expand Down Expand Up @@ -323,6 +339,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
if (prevHoverEvent === null) {
return prevHoverEvent;
} else if (
prevHoverEvent.componentMeasure !== null ||
prevHoverEvent.flamechartStackFrame !== null ||
prevHoverEvent.measure !== null ||
prevHoverEvent.nativeEvent !== null ||
Expand All @@ -331,6 +348,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
prevHoverEvent.userTimingMark !== null
) {
return {
componentMeasure: null,
data: prevHoverEvent.data,
flamechartStackFrame: null,
measure: null,
Expand Down Expand Up @@ -378,6 +396,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
userTimingMarksView.onHover = userTimingMark => {
if (!hoveredEvent || hoveredEvent.userTimingMark !== userTimingMark) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame: null,
measure: null,
Expand All @@ -395,6 +414,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
nativeEventsView.onHover = nativeEvent => {
if (!hoveredEvent || hoveredEvent.nativeEvent !== nativeEvent) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame: null,
measure: null,
Expand All @@ -412,6 +432,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
schedulingEventsView.onHover = schedulingEvent => {
if (!hoveredEvent || hoveredEvent.schedulingEvent !== schedulingEvent) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame: null,
measure: null,
Expand All @@ -429,6 +450,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
suspenseEventsView.onHover = suspenseEvent => {
if (!hoveredEvent || hoveredEvent.suspenseEvent !== suspenseEvent) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame: null,
measure: null,
Expand All @@ -446,6 +468,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
reactMeasuresView.onHover = measure => {
if (!hoveredEvent || hoveredEvent.measure !== measure) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame: null,
measure,
Expand All @@ -458,6 +481,27 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
};
}

const {current: componentMeasuresView} = componentMeasuresViewRef;
if (componentMeasuresView) {
componentMeasuresView.onHover = componentMeasure => {
if (
!hoveredEvent ||
hoveredEvent.componentMeasure !== componentMeasure
) {
setHoveredEvent({
componentMeasure,
data,
flamechartStackFrame: null,
measure: null,
nativeEvent: null,
schedulingEvent: null,
suspenseEvent: null,
userTimingMark: null,
});
}
};
}

const {current: flamechartView} = flamechartViewRef;
if (flamechartView) {
flamechartView.setOnHover(flamechartStackFrame => {
Expand All @@ -466,6 +510,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
hoveredEvent.flamechartStackFrame !== flamechartStackFrame
) {
setHoveredEvent({
componentMeasure: null,
data,
flamechartStackFrame,
measure: null,
Expand Down Expand Up @@ -540,13 +585,21 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
return null;
}
const {
componentMeasure,
flamechartStackFrame,
measure,
schedulingEvent,
suspenseEvent,
} = contextData.hoveredEvent;
return (
<Fragment>
{componentMeasure !== null && (
<ContextMenuItem
onClick={() => copy(componentMeasure.componentName)}
title="Copy component name">
Copy component name
</ContextMenuItem>
)}
{schedulingEvent !== null && (
<ContextMenuItem
onClick={() => copy(schedulingEvent.componentName)}
Expand Down
43 changes: 42 additions & 1 deletion packages/react-devtools-scheduling-profiler/src/EventTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {Point} from './view-base';
import type {
FlamechartStackFrame,
NativeEvent,
ReactComponentMeasure,
ReactHoverContextInfo,
ReactMeasure,
ReactProfilerData,
Expand Down Expand Up @@ -81,6 +82,7 @@ export default function EventTooltip({
}

const {
componentMeasure,
flamechartStackFrame,
measure,
nativeEvent,
Expand All @@ -89,7 +91,14 @@ export default function EventTooltip({
userTimingMark,
} = hoveredEvent;

if (nativeEvent !== null) {
if (componentMeasure !== null) {
return (
<TooltipReactComponentMeasure
componentMeasure={componentMeasure}
tooltipRef={tooltipRef}
/>
);
} else if (nativeEvent !== null) {
return (
<TooltipNativeEvent nativeEvent={nativeEvent} tooltipRef={tooltipRef} />
);
Expand Down Expand Up @@ -130,6 +139,38 @@ export default function EventTooltip({
return null;
}

const TooltipReactComponentMeasure = ({
componentMeasure,
tooltipRef,
}: {
componentMeasure: ReactComponentMeasure,
tooltipRef: Return<typeof useRef>,
}) => {
const {componentName, duration, timestamp, warning} = componentMeasure;

const label = `${componentName} rendered`;

return (
<div className={styles.Tooltip} ref={tooltipRef}>
<div className={styles.TooltipSection}>
{trimString(label, 768)}
<div className={styles.Divider} />
<div className={styles.DetailsGrid}>
<div className={styles.DetailsGridLabel}>Timestamp:</div>
<div>{formatTimestamp(timestamp)}</div>
<div className={styles.DetailsGridLabel}>Duration:</div>
<div>{formatDuration(duration)}</div>
</div>
</div>
{warning !== null && (
<div className={styles.TooltipWarningSection}>
<div className={styles.WarningText}>{warning}</div>
</div>
)}
</div>
);
};

const TooltipFlamechartNode = ({
stackFrame,
tooltipRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import createDataResourceFromImportedFile from './createDataResourceFromImported
import type {DataResource} from './createDataResourceFromImportedFile';

export type Context = {|
clearSchedulingProfilerData: () => void,
importSchedulingProfilerData: (file: File) => void,
schedulingProfilerData: DataResource | null,
|};
Expand All @@ -33,6 +34,10 @@ function SchedulingProfilerContextController({children}: Props) {
setSchedulingProfilerData,
] = useState<DataResource | null>(null);

const clearSchedulingProfilerData = useCallback(() => {
setSchedulingProfilerData(null);
}, []);

const importSchedulingProfilerData = useCallback((file: File) => {
setSchedulingProfilerData(createDataResourceFromImportedFile(file));
}, []);
Expand All @@ -41,11 +46,13 @@ function SchedulingProfilerContextController({children}: Props) {

const value = useMemo(
() => ({
clearSchedulingProfilerData,
importSchedulingProfilerData,
schedulingProfilerData,
// TODO (scheduling profiler)
}),
[
clearSchedulingProfilerData,
importSchedulingProfilerData,
schedulingProfilerData,
// TODO (scheduling profiler)
Expand Down
Loading

0 comments on commit d745733

Please sign in to comment.