Skip to content

Commit

Permalink
Implement User Timing marks view (#109)
Browse files Browse the repository at this point in the history
Summary
---

Implement User Timing marks view as a fixed-height row above our React
events. Also adds a tooltip for them.

Colors [obtained from Firefox Profiler](https://github.com/firefox-devtools/profiler/blob/c4f68f423c7035a094952311addd0af5bec209e7/src/utils/colors.js#L26-L27) to match Firefox's Marker Chart. The colors aren't the best since they kind of upstage the React events.

Resolves #72 iteration 1. This PR does not display User Timing measures
due to the complexities of displaying a stack of User Timing measures. I
also don't know how to identify a measure's start/end marks, so I'm not
able to implement highlighting of related marks. We can create a
separate issue for User Timing measures (#72 iteration 3).

Test Plan
---

* `yarn lint`
* `yarn flow`: no type errors in changed code. Also fixed type errors in
  `EventTooltip`; 24 errors remaining.
* `yarn test`: tests for `preprocessData` updated.
* `yarn start`: tested with Facebook.com profile
  • Loading branch information
taneliang authored Aug 4, 2020
1 parent 62a6796 commit 361a860
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 45 deletions.
55 changes: 47 additions & 8 deletions src/CanvasPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
ReactEventsView,
ReactMeasuresView,
TimeAxisMarkersView,
UserTimingMarksView,
} from './canvas/views';

type ContextMenuContextData = {|
Expand Down Expand Up @@ -121,6 +122,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
] = useState<ReactHoverContextInfo | null>(null);

const surfaceRef = useRef(new Surface());
const userTimingMarksViewRef = useRef(null);
const reactEventsViewRef = useRef(null);
const reactMeasuresViewRef = useRef(null);
const flamechartViewRef = useRef(null);
Expand All @@ -137,21 +139,32 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {

// Top content

const axisMarkersView = new TimeAxisMarkersView(
const topContentStack = new View(
surface,
defaultFrame,
data.duration,
verticallyStackedLayout,
);

const reactEventsView = new ReactEventsView(surface, defaultFrame, data);
reactEventsViewRef.current = reactEventsView;

const topContentStack = new View(
const axisMarkersView = new TimeAxisMarkersView(
surface,
defaultFrame,
verticallyStackedLayout,
data.duration,
);
topContentStack.addSubview(axisMarkersView);

if (data.otherUserTimingMarks.length > 0) {
const userTimingMarksView = new UserTimingMarksView(
surface,
defaultFrame,
data.otherUserTimingMarks,
data.duration,
);
userTimingMarksViewRef.current = userTimingMarksView;
topContentStack.addSubview(userTimingMarksView);
}

const reactEventsView = new ReactEventsView(surface, defaultFrame, data);
reactEventsViewRef.current = reactEventsView;
topContentStack.addSubview(reactEventsView);

const topContentHorizontalPanAndZoomView = new HorizontalPanAndZoomView(
Expand Down Expand Up @@ -239,7 +252,8 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
hoveredEvent &&
(hoveredEvent.event ||
hoveredEvent.measure ||
hoveredEvent.flamechartStackFrame)
hoveredEvent.flamechartStackFrame ||
hoveredEvent.userTimingMark)
) {
setMouseLocation({
x: interaction.payload.event.x,
Expand Down Expand Up @@ -268,11 +282,27 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
});

useEffect(() => {
const {current: userTimingMarksView} = userTimingMarksViewRef;
if (userTimingMarksView) {
userTimingMarksView.onHover = userTimingMark => {
if (!hoveredEvent || hoveredEvent.userTimingMark !== userTimingMark) {
setHoveredEvent({
userTimingMark,
event: null,
flamechartStackFrame: null,
measure: null,
data,
});
}
};
}

const {current: reactEventsView} = reactEventsViewRef;
if (reactEventsView) {
reactEventsView.onHover = event => {
if (!hoveredEvent || hoveredEvent.event !== event) {
setHoveredEvent({
userTimingMark: null,
event,
flamechartStackFrame: null,
measure: null,
Expand All @@ -287,6 +317,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
reactMeasuresView.onHover = measure => {
if (!hoveredEvent || hoveredEvent.measure !== measure) {
setHoveredEvent({
userTimingMark: null,
event: null,
flamechartStackFrame: null,
measure,
Expand All @@ -304,6 +335,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
hoveredEvent.flamechartStackFrame !== flamechartStackFrame
) {
setHoveredEvent({
userTimingMark: null,
event: null,
flamechartStackFrame,
measure: null,
Expand All @@ -321,6 +353,13 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
]);

useLayoutEffect(() => {
const {current: userTimingMarksView} = userTimingMarksViewRef;
if (userTimingMarksView) {
userTimingMarksView.setHoveredMark(
hoveredEvent ? hoveredEvent.userTimingMark : null,
);
}

const {current: reactEventsView} = reactEventsViewRef;
if (reactEventsView) {
reactEventsView.setHoveredEvent(hoveredEvent ? hoveredEvent.event : null);
Expand Down
54 changes: 30 additions & 24 deletions src/EventTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import type {Point} from './layout';
import type {
FlamechartStackFrame,
ReactEvent,
ReactHoverContextInfo,
ReactMeasure,
ReactProfilerData,
ReactHoverContextInfo,
Return,
UserTimingMark,
} from './types';

import prettyMilliseconds from 'pretty-ms';
Expand Down Expand Up @@ -84,7 +85,7 @@ export default function EventTooltip({data, hoveredEvent, origin}: Props) {
return null;
}

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

if (event !== null) {
switch (event.type) {
Expand Down Expand Up @@ -150,6 +151,10 @@ export default function EventTooltip({data, hoveredEvent, origin}: Props) {
tooltipRef={tooltipRef}
/>
);
} else if (userTimingMark !== null) {
return (
<TooltipUserTimingMark mark={userTimingMark} tooltipRef={tooltipRef} />
);
}
return null;
}
Expand Down Expand Up @@ -180,13 +185,7 @@ const TooltipFlamechartNode = ({
locationColumn,
} = stackFrame;
return (
<div
className={styles.Tooltip}
style={{
backgroundColor: COLORS.TOOLTIP_BG,
color: COLORS.TOOLTIP,
}}
ref={tooltipRef}>
<div className={styles.Tooltip} ref={tooltipRef}>
{formatDuration(duration)} {trimComponentName(name)}
<div className={styles.DetailsGrid}>
<div className={styles.DetailsGridLabel}>Timestamp:</div>
Expand Down Expand Up @@ -223,13 +222,7 @@ const TooltipReactEvent = ({
const label = getReactEventLabel(type);

return (
<div
className={styles.Tooltip}
style={{
backgroundColor: COLORS.TOOLTIP_BG,
color: COLORS.TOOLTIP,
}}
ref={tooltipRef}>
<div className={styles.Tooltip} ref={tooltipRef}>
{componentName && (
<span className={styles.ComponentName} style={{color}}>
{trimComponentName(componentName)}
Expand Down Expand Up @@ -267,14 +260,7 @@ const TooltipReactMeasure = ({
const [startTime, stopTime] = getBatchRange(batchUID, data);

return (
<div
className={styles.Tooltip}
style={{
position: 'absolute',
backgroundColor: COLORS.TOOLTIP_BG,
color: COLORS.TOOLTIP,
}}
ref={tooltipRef}>
<div className={styles.Tooltip} ref={tooltipRef}>
{formatDuration(duration)} {label}
<div className={styles.Divider} />
<div className={styles.DetailsGrid}>
Expand All @@ -290,3 +276,23 @@ const TooltipReactMeasure = ({
</div>
);
};

const TooltipUserTimingMark = ({
mark,
tooltipRef,
}: {
mark: UserTimingMark,
tooltipRef: Return<typeof useRef>,
}) => {
const {name, timestamp} = mark;
return (
<div className={styles.Tooltip} ref={tooltipRef}>
{name}
<div className={styles.Divider} />
<div className={styles.DetailsGrid}>
<div className={styles.DetailsGridLabel}>Timestamp:</div>
<div>{formatTimestamp(timestamp)}</div>
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions src/canvas/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export const COLORS = Object.freeze({
PRIORITY_BACKGROUND: '#ededf0',
PRIORITY_BORDER: '#d7d7db',
PRIORITY_LABEL: '#272727',
USER_TIMING: '#45a1ff',
USER_TIMING_HOVER: '#0a84ff',
REACT_IDLE: '#edf6ff',
REACT_IDLE_SELECTED: '#EDF6FF',
REACT_IDLE_HOVER: '#EDF6FF',
Expand Down
Loading

0 comments on commit 361a860

Please sign in to comment.