Skip to content

Commit

Permalink
[DevTools] Add native events to the scheduling profiler (facebook#21947)
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 53d3f22 commit 84d11d7
Show file tree
Hide file tree
Showing 12 changed files with 679 additions and 316 deletions.
59 changes: 47 additions & 12 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 {
} from './view-base';
import {
FlamechartView,
NativeEventsView,
ReactEventsView,
ReactMeasuresView,
TimeAxisMarkersView,
Expand Down Expand Up @@ -126,6 +127,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {

const surfaceRef = useRef(new Surface());
const userTimingMarksViewRef = useRef(null);
const nativeEventsViewRef = useRef(null);
const reactEventsViewRef = useRef(null);
const reactMeasuresViewRef = useRef(null);
const flamechartViewRef = useRef(null);
Expand Down Expand Up @@ -176,6 +178,10 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
topContentStack.addSubview(userTimingMarksView);
}

const nativeEventsView = new NativeEventsView(surface, defaultFrame, data);
nativeEventsViewRef.current = nativeEventsView;
topContentStack.addSubview(nativeEventsView);

const reactEventsView = new ReactEventsView(surface, defaultFrame, data);
reactEventsViewRef.current = reactEventsView;
topContentStack.addSubview(reactEventsView);
Expand Down Expand Up @@ -299,7 +305,24 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
if (!hoveredEvent || hoveredEvent.userTimingMark !== userTimingMark) {
setHoveredEvent({
userTimingMark,
event: null,
nativeEvent: null,
reactEvent: null,
flamechartStackFrame: null,
measure: null,
data,
});
}
};
}

const {current: nativeEventsView} = nativeEventsViewRef;
if (nativeEventsView) {
nativeEventsView.onHover = nativeEvent => {
if (!hoveredEvent || hoveredEvent.nativeEvent !== nativeEvent) {
setHoveredEvent({
userTimingMark: null,
nativeEvent,
reactEvent: null,
flamechartStackFrame: null,
measure: null,
data,
Expand All @@ -310,11 +333,12 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {

const {current: reactEventsView} = reactEventsViewRef;
if (reactEventsView) {
reactEventsView.onHover = event => {
if (!hoveredEvent || hoveredEvent.event !== event) {
reactEventsView.onHover = reactEvent => {
if (!hoveredEvent || hoveredEvent.reactEvent !== reactEvent) {
setHoveredEvent({
userTimingMark: null,
event,
nativeEvent: null,
reactEvent,
flamechartStackFrame: null,
measure: null,
data,
Expand All @@ -329,7 +353,8 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
if (!hoveredEvent || hoveredEvent.measure !== measure) {
setHoveredEvent({
userTimingMark: null,
event: null,
nativeEvent: null,
reactEvent: null,
flamechartStackFrame: null,
measure,
data,
Expand All @@ -347,7 +372,8 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
) {
setHoveredEvent({
userTimingMark: null,
event: null,
nativeEvent: null,
reactEvent: null,
flamechartStackFrame,
measure: null,
data,
Expand All @@ -368,9 +394,18 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
);
}

const {current: nativeEventsView} = nativeEventsViewRef;
if (nativeEventsView) {
nativeEventsView.setHoveredEvent(
hoveredEvent ? hoveredEvent.nativeEvent : null,
);
}

const {current: reactEventsView} = reactEventsViewRef;
if (reactEventsView) {
reactEventsView.setHoveredEvent(hoveredEvent ? hoveredEvent.event : null);
reactEventsView.setHoveredEvent(
hoveredEvent ? hoveredEvent.reactEvent : null,
);
}

const {current: reactMeasuresView} = reactMeasuresViewRef;
Expand Down Expand Up @@ -402,22 +437,22 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
return null;
}
const {
event,
reactEvent,
flamechartStackFrame,
measure,
} = contextData.hoveredEvent;
return (
<Fragment>
{event !== null && (
{reactEvent !== null && (
<ContextMenuItem
onClick={() => copy(event.componentName)}
onClick={() => copy(reactEvent.componentName)}
title="Copy component name">
Copy component name
</ContextMenuItem>
)}
{event !== null && event.componentStack && (
{reactEvent !== null && reactEvent.componentStack && (
<ContextMenuItem
onClick={() => copy(event.componentStack)}
onClick={() => copy(reactEvent.componentStack)}
title="Copy component stack">
Copy component stack
</ContextMenuItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
.DetailsGridLabel {
color: var(--color-dim);
text-align: right;
white-space: nowrap;
}

.DetailsGridURL {
Expand All @@ -45,7 +46,7 @@
.ComponentName {
font-weight: bold;
word-break: break-word;
margin-right: 0.4rem;
margin-right: 0.25rem;
}

.ComponentStack {
Expand Down
59 changes: 48 additions & 11 deletions packages/react-devtools-scheduling-profiler/src/EventTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {Point} from './view-base';
import type {
FlamechartStackFrame,
NativeEvent,
ReactEvent,
ReactHoverContextInfo,
ReactMeasure,
Expand Down Expand Up @@ -47,8 +48,8 @@ function trimmedString(string: string, length: number): string {
return string;
}

function getReactEventLabel(type): string | null {
switch (type) {
function getReactEventLabel(event: ReactEvent): string | null {
switch (event.type) {
case 'schedule-render':
return 'render scheduled';
case 'schedule-state-update':
Expand Down Expand Up @@ -111,10 +112,22 @@ export default function EventTooltip({data, hoveredEvent, origin}: Props) {
return null;
}

const {event, measure, flamechartStackFrame, userTimingMark} = hoveredEvent;
const {
nativeEvent,
reactEvent,
measure,
flamechartStackFrame,
userTimingMark,
} = hoveredEvent;

if (event !== null) {
return <TooltipReactEvent event={event} tooltipRef={tooltipRef} />;
if (nativeEvent !== null) {
return (
<TooltipNativeEvent nativeEvent={nativeEvent} tooltipRef={tooltipRef} />
);
} else if (reactEvent !== null) {
return (
<TooltipReactEvent reactEvent={reactEvent} tooltipRef={tooltipRef} />
);
} else if (measure !== null) {
return (
<TooltipReactMeasure
Expand Down Expand Up @@ -189,23 +202,47 @@ const TooltipFlamechartNode = ({
);
};

const TooltipNativeEvent = ({
nativeEvent,
tooltipRef,
}: {
nativeEvent: NativeEvent,
tooltipRef: Return<typeof useRef>,
}) => {
const {duration, timestamp, type} = nativeEvent;

return (
<div className={styles.Tooltip} ref={tooltipRef}>
<span className={styles.ComponentName}>{trimmedString(type, 768)}</span>
event
<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>
);
};

const TooltipReactEvent = ({
event,
reactEvent,
tooltipRef,
}: {
event: ReactEvent,
reactEvent: ReactEvent,
tooltipRef: Return<typeof useRef>,
}) => {
const label = getReactEventLabel(event.type);
const color = getReactEventColor(event);
const label = getReactEventLabel(reactEvent);
const color = getReactEventColor(reactEvent);
if (!label || !color) {
if (__DEV__) {
console.warn('Unexpected event type "%s"', event.type);
console.warn('Unexpected reactEvent type "%s"', reactEvent.type);
}
return null;
}

const {componentName, componentStack, timestamp} = event;
const {componentName, componentStack, timestamp} = reactEvent;

return (
<div className={styles.Tooltip} ref={tooltipRef}>
Expand Down
Loading

0 comments on commit 84d11d7

Please sign in to comment.