From 37941624cd25f0593383a6f8290d4156de6f790c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 26 Jul 2021 19:04:00 -0400 Subject: [PATCH] Scheduling profiler: Vertically separate overlapping events Also highlight events that have synchronous updates inside of them. (We may want to relax this highlighting later to not warn about event handlers that are still fast enough.) --- .../src/content-views/FlamechartView.js | 14 +- .../src/content-views/NativeEventsView.js | 224 ++++++++++++------ .../src/content-views/ReactEventsView.js | 14 +- .../src/content-views/ReactMeasuresView.js | 2 +- .../src/content-views/TimeAxisMarkersView.js | 4 +- .../src/content-views/UserTimingMarksView.js | 6 +- .../src/content-views/constants.js | 41 ++-- .../src/import-worker/preprocessData.js | 46 +++- .../src/types.js | 2 + .../views/Settings/SettingsContext.js | 17 +- .../src/devtools/views/root.css | 18 +- 11 files changed, 265 insertions(+), 123 deletions(-) diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js b/packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js index 8b79a30916fed..6b839a39f71cb 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js @@ -32,9 +32,9 @@ import { } from './utils/positioning'; import { COLORS, - FLAMECHART_FONT_SIZE, + FONT_SIZE, FLAMECHART_FRAME_HEIGHT, - FLAMECHART_TEXT_PADDING, + TEXT_PADDING, COLOR_HOVER_DIM_DELTA, BORDER_SIZE, } from './constants'; @@ -157,7 +157,7 @@ class FlamechartStackLayerView extends View { context.textAlign = 'left'; context.textBaseline = 'middle'; - context.font = `${FLAMECHART_FONT_SIZE}px sans-serif`; + context.font = `${FONT_SIZE}px sans-serif`; const scaleFactor = positioningScaleFactor(_intrinsicSize.width, frame); @@ -195,15 +195,15 @@ class FlamechartStackLayerView extends View { drawableRect.size.height, ); - if (width > FLAMECHART_TEXT_PADDING * 2) { + if (width > TEXT_PADDING * 2) { const trimmedName = trimFlamechartText( context, name, - width - FLAMECHART_TEXT_PADDING * 2 + (x < 0 ? x : 0), + width - TEXT_PADDING * 2 + (x < 0 ? x : 0), ); if (trimmedName !== null) { - context.fillStyle = COLORS.FLAME_GRAPH_LABEL; + context.fillStyle = COLORS.TEXT_COLOR; // Prevent text from being drawn outside `viewableArea` const textOverflowsViewableArea = !rectEqualToRect( @@ -225,7 +225,7 @@ class FlamechartStackLayerView extends View { context.fillText( trimmedName, - nodeRect.origin.x + FLAMECHART_TEXT_PADDING - (x < 0 ? x : 0), + nodeRect.origin.x + TEXT_PADDING - (x < 0 ? x : 0), nodeRect.origin.y + FLAMECHART_FRAME_HEIGHT / 2, ); diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/NativeEventsView.js b/packages/react-devtools-scheduling-profiler/src/content-views/NativeEventsView.js index 97d4369753a16..3666f64539732 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/NativeEventsView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/NativeEventsView.js @@ -11,9 +11,10 @@ import type {NativeEvent, ReactProfilerData} from '../types'; import type {Interaction, MouseMoveInteraction, Rect, Size} from '../view-base'; import { + durationToWidth, positioningScaleFactor, - timestampToPosition, positionToTimestamp, + timestampToPosition, } from './utils/positioning'; import { View, @@ -24,28 +25,77 @@ import { } from '../view-base'; import { COLORS, - EVENT_ROW_PADDING, - EVENT_DIAMETER, + TEXT_PADDING, + NATIVE_EVENT_HEIGHT, + FONT_SIZE, BORDER_SIZE, } from './constants'; -const EVENT_ROW_HEIGHT_FIXED = - EVENT_ROW_PADDING + EVENT_DIAMETER + EVENT_ROW_PADDING; +const ROW_WITH_BORDER_HEIGHT = NATIVE_EVENT_HEIGHT + BORDER_SIZE; + +// TODO (scheduling profiler) Make this a reusable util +const cachedFlamechartTextWidths = new Map(); +const trimFlamechartText = ( + context: CanvasRenderingContext2D, + text: string, + width: number, +) => { + for (let i = text.length - 1; i >= 0; i--) { + const trimmedText = i === text.length - 1 ? text : text.substr(0, i) + '…'; + + let measuredWidth = cachedFlamechartTextWidths.get(trimmedText); + if (measuredWidth == null) { + measuredWidth = context.measureText(trimmedText).width; + cachedFlamechartTextWidths.set(trimmedText, measuredWidth); + } + + if (measuredWidth <= width) { + return trimmedText; + } + } + + return null; +}; export class NativeEventsView extends View { - _profilerData: ReactProfilerData; + _depthToNativeEvent: Map; + _hoveredEvent: NativeEvent | null = null; _intrinsicSize: Size; + _maxDepth: number = 0; + _profilerData: ReactProfilerData; - _hoveredEvent: NativeEvent | null = null; onHover: ((event: NativeEvent | null) => void) | null = null; constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { super(surface, frame); + this._profilerData = profilerData; + this._performPreflightComputations(); + console.log(this._depthToNativeEvent); + } + + _performPreflightComputations() { + this._depthToNativeEvent = new Map(); + + const {duration, nativeEvents} = this._profilerData; + + nativeEvents.forEach(event => { + const depth = event.depth; + + this._maxDepth = Math.max(this._maxDepth, depth); + + if (!this._depthToNativeEvent.has(depth)) { + this._depthToNativeEvent.set(depth, [event]); + } else { + // $FlowFixMe This is unnecessary. + this._depthToNativeEvent.get(depth).push(event); + } + }); + this._intrinsicSize = { - width: this._profilerData.duration, - height: EVENT_ROW_HEIGHT_FIXED, + width: duration, + height: (this._maxDepth + 1) * ROW_WITH_BORDER_HEIGHT, }; } @@ -73,7 +123,9 @@ export class NativeEventsView extends View { showHoverHighlight: boolean, ) { const {frame} = this; - const {duration, timestamp} = event; + const {depth, duration, highlight, timestamp, type} = event; + + baseY += depth * ROW_WITH_BORDER_HEIGHT; const xStart = timestampToPosition(timestamp, scaleFactor, frame); const xStop = timestampToPosition(timestamp + duration, scaleFactor, frame); @@ -82,25 +134,60 @@ export class NativeEventsView extends View { x: xStart, y: baseY, }, - size: {width: xStop - xStart, height: EVENT_DIAMETER}, + size: {width: xStop - xStart, height: NATIVE_EVENT_HEIGHT}, }; if (!rectIntersectsRect(eventRect, rect)) { return; // Not in view } - const fillStyle = showHoverHighlight - ? COLORS.NATIVE_EVENT_HOVER - : COLORS.NATIVE_EVENT; + const width = durationToWidth(duration, scaleFactor); + if (width < 1) { + return; // Too small to render at this zoom level + } const drawableRect = intersectionOfRects(eventRect, rect); context.beginPath(); - context.fillStyle = fillStyle; + if (highlight) { + context.fillStyle = showHoverHighlight + ? COLORS.NATIVE_EVENT_WARNING_HOVER + : COLORS.NATIVE_EVENT_WARNING; + } else { + context.fillStyle = showHoverHighlight + ? COLORS.NATIVE_EVENT_HOVER + : COLORS.NATIVE_EVENT; + } context.fillRect( drawableRect.origin.x, drawableRect.origin.y, drawableRect.size.width, drawableRect.size.height, ); + + // Render event type label + context.textAlign = 'left'; + context.textBaseline = 'middle'; + context.font = `${FONT_SIZE}px sans-serif`; + + if (width > TEXT_PADDING * 2) { + const x = Math.floor(timestampToPosition(timestamp, scaleFactor, frame)); + const trimmedName = trimFlamechartText( + context, + type, + width - TEXT_PADDING * 2 + (x < 0 ? x : 0), + ); + + if (trimmedName !== null) { + context.fillStyle = highlight + ? COLORS.NATIVE_EVENT_WARNING_TEXT + : COLORS.TEXT_COLOR; + + context.fillText( + trimmedName, + eventRect.origin.x + TEXT_PADDING - (x < 0 ? x : 0), + eventRect.origin.y + NATIVE_EVENT_HEIGHT / 2, + ); + } + } } draw(context: CanvasRenderingContext2D) { @@ -111,7 +198,7 @@ export class NativeEventsView extends View { visibleArea, } = this; - context.fillStyle = COLORS.BACKGROUND; + context.fillStyle = COLORS.PRIORITY_BACKGROUND; context.fillRect( visibleArea.origin.x, visibleArea.origin.y, @@ -120,57 +207,43 @@ export class NativeEventsView extends View { ); // Draw events - const baseY = frame.origin.y + EVENT_ROW_PADDING; const scaleFactor = positioningScaleFactor( this._intrinsicSize.width, frame, ); nativeEvents.forEach(event => { - if (event === _hoveredEvent) { - // Draw the highlighted items on top so they stand out. - // This is helpful if there are multiple (overlapping) items close to each other. - this._drawSingleNativeEvent( - context, - visibleArea, - event, - baseY, - scaleFactor, - true, - ); - } else { - this._drawSingleNativeEvent( - context, - visibleArea, - event, - baseY, - scaleFactor, - false, - ); - } + this._drawSingleNativeEvent( + context, + visibleArea, + event, + frame.origin.y, + scaleFactor, + event === _hoveredEvent, + ); }); - // Render bottom border. - // Propose border rect, check if intersects with `rect`, draw intersection. - const borderFrame: Rect = { - origin: { - x: frame.origin.x, - y: frame.origin.y + EVENT_ROW_HEIGHT_FIXED - BORDER_SIZE, - }, - size: { - width: frame.size.width, - height: BORDER_SIZE, - }, - }; - if (rectIntersectsRect(borderFrame, visibleArea)) { - const borderDrawableRect = intersectionOfRects(borderFrame, visibleArea); - context.fillStyle = COLORS.PRIORITY_BORDER; - context.fillRect( - borderDrawableRect.origin.x, - borderDrawableRect.origin.y, - borderDrawableRect.size.width, - borderDrawableRect.size.height, - ); + // Render bottom borders. + for (let i = 0; i <= this._maxDepth; i++) { + const borderFrame: Rect = { + origin: { + x: frame.origin.x, + y: frame.origin.y + NATIVE_EVENT_HEIGHT, + }, + size: { + width: frame.size.width, + height: BORDER_SIZE, + }, + }; + if (rectIntersectsRect(borderFrame, visibleArea)) { + context.fillStyle = COLORS.PRIORITY_BORDER; + context.fillRect( + visibleArea.origin.x, + frame.origin.y + (i + 1) * ROW_WITH_BORDER_HEIGHT - BORDER_SIZE, + visibleArea.size.width, + BORDER_SIZE, + ); + } } } @@ -189,25 +262,26 @@ export class NativeEventsView extends View { return; } - const {nativeEvents} = this._profilerData; - const scaleFactor = positioningScaleFactor(_intrinsicSize.width, frame); const hoverTimestamp = positionToTimestamp(location.x, scaleFactor, frame); - // Find the event being hovered over. - // - // Because data ranges may overlap, we want to find the last intersecting item. - // This will always be the one on "top" (the one the user is hovering over). - for (let index = nativeEvents.length - 1; index >= 0; index--) { - const nativeEvent = nativeEvents[index]; - const {duration, timestamp} = nativeEvent; - - if ( - hoverTimestamp >= timestamp && - hoverTimestamp <= timestamp + duration - ) { - onHover(nativeEvent); - return; + const adjustedCanvasMouseY = location.y - frame.origin.y; + const depth = Math.floor(adjustedCanvasMouseY / ROW_WITH_BORDER_HEIGHT); + const nativeEventsAtDepth = this._depthToNativeEvent.get(depth); + + if (nativeEventsAtDepth) { + // Find the event being hovered over. + for (let index = nativeEventsAtDepth.length - 1; index >= 0; index--) { + const nativeEvent = nativeEventsAtDepth[index]; + const {duration, timestamp} = nativeEvent; + + if ( + hoverTimestamp >= timestamp && + hoverTimestamp <= timestamp + duration + ) { + onHover(nativeEvent); + return; + } } } diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/ReactEventsView.js b/packages/react-devtools-scheduling-profiler/src/content-views/ReactEventsView.js index e67f590f6bf19..f76df3bed5592 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/ReactEventsView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/ReactEventsView.js @@ -25,13 +25,13 @@ import { } from '../view-base'; import { COLORS, - EVENT_ROW_PADDING, - EVENT_DIAMETER, + TOP_ROW_PADDING, + REACT_EVENT_DIAMETER, BORDER_SIZE, } from './constants'; const EVENT_ROW_HEIGHT_FIXED = - EVENT_ROW_PADDING + EVENT_DIAMETER + EVENT_ROW_PADDING; + TOP_ROW_PADDING + REACT_EVENT_DIAMETER + TOP_ROW_PADDING; function isSuspenseEvent(event: ReactEvent): boolean %checks { return ( @@ -85,13 +85,13 @@ export class ReactEventsView extends View { const {timestamp, type} = event; const x = timestampToPosition(timestamp, scaleFactor, frame); - const radius = EVENT_DIAMETER / 2; + const radius = REACT_EVENT_DIAMETER / 2; const eventRect: Rect = { origin: { x: x - radius, y: baseY, }, - size: {width: EVENT_DIAMETER, height: EVENT_DIAMETER}, + size: {width: REACT_EVENT_DIAMETER, height: REACT_EVENT_DIAMETER}, }; if (!rectIntersectsRect(eventRect, rect)) { return; // Not in view @@ -156,7 +156,7 @@ export class ReactEventsView extends View { ); // Draw events - const baseY = frame.origin.y + EVENT_ROW_PADDING; + const baseY = frame.origin.y + TOP_ROW_PADDING; const scaleFactor = positioningScaleFactor( this._intrinsicSize.width, frame, @@ -246,7 +246,7 @@ export class ReactEventsView extends View { ); const hoverTimestamp = positionToTimestamp(location.x, scaleFactor, frame); const eventTimestampAllowance = widthToDuration( - EVENT_DIAMETER / 2, + REACT_EVENT_DIAMETER / 2, scaleFactor, ); diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js b/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js index 46fbe51bb8cd0..61e804223a9e7 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js @@ -54,7 +54,7 @@ export class ReactMeasuresView extends View { _performPreflightComputations() { this._lanesToRender = []; - this._laneToMeasures = new Map(); + this._laneToMeasures = new Map(); for (let lane: ReactLane = 0; lane < REACT_TOTAL_NUM_LANES; lane++) { const measuresForLane = getMeasuresForLane( diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/TimeAxisMarkersView.js b/packages/react-devtools-scheduling-profiler/src/content-views/TimeAxisMarkersView.js index 4f55494282407..47913dbb22655 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/TimeAxisMarkersView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/TimeAxisMarkersView.js @@ -25,7 +25,7 @@ import { COLORS, INTERVAL_TIMES, LABEL_SIZE, - MARKER_FONT_SIZE, + FONT_SIZE, MARKER_HEIGHT, MARKER_TEXT_PADDING, MARKER_TICK_HEIGHT, @@ -129,7 +129,7 @@ export class TimeAxisMarkersView extends View { context.fillStyle = COLORS.TIME_MARKER_LABEL; context.textAlign = 'right'; context.textBaseline = 'middle'; - context.font = `${MARKER_FONT_SIZE}px sans-serif`; + context.font = `${FONT_SIZE}px sans-serif`; context.fillText( `${markerLabel}ms`, x - MARKER_TEXT_PADDING, diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/UserTimingMarksView.js b/packages/react-devtools-scheduling-profiler/src/content-views/UserTimingMarksView.js index 4a73c48aae076..7a822716db6b0 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/UserTimingMarksView.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/UserTimingMarksView.js @@ -25,13 +25,13 @@ import { } from '../view-base'; import { COLORS, - EVENT_ROW_PADDING, + TOP_ROW_PADDING, USER_TIMING_MARK_SIZE, BORDER_SIZE, } from './constants'; const ROW_HEIGHT_FIXED = - EVENT_ROW_PADDING + USER_TIMING_MARK_SIZE + EVENT_ROW_PADDING; + TOP_ROW_PADDING + USER_TIMING_MARK_SIZE + TOP_ROW_PADDING; export class UserTimingMarksView extends View { _marks: UserTimingMark[]; @@ -125,7 +125,7 @@ export class UserTimingMarksView extends View { ); // Draw marks - const baseY = frame.origin.y + EVENT_ROW_PADDING; + const baseY = frame.origin.y + TOP_ROW_PADDING; const scaleFactor = positioningScaleFactor( this._intrinsicSize.width, frame, diff --git a/packages/react-devtools-scheduling-profiler/src/content-views/constants.js b/packages/react-devtools-scheduling-profiler/src/content-views/constants.js index 6581bed312c2c..da3834892636d 100644 --- a/packages/react-devtools-scheduling-profiler/src/content-views/constants.js +++ b/packages/react-devtools-scheduling-profiler/src/content-views/constants.js @@ -8,12 +8,19 @@ */ export const LABEL_SIZE = 80; -export const LABEL_FONT_SIZE = 11; export const MARKER_HEIGHT = 20; export const MARKER_TICK_HEIGHT = 8; -export const MARKER_FONT_SIZE = 10; +export const FONT_SIZE = 10; export const MARKER_TEXT_PADDING = 8; export const COLOR_HOVER_DIM_DELTA = 5; +export const TOP_ROW_PADDING = 4; +export const NATIVE_EVENT_HEIGHT = 14; +export const REACT_EVENT_DIAMETER = 6; +export const USER_TIMING_MARK_SIZE = 8; +export const REACT_MEASURE_HEIGHT = 9; +export const BORDER_SIZE = 1; +export const FLAMECHART_FRAME_HEIGHT = 14; +export const TEXT_PADDING = 3; export const INTERVAL_TIMES = [ 1, @@ -31,22 +38,14 @@ export const INTERVAL_TIMES = [ ]; export const MIN_INTERVAL_SIZE_PX = 70; -export const EVENT_ROW_PADDING = 4; -export const EVENT_DIAMETER = 6; -export const USER_TIMING_MARK_SIZE = 8; -export const REACT_MEASURE_HEIGHT = 9; -export const BORDER_SIZE = 1; - -export const FLAMECHART_FONT_SIZE = 10; -export const FLAMECHART_FRAME_HEIGHT = 16; -export const FLAMECHART_TEXT_PADDING = 3; - // TODO Replace this with "export let" vars export let COLORS = { BACKGROUND: '', - FLAME_GRAPH_LABEL: '', NATIVE_EVENT: '', NATIVE_EVENT_HOVER: '', + NATIVE_EVENT_WARNING: '', + NATIVE_EVENT_WARNING_HOVER: '', + NATIVE_EVENT_WARNING_TEXT: '', PRIORITY_BACKGROUND: '', PRIORITY_BORDER: '', PRIORITY_LABEL: '', @@ -75,6 +74,7 @@ export let COLORS = { REACT_SUSPEND: '', REACT_SUSPEND_HOVER: '', REACT_WORK_BORDER: '', + TEXT_COLOR: '', TIME_MARKER_LABEL: '', }; @@ -83,15 +83,21 @@ export function updateColorsToMatchTheme(): void { COLORS = { BACKGROUND: computedStyle.getPropertyValue('--color-background'), - FLAME_GRAPH_LABEL: computedStyle.getPropertyValue( - '--color-scheduling-profiler-flame-graph-label', - ), NATIVE_EVENT: computedStyle.getPropertyValue( '--color-scheduling-profiler-native-event', ), NATIVE_EVENT_HOVER: computedStyle.getPropertyValue( '--color-scheduling-profiler-native-event-hover', ), + NATIVE_EVENT_WARNING: computedStyle.getPropertyValue( + '--color-scheduling-profiler-native-event-warning', + ), + NATIVE_EVENT_WARNING_HOVER: computedStyle.getPropertyValue( + '--color-scheduling-profiler-native-event-warning-hover', + ), + NATIVE_EVENT_WARNING_TEXT: computedStyle.getPropertyValue( + '--color-scheduling-profiler-native-event-warning-text', + ), PRIORITY_BACKGROUND: computedStyle.getPropertyValue( '--color-scheduling-profiler-priority-background', ), @@ -172,6 +178,9 @@ export function updateColorsToMatchTheme(): void { REACT_WORK_BORDER: computedStyle.getPropertyValue( '--color-scheduling-profiler-react-work-border', ), + TEXT_COLOR: computedStyle.getPropertyValue( + '--color-scheduling-profiler-text-color', + ), TIME_MARKER_LABEL: computedStyle.getPropertyValue('--color-text'), }; } diff --git a/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js b/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js index 2140c40ce490a..f3435745b37d9 100644 --- a/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js +++ b/packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js @@ -16,6 +16,7 @@ import type { Milliseconds, BatchUID, Flamechart, + NativeEvent, ReactLane, ReactMeasureType, ReactProfilerData, @@ -39,6 +40,8 @@ type ProcessorState = {| measureStack: MeasureStackElement[], |}; +let nativeEventStack: Array = []; + // Exported for tests export function getLanesFromTransportDecimalBitmask( laneBitmaskString: string, @@ -178,15 +181,37 @@ function processTimelineEvent( } } - const startTime = (ts - currentProfilerData.startTime) / 1000; + const timestamp = (ts - currentProfilerData.startTime) / 1000; const duration = event.dur / 1000; - // TODO (scheduling profiler) Should we filter out certain event types? - currentProfilerData.nativeEvents.push({ + let depth = 0; + + while (nativeEventStack.length > 0) { + const prevNativeEvent = nativeEventStack[nativeEventStack.length - 1]; + const prevStopTime = + prevNativeEvent.timestamp + prevNativeEvent.duration; + + if (timestamp < prevStopTime) { + depth = prevNativeEvent.depth + 1; + break; + } else { + nativeEventStack.pop(); + } + } + + const nativeEvent = { + depth, duration, - timestamp: startTime, + highlight: false, + timestamp, type, - }); + }; + + currentProfilerData.nativeEvents.push(nativeEvent); + + // Keep track of curent event in case future ones overlap. + // We separate them into different vertical lanes in this case. + nativeEventStack.push(nativeEvent); } break; case 'blink.user_timing': @@ -309,6 +334,15 @@ function processTimelineEvent( currentProfilerData, state, ); + + for (let i = 0; i < nativeEventStack.length; i++) { + const nativeEvent = nativeEventStack[i]; + const stopTime = nativeEvent.timestamp + nativeEvent.duration; + if (stopTime > startTime) { + // Warn about sync updates that happen an event handler. + nativeEvent.highlight = true; + } + } } else if ( name.startsWith('--render-stop') || name.startsWith('--render-yield') @@ -470,6 +504,8 @@ function preprocessFlamechart(rawData: TimelineEvent[]): Flamechart { export default function preprocessData( timeline: TimelineEvent[], ): ReactProfilerData { + nativeEventStack = []; + const flamechart = preprocessFlamechart(timeline); const profilerData: ReactProfilerData = { diff --git a/packages/react-devtools-scheduling-profiler/src/types.js b/packages/react-devtools-scheduling-profiler/src/types.js index 3047e7abae06e..ece7d66034158 100644 --- a/packages/react-devtools-scheduling-profiler/src/types.js +++ b/packages/react-devtools-scheduling-profiler/src/types.js @@ -22,7 +22,9 @@ export type Milliseconds = number; export type ReactLane = number; export type NativeEvent = {| + +depth: number, +duration: Milliseconds, + highlight: boolean, +timestamp: Milliseconds, +type: string, |}; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js index 8967cca0f221c..e3fbec773ddcd 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js @@ -418,7 +418,7 @@ export function updateThemeVariables( updateStyleHelper(theme, 'color-search-match-current', documentElements); updateStyleHelper( theme, - 'color-scheduling-profiler-flame-graph-label', + 'color-scheduling-profiler-text-color', documentElements, ); updateStyleHelper( @@ -431,6 +431,21 @@ export function updateThemeVariables( 'color-scheduling-profiler-native-event-hover', documentElements, ); + updateStyleHelper( + theme, + 'color-scheduling-profiler-native-event-warning', + documentElements, + ); + updateStyleHelper( + theme, + 'color-scheduling-profiler-native-event-warning-hover', + documentElements, + ); + updateStyleHelper( + theme, + 'color-scheduling-profiler-native-event-warning-text', + documentElements, + ); updateStyleHelper( theme, 'color-selected-tree-highlight-active', diff --git a/packages/react-devtools-shared/src/devtools/views/root.css b/packages/react-devtools-shared/src/devtools/views/root.css index 13f67daba2252..7d69b4536a708 100644 --- a/packages/react-devtools-shared/src/devtools/views/root.css +++ b/packages/react-devtools-shared/src/devtools/views/root.css @@ -78,9 +78,11 @@ --light-color-record-hover: #3578e5; --light-color-record-inactive: #0088fa; --light-color-resize-bar: #cccccc; - --light-color-scheduling-profiler-flame-graph-label: #000000; - --light-color-scheduling-profiler-native-event: #aaaaaa; - --light-color-scheduling-profiler-native-event-hover: #888888; + --light-color-scheduling-profiler-native-event: #ccc; + --light-color-scheduling-profiler-native-event-hover: #aaa; + --light-color-scheduling-profiler-native-event-warning: #ee1638; + --light-color-scheduling-profiler-native-event-warning-hover: #da1030; + --light-color-scheduling-profiler-native-event-warning-text: #fff; --light-color-scheduling-profiler-priority-background: #f6f6f6; --light-color-scheduling-profiler-priority-border: #eeeeee; --light-color-scheduling-profiler-user-timing: #c9cacd; @@ -106,6 +108,7 @@ --light-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030; --light-color-scheduling-profiler-react-suspend: #a6e59f; --light-color-scheduling-profiler-react-suspend-hover:#13bc00; + --light-color-scheduling-profiler-text-color: #000000; --light-color-scheduling-profiler-react-work-border:#ffffff; --light-color-scroll-thumb: #c2c2c2; --light-color-scroll-track: #fafafa; @@ -200,9 +203,11 @@ --dark-color-record-hover: #a2e9fc; --dark-color-record-inactive: #61dafb; --dark-color-resize-bar: #3d424a; - --dark-color-scheduling-profiler-flame-graph-label: #000000; - --dark-color-scheduling-profiler-native-event: #aaaaaa; - --dark-color-scheduling-profiler-native-event-hover: #888888; + --dark-color-scheduling-profiler-native-event: #b2b2b2; + --dark-color-scheduling-profiler-native-event-hover: #949494; + --dark-color-scheduling-profiler-native-event-warning: #ee1638; + --dark-color-scheduling-profiler-native-event-warning-hover: #da1030; + --dark-color-scheduling-profiler-native-event-warning-text: #fff; --dark-color-scheduling-profiler-priority-background: #1d2129; --dark-color-scheduling-profiler-priority-border: #282c34; --dark-color-scheduling-profiler-user-timing: #c9cacd; @@ -228,6 +233,7 @@ --dark-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030; --dark-color-scheduling-profiler-react-suspend: #a6e59f; --dark-color-scheduling-profiler-react-suspend-hover:#13bc00; + --dark-color-scheduling-profiler-text-color: #000000; --dark-color-scheduling-profiler-react-work-border:#ffffff; --dark-color-scroll-thumb: #afb3b9; --dark-color-scroll-track: #313640;