Add Lane labels to scheduling profiler marks (#20808)

This commit changes scheduling profiler marks from a format like '--schedule-render-1' to '--schedule-render-1-Sync' (where 1 is the numeric value of the Sync lane). This will enable the profiler itself to show more meaningful labels for updates and render work.

The commit also refactors and adds additional tests for the scheduling profiler package.

It also updates the preprocessor to 'support' instant events. These are no-ops for us, but adding recognition of the event type will prevent profiles imported from e.g. Chrome Canary from throwing with an 'unrecognized event' error. (This will resolve issue #20767.)
This commit is contained in:
Brian Vaughn
2021-02-23 11:31:29 -05:00
committed by GitHub
parent c62986cfd8
commit e5f6b91d29
13 changed files with 1055 additions and 1817 deletions
@@ -249,7 +249,7 @@ const TooltipReactMeasure = ({
return null;
}
const {batchUID, duration, timestamp, lanes} = measure;
const {batchUID, duration, timestamp, lanes, laneLabels} = measure;
const [startTime, stopTime] = getBatchRange(batchUID, data);
return (
@@ -265,7 +265,11 @@ const TooltipReactMeasure = ({
<div className={styles.DetailsGridLabel}>
Lane{lanes.length === 1 ? '' : 's'}:
</div>
<div>{lanes.join(', ')}</div>
<div>
{laneLabels.length > 0
? `${laneLabels.join(', ')} (${lanes.join(', ')})`
: lanes.join(', ')}
</div>
</div>
</div>
);
@@ -11,5 +11,6 @@ export {
COMFORTABLE_LINE_HEIGHT,
COMPACT_LINE_HEIGHT,
} from 'react-devtools-shared/src/constants.js';
import {TotalLanes} from 'react-reconciler/src/ReactFiberLane.new';
export const REACT_TOTAL_NUM_LANES = 31;
export const REACT_TOTAL_NUM_LANES = TotalLanes;
@@ -0,0 +1,602 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
import preprocessData, {
getLanesFromTransportDecimalBitmask,
} from '../preprocessData';
import {REACT_TOTAL_NUM_LANES} from '../../constants';
describe(getLanesFromTransportDecimalBitmask, () => {
it('should return array of lane numbers from bitmask string', () => {
expect(getLanesFromTransportDecimalBitmask('1')).toEqual([0]);
expect(getLanesFromTransportDecimalBitmask('512')).toEqual([9]);
expect(getLanesFromTransportDecimalBitmask('3')).toEqual([0, 1]);
expect(getLanesFromTransportDecimalBitmask('1234')).toEqual([
1,
4,
6,
7,
10,
]); // 2 + 16 + 64 + 128 + 1024
expect(
getLanesFromTransportDecimalBitmask('1073741824'), // 0b1000000000000000000000000000000
).toEqual([30]);
expect(
getLanesFromTransportDecimalBitmask('2147483647'), // 0b1111111111111111111111111111111
).toEqual(Array.from(Array(31).keys()));
});
it('should return empty array if laneBitmaskString is not a bitmask', () => {
expect(getLanesFromTransportDecimalBitmask('')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('hello')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('-1')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('-0')).toEqual([]);
});
it('should ignore lanes outside REACT_TOTAL_NUM_LANES', () => {
// Sanity check; this test may need to be updated when the no. of fiber
// lanes are changed.
expect(REACT_TOTAL_NUM_LANES).toBe(31);
expect(
getLanesFromTransportDecimalBitmask(
'4294967297', // 2^32 + 1
),
).toEqual([0]);
});
});
describe(preprocessData, () => {
let React;
let ReactDOM;
let act;
let clearedMarks;
let featureDetectionMarkName = null;
let marks;
function polyfillJSDomUserTiming() {
featureDetectionMarkName = null;
clearedMarks = [];
marks = [];
// This is not a true polyfill, but it gives us enough to capture marks.
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
// JSDom already implements global.performance and it can't be overridden.
// However it only supports now() and timeOrigin() so we need to "upgrade it" in place.
if (!global.performance) {
global.performance = {};
}
global.performance.clearMarks = function clearMarks(markName) {
clearedMarks.push(markName);
marks = marks.filter(mark => mark !== markName);
};
global.performance.mark = function mark(markName, markOptions) {
if (featureDetectionMarkName === null) {
featureDetectionMarkName = markName;
}
marks.push(markName);
if (markOptions != null) {
// This is triggers the feature detection.
markOptions.startTime++;
}
};
}
// These should be dynamic to mimic a real profile,
// but reprooducible between test runs.
let pid = 0;
let tid = 0;
let startTime = 0;
function createUserTimingEntry(data) {
return {
pid: ++pid,
tid: ++tid,
ts: ++startTime,
...data,
};
}
function createUserTimingData(sampleMarks) {
const cpuProfilerSample = createUserTimingEntry({
args: {data: {startTime: ++startTime}},
cat: 'disabled-by-default-v8.cpu_profiler',
id: '0x1',
name: 'Profile',
ph: 'P',
});
const randomSample = createUserTimingEntry({
dur: 100,
tdur: 200,
ph: 'X',
cat: 'disabled-by-default-devtools.timeline',
name: 'RunTask',
args: {},
});
const userTimingData = [cpuProfilerSample, randomSample];
sampleMarks.forEach(markName => {
userTimingData.push({
pid: ++pid,
tid: ++tid,
ts: ++startTime,
args: {data: {navigationId: 'fake-navigation-id'}},
cat: 'blink.user_timing',
name: markName,
ph: 'R',
});
});
return userTimingData;
}
beforeEach(() => {
jest.resetModules();
polyfillJSDomUserTiming();
React = require('react');
ReactDOM = require('react-dom');
const TestUtils = require('react-dom/test-utils');
act = TestUtils.act;
tid = 0;
pid = 0;
startTime = 0;
});
afterEach(() => {
// Verify all logged marks also get cleared.
expect(marks).toHaveLength(0);
delete global.performance;
});
it('should throw given an empty timeline', () => {
expect(() => preprocessData([])).toThrow();
});
it('should throw given a timeline with no Profile event', () => {
const randomSample = createUserTimingEntry({
dur: 100,
tdur: 200,
ph: 'X',
cat: 'disabled-by-default-devtools.timeline',
name: 'RunTask',
args: {},
});
expect(() => preprocessData([randomSample])).toThrow();
});
it('should return empty data given a timeline with no React scheduling profiling marks', () => {
const cpuProfilerSample = createUserTimingEntry({
args: {data: {startTime: ++startTime}},
cat: 'disabled-by-default-v8.cpu_profiler',
id: '0x1',
name: 'Profile',
ph: 'P',
});
const randomSample = createUserTimingEntry({
dur: 100,
tdur: 200,
ph: 'X',
cat: 'disabled-by-default-devtools.timeline',
name: 'RunTask',
args: {},
});
expect(preprocessData([cpuProfilerSample, randomSample])).toStrictEqual({
duration: 0.002,
events: [],
flamechart: [],
measures: [],
otherUserTimingMarks: [],
startTime: 1,
});
});
// NOTE This test doesn't have to be gated because it has hard-coded profiler samples.
it('should process legacy data format (before lane labels were added)', () => {
const cpuProfilerSample = createUserTimingEntry({
args: {data: {startTime: ++startTime}},
cat: 'disabled-by-default-v8.cpu_profiler',
id: '0x1',
name: 'Profile',
ph: 'P',
});
expect(
// Data below is hard-coded based on an older profile sample.
// Should be fine since this is explicitly a legacy-format test.
preprocessData([
cpuProfilerSample,
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--schedule-render-512-',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--render-start-512',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--render-stop',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--commit-start-512',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--layout-effects-start-512',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--layout-effects-stop',
}),
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--commit-stop',
}),
]),
).toStrictEqual({
duration: 0.008,
events: [
{
componentStack: '',
laneLabels: [],
lanes: [9],
timestamp: 0.002,
type: 'schedule-render',
},
],
flamechart: [],
measures: [
{
batchUID: 0,
depth: 0,
duration: 0.005,
laneLabels: [],
lanes: [9],
timestamp: 0.003,
type: 'render-idle',
},
{
batchUID: 0,
depth: 0,
duration: 0.001,
laneLabels: [],
lanes: [9],
timestamp: 0.003,
type: 'render',
},
{
batchUID: 0,
depth: 0,
duration: 0.003,
laneLabels: [],
lanes: [9],
timestamp: 0.005,
type: 'commit',
},
{
batchUID: 0,
depth: 1,
duration: 0.001,
laneLabels: [],
lanes: [9],
timestamp: 0.006,
type: 'layout-effects',
},
],
otherUserTimingMarks: [],
startTime: 1,
});
});
// @gate enableSchedulingProfiler
it('should process a sample legacy render sequence', () => {
ReactDOM.render(<div />, document.createElement('div'));
const reactVersion = require('shared/ReactVersion').default;
const userTimingData = createUserTimingData(clearedMarks);
expect(preprocessData(userTimingData)).toStrictEqual({
duration: 0.011,
events: [
{
componentStack: '',
laneLabels: ['Sync'],
lanes: [0],
timestamp: 0.005,
type: 'schedule-render',
},
],
flamechart: [],
measures: [
{
batchUID: 0,
depth: 0,
duration: 0.004999999999999999,
laneLabels: ['Sync'],
lanes: [0],
timestamp: 0.006,
type: 'render-idle',
},
{
batchUID: 0,
depth: 0,
duration: 0.001,
laneLabels: ['Sync'],
lanes: [0],
timestamp: 0.006,
type: 'render',
},
{
batchUID: 0,
depth: 0,
duration: 0.002999999999999999,
laneLabels: ['Sync'],
lanes: [0],
timestamp: 0.008,
type: 'commit',
},
{
batchUID: 0,
depth: 1,
duration: 0.0010000000000000009,
laneLabels: ['Sync'],
lanes: [0],
timestamp: 0.009,
type: 'layout-effects',
},
],
otherUserTimingMarks: [
{
name: '__v3',
timestamp: 0.003,
},
{
name: `--react-init-${reactVersion}`,
timestamp: 0.004,
},
],
startTime: 1,
});
});
// @gate experimental && enableSchedulingProfiler
it('should process a sample createRoot render sequence', () => {
function App() {
const [didMount, setDidMount] = React.useState(false);
React.useEffect(() => {
if (!didMount) {
setDidMount(true);
}
});
return true;
}
const root = ReactDOM.createRoot(document.createElement('div'));
act(() => root.render(<App />));
const userTimingData = createUserTimingData(clearedMarks);
expect(preprocessData(userTimingData)).toStrictEqual({
duration: 0.022,
events: [
{
componentStack: '',
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.005,
type: 'schedule-render',
},
{
componentName: 'App',
componentStack: '',
isCascading: false,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.013,
type: 'schedule-state-update',
},
],
flamechart: [],
measures: [
{
batchUID: 0,
depth: 0,
duration: 0.004999999999999999,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.006,
type: 'render-idle',
},
{
batchUID: 0,
depth: 0,
duration: 0.001,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.006,
type: 'render',
},
{
batchUID: 0,
depth: 0,
duration: 0.002999999999999999,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.008,
type: 'commit',
},
{
batchUID: 0,
depth: 1,
duration: 0.0010000000000000009,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.009,
type: 'layout-effects',
},
{
batchUID: 0,
depth: 0,
duration: 0.002,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.012,
type: 'passive-effects',
},
{
batchUID: 1,
depth: 0,
duration: 0.005000000000000001,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.015,
type: 'render-idle',
},
{
batchUID: 1,
depth: 0,
duration: 0.0010000000000000009,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.015,
type: 'render',
},
{
batchUID: 1,
depth: 0,
duration: 0.002999999999999999,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.017,
type: 'commit',
},
{
batchUID: 1,
depth: 1,
duration: 0.0010000000000000009,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.018,
type: 'layout-effects',
},
{
batchUID: 1,
depth: 0,
duration: 0.0009999999999999974,
laneLabels: ['Default'],
lanes: [7],
timestamp: 0.021,
type: 'passive-effects',
},
],
otherUserTimingMarks: [
{
name: '__v3',
timestamp: 0.003,
},
{
name: '--react-init-17.0.2',
timestamp: 0.004,
},
],
startTime: 1,
});
});
// @gate enableSchedulingProfiler
it('should error if events and measures are incomplete', () => {
const container = document.createElement('div');
ReactDOM.render(<div />, container);
const invalidMarks = clearedMarks.filter(
mark => !mark.includes('render-stop'),
);
const invalidUserTimingData = createUserTimingData(invalidMarks);
const error = spyOnDevAndProd(console, 'error');
preprocessData(invalidUserTimingData);
expect(error).toHaveBeenCalled();
});
// @gate enableSchedulingProfiler
it('should error if work is completed without being started', () => {
const container = document.createElement('div');
ReactDOM.render(<div />, container);
const invalidMarks = clearedMarks.filter(
mark => !mark.includes('render-start'),
);
const invalidUserTimingData = createUserTimingData(invalidMarks);
const error = spyOnDevAndProd(console, 'error');
preprocessData(invalidUserTimingData);
expect(error).toHaveBeenCalled();
});
it('should populate other user timing marks', () => {
const userTimingData = createUserTimingData([]);
userTimingData.push(
createUserTimingEntry({
args: {},
cat: 'blink.user_timing',
id: '0xcdf75f7c',
name: 'VCWithoutImage: root',
ph: 'n',
scope: 'blink.user_timing',
}),
);
userTimingData.push(
createUserTimingEntry({
cat: 'blink.user_timing',
name: '--a-mark-that-looks-like-one-of-ours',
ph: 'R',
}),
);
userTimingData.push(
createUserTimingEntry({
cat: 'blink.user_timing',
name: 'Some other mark',
ph: 'R',
}),
);
expect(preprocessData(userTimingData).otherUserTimingMarks).toStrictEqual([
{
name: 'VCWithoutImage: root',
timestamp: 0.003,
},
{
name: '--a-mark-that-looks-like-one-of-ours',
timestamp: 0.004,
},
{
name: 'Some other mark',
timestamp: 0.005,
},
]);
});
// TODO: Add test for flamechart parsing
});
@@ -1,352 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
import preprocessData, {
getLanesFromTransportDecimalBitmask,
} from '../preprocessData';
import {REACT_TOTAL_NUM_LANES} from '../../constants';
// Disable quotes rule in the whole file as we paste raw JSON as test inputs and
// Prettier will already format the remaining quotes.
/* eslint-disable quotes */
describe(getLanesFromTransportDecimalBitmask, () => {
it('should return array of lane numbers from bitmask string', () => {
expect(getLanesFromTransportDecimalBitmask('1')).toEqual([0]);
expect(getLanesFromTransportDecimalBitmask('512')).toEqual([9]);
expect(getLanesFromTransportDecimalBitmask('3')).toEqual([0, 1]);
expect(getLanesFromTransportDecimalBitmask('1234')).toEqual([
1,
4,
6,
7,
10,
]); // 2 + 16 + 64 + 128 + 1024
expect(
getLanesFromTransportDecimalBitmask('1073741824'), // 0b1000000000000000000000000000000
).toEqual([30]);
expect(
getLanesFromTransportDecimalBitmask('2147483647'), // 0b1111111111111111111111111111111
).toEqual(Array.from(Array(31).keys()));
});
it('should return empty array if laneBitmaskString is not a bitmask', () => {
expect(getLanesFromTransportDecimalBitmask('')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('hello')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('-1')).toEqual([]);
expect(getLanesFromTransportDecimalBitmask('-0')).toEqual([]);
});
it('should ignore lanes outside REACT_TOTAL_NUM_LANES', () => {
// Sanity check; this test may need to be updated when the no. of fiber
// lanes are changed.
expect(REACT_TOTAL_NUM_LANES).toBe(31);
expect(
getLanesFromTransportDecimalBitmask(
'4294967297', // 2^32 + 1
),
).toEqual([0]);
});
});
describe(preprocessData, () => {
it('should throw given an empty timeline', () => {
expect(() => preprocessData([])).toThrow();
});
it('should throw given a timeline with no Profile event', () => {
expect(() =>
// prettier-ignore
preprocessData([
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--schedule-render-512-","ph":"R","pid":9312,"tid":10252,"ts":8994056569,"tts":1816966},
]),
).toThrow();
});
it('should return empty data given a timeline with no React scheduling profiling marks', () => {
expect(
// prettier-ignore
preprocessData([
{"args":{"data":{"startTime":8993778496}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x1","name":"Profile","ph":"P","pid":9312,"tid":10252,"ts":8993778520,"tts":1614266},
{"pid":57632,"tid":38659,"ts":874860756135,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":18,"tdur":19,"tts":8700284918,"args":{}},
{"pid":57632,"tid":38659,"ts":874860756158,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":30,"tdur":30,"tts":8700284941,"args":{}},
{"pid":57632,"tid":38659,"ts":874860756192,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":21,"tdur":20,"tts":8700284976,"args":{}},
{"pid":57632,"tid":38659,"ts":874860756216,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":6,"tdur":5,"tts":8700285000,"args":{}},
{"pid":57632,"tid":38659,"ts":874860756224,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":7,"tdur":6,"tts":8700285008,"args":{}},
{"pid":57632,"tid":38659,"ts":874860756233,"ph":"X","cat":"disabled-by-default-devtools.timeline","name":"RunTask","dur":5,"tdur":4,"tts":8700285017,"args":{}},
]),
).toEqual({
startTime: 8993778496,
duration: 865866977.737,
events: [],
measures: [],
flamechart: [],
otherUserTimingMarks: [],
});
});
it('should error if events and measures are incomplete', () => {
const error = spyOnDevAndProd(console, 'error');
// prettier-ignore
preprocessData([
{"args":{"data":{"startTime":8993778496}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x1","name":"Profile","ph":"P","pid":9312,"tid":10252,"ts":8993778520,"tts":1614266},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--render-start-8","ph":"R","pid":1852,"tid":12484,"ts":42351664678,"tts":1512475},
]);
expect(error).toHaveBeenCalled();
});
it('should error if work is completed without being started', () => {
const error = spyOnDevAndProd(console, 'error');
// prettier-ignore
preprocessData([
{"args":{"data":{"startTime":8993778496}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x1","name":"Profile","ph":"P","pid":9312,"tid":10252,"ts":8993778520,"tts":1614266},
{"args":{"data":{"navigationId":"E082C30FBDA3ACEE0E7B5FD75F8B7F0D"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":17232,"tid":13628,"ts":264686513020,"tts":4082554},
]);
expect(error).toHaveBeenCalled();
});
it('should process complete set of events (page load sample data)', () => {
expect(
// prettier-ignore
preprocessData([
{"args":{"data":{"documentLoaderURL":"https://concurrent-demo.now.sh/","isLoadingMainFrame":true,"navigationId":"43BC238A4FB7548146D3CD739C9C9434"},"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"navigationStart","ph":"R","pid":9312,"tid":10252,"ts":8993749139,"tts":1646191},
{"args":{"data":{"startTime":8993778496}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x1","name":"Profile","ph":"P","pid":9312,"tid":10252,"ts":8993778520,"tts":1614266},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"fetchStart","ph":"R","pid":9312,"tid":10252,"ts":8993751576,"tts":1646197},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8993757325,"tts":1612760},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"responseEnd","ph":"R","pid":9312,"tid":10252,"ts":8993762841,"tts":1652151},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"unloadEventStart","ph":"R","pid":9312,"tid":10252,"ts":8993777756,"tts":1646416},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"unloadEventEnd","ph":"R","pid":9312,"tid":10252,"ts":8993818104,"tts":1646419},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing,rail","name":"domLoading","ph":"R","pid":9312,"tid":10252,"ts":8993820215,"tts":1647488},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8993886145,"tts":1771277},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8993886881,"tts":1778953},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--schedule-render-512-","ph":"R","pid":9312,"tid":10252,"ts":8994056569,"tts":1816966},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing,rail","name":"domInteractive","ph":"R","pid":9312,"tid":10252,"ts":8994058638,"tts":1818851},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing,rail","name":"domContentLoadedEventStart","ph":"R","pid":9312,"tid":10252,"ts":8994058898,"tts":1819078},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing,rail","name":"domContentLoadedEventEnd","ph":"R","pid":9312,"tid":10252,"ts":8994060045,"tts":1820100},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994063789,"tts":1823183},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994069024,"tts":1826507},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994076204,"tts":1830657},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994084372,"tts":1837590},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing,rail","name":"domComplete","ph":"R","pid":9312,"tid":10252,"ts":8994085517,"tts":1838615},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"loadEventStart","ph":"R","pid":9312,"tid":10252,"ts":8994085552,"tts":1838649},
{"args":{"frame":"FD65D9AFD04B1295CEA36B883F0FA82F"},"cat":"blink.user_timing","name":"loadEventEnd","ph":"R","pid":9312,"tid":10252,"ts":8994086738,"tts":1839690},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994088953,"tts":1840749},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994093942,"tts":1844455},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8994119825,"tts":1877483},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8994122516,"tts":1886344},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994136263,"tts":1871212},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994142733,"tts":1875838},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994149982,"tts":1880208},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994156615,"tts":1885309},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994163546,"tts":1887453},
{"args":{},"cat":"blink.user_timing","name":"requestStart","ph":"R","pid":9312,"tid":10252,"ts":8994166081,"tts":1895751},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994173436,"tts":1894442},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994176556,"tts":1897176},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994184415,"tts":1904263},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994185162,"tts":1904938},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994192423,"tts":1910624},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994192714,"tts":1910872},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994200415,"tts":1917859},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994200637,"tts":1918062},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994208430,"tts":1924894},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994208681,"tts":1925124},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994216388,"tts":1932117},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994218048,"tts":1933622},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994225455,"tts":1940076},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994225790,"tts":1940391},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994233439,"tts":1947224},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994233711,"tts":1947473},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994241447,"tts":1954160},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994241755,"tts":1954426},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994249451,"tts":1961213},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994249743,"tts":1961494},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994257444,"tts":1968141},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994257858,"tts":1968525},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994265413,"tts":1975172},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994265691,"tts":1975416},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994273481,"tts":1981826},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994273850,"tts":1982112},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994281467,"tts":1988537},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994282202,"tts":1988894},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994289444,"tts":1994103},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994291602,"tts":1995165},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994299421,"tts":2000342},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994300163,"tts":2001009},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994309433,"tts":2005662},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994309681,"tts":2005897},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994317439,"tts":2012641},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994317709,"tts":2012890},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994325444,"tts":2019235},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994325719,"tts":2019477},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994333449,"tts":2026367},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994333730,"tts":2026617},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994341427,"tts":2033147},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994341728,"tts":2033411},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994349443,"tts":2040163},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994349708,"tts":2040409},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994357469,"tts":2047136},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994357820,"tts":2047465},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994365438,"tts":2054203},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994365697,"tts":2054434},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994373435,"tts":2061259},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994373705,"tts":2061502},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994381444,"tts":2068300},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994381690,"tts":2068529},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994389422,"tts":2075272},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994389708,"tts":2075518},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994397421,"tts":2082156},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994398070,"tts":2082726},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994405440,"tts":2088860},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994405684,"tts":2089091},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994413420,"tts":2095780},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994413689,"tts":2096021},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994421418,"tts":2102791},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994421702,"tts":2103047},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994429611,"tts":2110142},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994429835,"tts":2110349},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994436841,"tts":2115594},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994437451,"tts":2116087},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994437941,"tts":2116287},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-1-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994439235,"tts":2117153},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994441050,"tts":2118088},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994441951,"tts":2118783},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994442698,"tts":2119371},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994443276,"tts":2119875},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994443448,"tts":2120040},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994445176,"tts":2121499},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-1-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994445697,"tts":2121968},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994446236,"tts":2122460},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994446778,"tts":2122951},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994447344,"tts":2123444},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-yield","ph":"R","pid":9312,"tid":10252,"ts":8994449037,"tts":2124925},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994449280,"tts":2125142},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at SuspenseList\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994449831,"tts":2125639},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8994450864,"tts":2126555},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994451820,"tts":2127417},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-512","ph":"R","pid":9312,"tid":10252,"ts":8994455732,"tts":2130777},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--schedule-state-update-1-ForceUpdateDemo_ForceUpdateDemo-\n at ForceUpdateDemo_ForceUpdateDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:18:98)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8994457934,"tts":2132671},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8994458421,"tts":2133089},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-1","ph":"R","pid":9312,"tid":10252,"ts":8994462600,"tts":2136847},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8994464817,"tts":2138817},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-1","ph":"R","pid":9312,"tid":10252,"ts":8994464844,"tts":2138843},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-1","ph":"R","pid":9312,"tid":10252,"ts":8994465763,"tts":2139664},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8994465784,"tts":2139686},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8994466156,"tts":2140023},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8994466372,"tts":2140208},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-1-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995573418,"tts":2205582},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-1-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995573870,"tts":2205980},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-1024","ph":"R","pid":9312,"tid":10252,"ts":8995574538,"tts":2206568},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995575662,"tts":2207534},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995576307,"tts":2208142},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995576659,"tts":2208445},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995577001,"tts":2208736},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8995577971,"tts":2209602},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-1024","ph":"R","pid":9312,"tid":10252,"ts":8995578068,"tts":2209689},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-1024","ph":"R","pid":9312,"tid":10252,"ts":8995580101,"tts":2211495},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8995580122,"tts":2211515},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8995580657,"tts":2211995},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995644745,"tts":2217336},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995645038,"tts":2217571},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-2-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995645354,"tts":2217861},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-2048","ph":"R","pid":9312,"tid":10252,"ts":8995645494,"tts":2217999},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995646312,"tts":2218721},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995647134,"tts":2219450},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995647462,"tts":2219740},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8995648162,"tts":2220335},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-2048","ph":"R","pid":9312,"tid":10252,"ts":8995648191,"tts":2220363},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-2048","ph":"R","pid":9312,"tid":10252,"ts":8995649260,"tts":2221320},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8995649280,"tts":2221340},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8995649611,"tts":2221636},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995758769,"tts":2228839},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995759018,"tts":2229064},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995759442,"tts":2229424},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-3-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995759660,"tts":2229627},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-4096","ph":"R","pid":9312,"tid":10252,"ts":8995759857,"tts":2229803},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995760575,"tts":2230456},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8995761666,"tts":2231399},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8995762296,"tts":2231965},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-4096","ph":"R","pid":9312,"tid":10252,"ts":8995762367,"tts":2232017},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-4096","ph":"R","pid":9312,"tid":10252,"ts":8995763427,"tts":2232966},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8995763454,"tts":2232993},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8995763625,"tts":2233154},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996158083,"tts":2252466},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996158391,"tts":2252730},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996158738,"tts":2253038},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996158983,"tts":2253239},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-4-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996159231,"tts":2253447},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-8192","ph":"R","pid":9312,"tid":10252,"ts":8996159465,"tts":2253624},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-suspend-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996160268,"tts":2254312},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8996161775,"tts":2255670},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-8192","ph":"R","pid":9312,"tid":10252,"ts":8996161834,"tts":2255716},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-8192","ph":"R","pid":9312,"tid":10252,"ts":8996163031,"tts":2256754},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8996163051,"tts":2256773},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8996163355,"tts":2257045},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996357682,"tts":2267920},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996357983,"tts":2268179},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at SuspenseList\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996358231,"tts":2268389},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996358538,"tts":2268679},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996358786,"tts":2268867},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996359042,"tts":2269066},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--suspense-resolved-0-ResourceButton-\n at ResourceButton (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:295)\n at Suspense\n at div\n at div\n at SuspenseDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:36:713)\n at App","ph":"R","pid":9312,"tid":10252,"ts":8996359296,"tts":2269264},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-start-16384","ph":"R","pid":9312,"tid":10252,"ts":8996359448,"tts":2269412},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":9312,"tid":10252,"ts":8996362873,"tts":2272363},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-start-16384","ph":"R","pid":9312,"tid":10252,"ts":8996362944,"tts":2272420},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-start-16384","ph":"R","pid":9312,"tid":10252,"ts":8996364618,"tts":2273869},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":9312,"tid":10252,"ts":8996364645,"tts":2273895},
{"args":{"data":{"navigationId":"43BC238A4FB7548146D3CD739C9C9434"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":9312,"tid":10252,"ts":8996365391,"tts":2274547},
]),
).toMatchSnapshot();
});
it('should process forced update event', () => {
expect(
// prettier-ignore
preprocessData([
{"args":{"data":{"startTime":40806924876}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x2","name":"Profile","ph":"P","pid":1852,"tid":12484,"ts":40806924880,"tts":996658},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--schedule-forced-update-16-ForceUpdateDemo_ForceUpdateDemo-\n at ForceUpdateDemo_ForceUpdateDemo (https://concurrent-demo.now.sh/static/js/main.c9f122eb.chunk.js:18:98)\n at App","ph":"R","pid":1852,"tid":12484,"ts":40806988231,"tts":1037762},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--render-start-16","ph":"R","pid":1852,"tid":12484,"ts":40806990146,"tts":1038890},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--render-stop","ph":"R","pid":1852,"tid":12484,"ts":40806991123,"tts":1039401},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--commit-start-16","ph":"R","pid":1852,"tid":12484,"ts":40806991170,"tts":1039447},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--layout-effects-start-16","ph":"R","pid":1852,"tid":12484,"ts":40806992201,"tts":1040023},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--layout-effects-stop","ph":"R","pid":1852,"tid":12484,"ts":40806992219,"tts":1040041},
{"args":{"data":{"navigationId":"1065756F5FDAD64BE45CA86B0BBC1F8B"}},"cat":"blink.user_timing","name":"--commit-stop","ph":"R","pid":1852,"tid":12484,"ts":40806992337,"tts":1040149},
]),
).toMatchSnapshot();
});
it('should populate other user timing marks', () => {
expect(
// prettier-ignore
preprocessData([
{"args":{},"cat":"blink.user_timing","id":"0xcdf75f7c","name":"VCWithoutImage: root","ph":"n","pid":55132,"scope":"blink.user_timing","tid":775,"ts":458734963394},
{"args":{"data":{"startTime":458738069897}},"cat":"disabled-by-default-v8.cpu_profiler","id":"0x4","name":"Profile","ph":"P","pid":55132,"tid":775,"ts":458738069898,"tts":27896428},
{"args":{"data":{"navigationId":"B8774C733A75946C099FE21F8A0E8D38"}},"cat":"blink.user_timing","name":"--a-mark-that-looks-like-one-of-ours","ph":"R","pid":55132,"tid":775,"ts":458738256356,"tts":28082555},
{"args":{"data":{"navigationId":"B8774C733A75946C099FE21F8A0E8D38"}},"cat":"blink.user_timing","name":"Some other mark","ph":"R","pid":55132,"tid":775,"ts":458738261491,"tts":28087691},
]).otherUserTimingMarks,
).toMatchInlineSnapshot(`
Array [
Object {
"name": "VCWithoutImage: root",
"timestamp": -3106.503,
},
Object {
"name": "--a-mark-that-looks-like-one-of-ours",
"timestamp": 186.459,
},
Object {
"name": "Some other mark",
"timestamp": 191.594,
},
]
`);
});
// TODO: Add test for flamechart parsing
});
@@ -82,6 +82,7 @@ function markWorkStarted(
type: ReactMeasureType,
startTime: Milliseconds,
lanes: ReactLane[],
laneLabels: Array<string>,
currentProfilerData: ReactProfilerData,
state: ProcessorState,
) {
@@ -96,6 +97,7 @@ function markWorkStarted(
batchUID,
depth,
lanes,
laneLabels,
timestamp: startTime,
duration: 0,
});
@@ -168,18 +170,20 @@ function processTimelineEvent(
// React Events - schedule
if (name.startsWith('--schedule-render-')) {
const [laneBitmaskString, ...splitComponentStack] = name
const [laneBitmaskString, laneLabels, ...splitComponentStack] = name
.substr(18)
.split('-');
currentProfilerData.events.push({
type: 'schedule-render',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
laneLabels: laneLabels ? laneLabels.split(',') : [],
componentStack: splitComponentStack.join('-'),
timestamp: startTime,
});
} else if (name.startsWith('--schedule-forced-update-')) {
const [
laneBitmaskString,
laneLabels,
componentName,
...splitComponentStack
] = name.substr(25).split('-');
@@ -189,6 +193,7 @@ function processTimelineEvent(
currentProfilerData.events.push({
type: 'schedule-force-update',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
laneLabels: laneLabels ? laneLabels.split(',') : [],
componentName,
componentStack: splitComponentStack.join('-'),
timestamp: startTime,
@@ -197,6 +202,7 @@ function processTimelineEvent(
} else if (name.startsWith('--schedule-state-update-')) {
const [
laneBitmaskString,
laneLabels,
componentName,
...splitComponentStack
] = name.substr(24).split('-');
@@ -206,6 +212,7 @@ function processTimelineEvent(
currentProfilerData.events.push({
type: 'schedule-state-update',
lanes: getLanesFromTransportDecimalBitmask(laneBitmaskString),
laneLabels: laneLabels ? laneLabels.split(',') : [],
componentName,
componentStack: splitComponentStack.join('-'),
timestamp: startTime,
@@ -255,7 +262,7 @@ function processTimelineEvent(
state.nextRenderShouldGenerateNewBatchID = false;
state.batchUID = ((state.uidCounter++: any): BatchUID);
}
const laneBitmaskString = name.substr(15);
const [laneBitmaskString, laneLabels] = name.substr(15).split('-');
const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
throwIfIncomplete('render', state.measureStack);
if (getLastType(state.measureStack) !== 'render-idle') {
@@ -263,11 +270,19 @@ function processTimelineEvent(
'render-idle',
startTime,
lanes,
laneLabels ? laneLabels.split(',') : [],
currentProfilerData,
state,
);
}
markWorkStarted('render', startTime, lanes, currentProfilerData, state);
markWorkStarted(
'render',
startTime,
lanes,
laneLabels ? laneLabels.split(',') : [],
currentProfilerData,
state,
);
} else if (
name.startsWith('--render-stop') ||
name.startsWith('--render-yield')
@@ -297,9 +312,16 @@ function processTimelineEvent(
// React Measures - commits
else if (name.startsWith('--commit-start-')) {
state.nextRenderShouldGenerateNewBatchID = true;
const laneBitmaskString = name.substr(15);
const [laneBitmaskString, laneLabels] = name.substr(15).split('-');
const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
markWorkStarted('commit', startTime, lanes, currentProfilerData, state);
markWorkStarted(
'commit',
startTime,
lanes,
laneLabels ? laneLabels.split(',') : [],
currentProfilerData,
state,
);
} else if (name.startsWith('--commit-stop')) {
markWorkCompleted(
'commit',
@@ -317,12 +339,13 @@ function processTimelineEvent(
// React Measures - layout effects
else if (name.startsWith('--layout-effects-start-')) {
const laneBitmaskString = name.substr(23);
const [laneBitmaskString, laneLabels] = name.substr(23).split('-');
const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
markWorkStarted(
'layout-effects',
startTime,
lanes,
laneLabels ? laneLabels.split(',') : [],
currentProfilerData,
state,
);
@@ -337,12 +360,13 @@ function processTimelineEvent(
// React Measures - passive effects
else if (name.startsWith('--passive-effects-start-')) {
const laneBitmaskString = name.substr(24);
const [laneBitmaskString, laneLabels] = name.substr(24).split('-');
const lanes = getLanesFromTransportDecimalBitmask(laneBitmaskString);
markWorkStarted(
'passive-effects',
startTime,
lanes,
laneLabels ? laneLabels.split(',') : [],
currentProfilerData,
state,
);
@@ -366,6 +390,9 @@ function processTimelineEvent(
// TODO: Begin user timing measure
} else if (ph === 'e') {
// TODO: End user timing measure
} else if (ph === 'i' || ph === 'I') {
// Instant events.
// Note that the capital "I" is a deprecated value that exists in Chrome Canary traces.
} // eslint-disable-line brace-style
// Unrecognized event
@@ -30,6 +30,7 @@ type BaseReactEvent = {|
type BaseReactScheduleEvent = {|
...BaseReactEvent,
+lanes: ReactLane[],
+laneLabels: string[],
|};
export type ReactScheduleRenderEvent = {|
...BaseReactScheduleEvent,
@@ -86,6 +87,7 @@ export type BatchUID = number;
export type ReactMeasure = {|
+type: ReactMeasureType,
+lanes: ReactLane[],
+laneLabels: string[],
+timestamp: Milliseconds,
+duration: Milliseconds,
+batchUID: BatchUID,
@@ -56,6 +56,7 @@ const config = {
__DEV__,
__PROFILE__: false,
__EXPERIMENTAL__: true,
__VARIANT__: false,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
}),
new HtmlWebpackPlugin({
@@ -112,7 +113,7 @@ const config = {
if (shouldUseDevServer) {
config.devServer = {
hot: true,
port: 8080,
port: 8081,
clientLogLevel: 'warning',
stats: 'errors-only',
};
@@ -36,7 +36,7 @@ export type Lane = number;
export type LaneMap<T> = Array<T>;
import invariant from 'shared/invariant';
import {enableCache} from 'shared/ReactFeatureFlags';
import {enableCache, enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
import {
ImmediatePriority as ImmediateSchedulerPriority,
@@ -73,7 +73,10 @@ const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;
const TotalLanes = 31;
// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
// If those values are changed that package should be rebuilt and redeployed.
export const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
@@ -82,10 +85,10 @@ export const SyncLane: Lane = /* */ 0b0000000000000000000
export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100;
const InputDiscreteLane: Lanes = /* */ 0b0000000000000000000000000001000;
export const InputDiscreteLane: Lanes = /* */ 0b0000000000000000000000000001000;
const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000010000;
const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000100000;
export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000100000;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000001000000;
export const DefaultLane: Lanes = /* */ 0b0000000000000000000000010000000;
@@ -124,6 +127,60 @@ const IdleLane: Lanes = /* */ 0b0100000000000000000
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
// This function is used for the experimental scheduling profiler (react-devtools-scheduling-profiler)
// It should be kept in sync with the Lanes values above.
export function getLabelsForLanes(lanes: Lanes): Array<string> | void {
if (enableSchedulingProfiler) {
const labels = [];
if (lanes & SyncLane) {
labels.push('Sync');
}
if (lanes & SyncBatchedLane) {
labels.push('SyncBatched');
}
if (lanes & InputDiscreteHydrationLane) {
labels.push('InputDiscreteHydration');
}
if (lanes & InputDiscreteLane) {
labels.push('InputDiscrete');
}
if (lanes & InputContinuousHydrationLane) {
labels.push('InputContinuousHydration');
}
if (lanes & InputContinuousLane) {
labels.push('InputContinuous');
}
if (lanes & DefaultHydrationLane) {
labels.push('DefaultHydration');
}
if (lanes & DefaultLane) {
labels.push('Default');
}
if (lanes & TransitionHydrationLane) {
labels.push('TransitionHydration');
}
if (lanes & TransitionLanes) {
labels.push('Transition(s)');
}
if (lanes & RetryLanes) {
labels.push('Retry(s)');
}
if (lanes & SelectiveHydrationLane) {
labels.push('SelectiveHydration');
}
if (lanes & IdleHydrationLane) {
labels.push('IdleHydration');
}
if (lanes & IdleLane) {
labels.push('Idle');
}
if (lanes & OffscreenLane) {
labels.push('Offscreen');
}
return labels;
}
}
export const NoTimestamp = -1;
let currentUpdateLanePriority: LanePriority = NoLanePriority;
@@ -36,7 +36,7 @@ export type Lane = number;
export type LaneMap<T> = Array<T>;
import invariant from 'shared/invariant';
import {enableCache} from 'shared/ReactFeatureFlags';
import {enableCache, enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
import {
ImmediatePriority as ImmediateSchedulerPriority,
@@ -73,7 +73,10 @@ const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;
const TotalLanes = 31;
// Lane values below should be kept in sync with getLabelsForLanes(), used by react-devtools-scheduling-profiler.
// If those values are changed that package should be rebuilt and redeployed.
export const TotalLanes = 31;
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;
@@ -82,10 +85,10 @@ export const SyncLane: Lane = /* */ 0b0000000000000000000
export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100;
const InputDiscreteLane: Lanes = /* */ 0b0000000000000000000000000001000;
export const InputDiscreteLane: Lanes = /* */ 0b0000000000000000000000000001000;
const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000010000;
const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000100000;
export const InputContinuousLane: Lanes = /* */ 0b0000000000000000000000000100000;
export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000001000000;
export const DefaultLane: Lanes = /* */ 0b0000000000000000000000010000000;
@@ -124,6 +127,60 @@ const IdleLane: Lanes = /* */ 0b0100000000000000000
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
// This function is used for the experimental scheduling profiler (react-devtools-scheduling-profiler)
// It should be kept in sync with the Lanes values above.
export function getLabelsForLanes(lanes: Lanes): Array<string> | void {
if (enableSchedulingProfiler) {
const labels = [];
if (lanes & SyncLane) {
labels.push('Sync');
}
if (lanes & SyncBatchedLane) {
labels.push('SyncBatched');
}
if (lanes & InputDiscreteHydrationLane) {
labels.push('InputDiscreteHydration');
}
if (lanes & InputDiscreteLane) {
labels.push('InputDiscrete');
}
if (lanes & InputContinuousHydrationLane) {
labels.push('InputContinuousHydration');
}
if (lanes & InputContinuousLane) {
labels.push('InputContinuous');
}
if (lanes & DefaultHydrationLane) {
labels.push('DefaultHydration');
}
if (lanes & DefaultLane) {
labels.push('Default');
}
if (lanes & TransitionHydrationLane) {
labels.push('TransitionHydration');
}
if (lanes & TransitionLanes) {
labels.push('Transition(s)');
}
if (lanes & RetryLanes) {
labels.push('Retry(s)');
}
if (lanes & SelectiveHydrationLane) {
labels.push('SelectiveHydration');
}
if (lanes & IdleHydrationLane) {
labels.push('IdleHydration');
}
if (lanes & IdleLane) {
labels.push('Idle');
}
if (lanes & OffscreenLane) {
labels.push('Offscreen');
}
return labels;
}
}
export const NoTimestamp = -1;
let currentUpdateLanePriority: LanePriority = NoLanePriority;
+19 -3
View File
@@ -11,10 +11,20 @@ import type {Lane, Lanes} from './ReactFiberLane.old';
import type {Fiber} from './ReactInternalTypes';
import type {Wakeable} from 'shared/ReactTypes';
import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
import {
enableNewReconciler,
enableSchedulingProfiler,
} from 'shared/ReactFeatureFlags';
import ReactVersion from 'shared/ReactVersion';
import getComponentName from 'shared/getComponentName';
import {getLabelsForLanes as getLabelsForLanes_old} from 'react-reconciler/src/ReactFiberLane.old';
import {getLabelsForLanes as getLabelsForLanes_new} from 'react-reconciler/src/ReactFiberLane.new';
const getLabelsForLanes = enableNewReconciler
? getLabelsForLanes_new
: getLabelsForLanes_old;
/**
* If performance exists and supports the subset of the User Timing API that we
* require.
@@ -49,8 +59,14 @@ if (enableSchedulingProfiler) {
}
}
function formatLanes(laneOrLanes: Lane | Lanes): string {
return ((laneOrLanes: any): number).toString();
export function formatLanes(laneOrLanes: Lane | Lanes): string {
let labels = getLabelsForLanes(laneOrLanes);
if (labels != null) {
labels = labels.sort().join(',');
} else {
labels = '';
}
return `${laneOrLanes}-${labels}`;
}
function markAndClear(name) {
@@ -10,6 +10,7 @@
'use strict';
// This test is *.internal so that it can import this shared file.
import ReactVersion from 'shared/ReactVersion';
describe('SchedulingProfiler', () => {
@@ -17,9 +18,11 @@ describe('SchedulingProfiler', () => {
let ReactTestRenderer;
let ReactNoop;
let Scheduler;
let ReactFiberLane;
let clearedMarks;
let featureDetectionMarkName = null;
let formatLanes;
let marks;
function createUserTimingPolyfill() {
@@ -76,6 +79,14 @@ describe('SchedulingProfiler', () => {
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
const SchedulingProfiler = require('react-reconciler/src/SchedulingProfiler');
formatLanes = SchedulingProfiler.formatLanes;
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFiberLane = ReactFeatureFlags.enableNewReconciler
? require('react-reconciler/src/ReactFiberLane.new')
: require('react-reconciler/src/ReactFiberLane.old');
});
afterEach(() => {
@@ -85,9 +96,6 @@ describe('SchedulingProfiler', () => {
delete global.performance;
});
// This is coupled to implementation
const DEFAULT_LANE = 128;
// @gate !enableSchedulingProfiler
it('should not mark if enableSchedulingProfiler is false', () => {
ReactTestRenderer.create(<div />);
@@ -105,11 +113,11 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
'--schedule-render-1',
'--render-start-1',
`--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--render-stop',
'--commit-start-1',
'--layout-effects-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -121,7 +129,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -129,10 +137,10 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -156,8 +164,8 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--render-start-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-yield',
]);
});
@@ -177,12 +185,12 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
'--schedule-render-1',
'--render-start-1',
`--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--suspense-suspend-0-Example',
'--render-stop',
'--commit-start-1',
'--layout-effects-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -208,12 +216,12 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
'--schedule-render-1',
'--render-start-1',
`--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--suspense-suspend-0-Example',
'--render-stop',
'--commit-start-1',
'--layout-effects-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -240,7 +248,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -248,11 +256,11 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--suspense-suspend-0-Example',
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -279,7 +287,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -287,11 +295,11 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--suspense-suspend-0-Example',
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--layout-effects-stop',
'--commit-stop',
]);
@@ -318,7 +326,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -326,15 +334,15 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
'--schedule-state-update-1-Example',
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`,
'--layout-effects-stop',
'--render-start-1',
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--render-stop',
'--commit-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--commit-stop',
'--commit-stop',
]);
@@ -355,7 +363,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -363,15 +371,17 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
'--schedule-forced-update-1-Example',
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--schedule-forced-update-${formatLanes(
ReactFiberLane.SyncLane,
)}-Example`,
'--layout-effects-stop',
'--render-start-1',
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--render-stop',
'--commit-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--commit-stop',
'--commit-stop',
]);
@@ -393,7 +403,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -402,7 +412,11 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
}).toErrorDev('Cannot update during an existing state transition');
expectMarksToContain(`--schedule-state-update-${DEFAULT_LANE}-Example`);
expectMarksToContain(
`--schedule-state-update-${formatLanes(
ReactFiberLane.DefaultLane,
)}-Example`,
);
});
// @gate enableSchedulingProfiler
@@ -421,7 +435,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -430,7 +444,11 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
}).toErrorDev('Cannot update during an existing state transition');
expectMarksToContain(`--schedule-forced-update-${DEFAULT_LANE}-Example`);
expectMarksToContain(
`--schedule-forced-update-${formatLanes(
ReactFiberLane.DefaultLane,
)}-Example`,
);
});
// @gate enableSchedulingProfiler
@@ -447,7 +465,7 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
]);
clearPendingMarks();
@@ -455,15 +473,15 @@ describe('SchedulingProfiler', () => {
expect(Scheduler).toFlushUntilNextPaint([]);
expectMarksToEqual([
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
'--schedule-state-update-1-Example',
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--schedule-state-update-${formatLanes(ReactFiberLane.SyncLane)}-Example`,
'--layout-effects-stop',
'--render-start-1',
`--render-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--render-stop',
'--commit-start-1',
`--commit-start-${formatLanes(ReactFiberLane.SyncLane)}`,
'--commit-stop',
'--commit-stop',
]);
@@ -487,19 +505,21 @@ describe('SchedulingProfiler', () => {
expectMarksToEqual([
`--react-init-${ReactVersion}`,
`--schedule-render-${DEFAULT_LANE}`,
`--render-start-${DEFAULT_LANE}`,
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--layout-effects-start-${DEFAULT_LANE}`,
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--layout-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--layout-effects-stop',
'--commit-stop',
`--passive-effects-start-${DEFAULT_LANE}`,
`--schedule-state-update-${DEFAULT_LANE}-Example`,
`--passive-effects-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
`--schedule-state-update-${formatLanes(
ReactFiberLane.DefaultLane,
)}-Example`,
'--passive-effects-stop',
`--render-start-${DEFAULT_LANE}`,
`--render-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--render-stop',
`--commit-start-${DEFAULT_LANE}`,
`--commit-start-${formatLanes(ReactFiberLane.DefaultLane)}`,
'--commit-stop',
]);
});
@@ -518,6 +538,10 @@ describe('SchedulingProfiler', () => {
ReactTestRenderer.create(<Example />, {unstable_isConcurrent: true});
});
expectMarksToContain(`--schedule-state-update-${DEFAULT_LANE}-Example`);
expectMarksToContain(
`--schedule-state-update-${formatLanes(
ReactFiberLane.DefaultLane,
)}-Example`,
);
});
});
@@ -0,0 +1,179 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
// This test schedules work for as many lanes as we can (easily) using public APIs.
// It will hopefully serve as a reminder to update getLabelsForLanes() when we update Lanes.
// It's okay to delete any individual test in this file if the public API changes.
describe('SchedulingProfiler labels', () => {
let React;
let ReactDOM;
let ReactFiberLane;
let act;
let clearedMarks;
let featureDetectionMarkName = null;
let formatLanes;
let marks;
function polyfillJSDomUserTiming() {
featureDetectionMarkName = null;
clearedMarks = [];
marks = [];
// This is not a true polyfill, but it gives us enough to capture marks.
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
// JSDom already implements global.performance and it can't be overridden.
// However it only supports now() and timeOrigin() so we need to "upgrade it" in place.
if (!global.performance) {
global.performance = {};
}
global.performance.clearMarks = function clearMarks(markName) {
clearedMarks.push(markName);
marks = marks.filter(mark => mark !== markName);
};
global.performance.mark = function mark(markName, markOptions) {
if (featureDetectionMarkName === null) {
featureDetectionMarkName = markName;
}
marks.push(markName);
if (markOptions != null) {
// This is triggers the feature detection.
markOptions.startTime++;
}
};
}
function dispatchAndSetCurrentEvent(element, event) {
try {
window.event = event;
element.dispatchEvent(event);
} finally {
window.event = undefined;
}
}
beforeEach(() => {
jest.resetModules();
polyfillJSDomUserTiming();
React = require('react');
ReactDOM = require('react-dom');
const TestUtils = require('react-dom/test-utils');
act = TestUtils.act;
const SchedulingProfiler = require('react-reconciler/src/SchedulingProfiler');
formatLanes = SchedulingProfiler.formatLanes;
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFiberLane = ReactFeatureFlags.enableNewReconciler
? require('react-reconciler/src/ReactFiberLane.new')
: require('react-reconciler/src/ReactFiberLane.old');
});
afterEach(() => {
// Verify all logged marks also get cleared.
expect(marks).toHaveLength(0);
delete global.performance;
});
// @gate enableSchedulingProfiler
it('regression test SyncLane', () => {
ReactDOM.render(<div />, document.createElement('div'));
expect(clearedMarks).toContain(
`--schedule-render-${formatLanes(ReactFiberLane.SyncLane)}`,
);
});
// @gate experimental
// @gate enableSchedulingProfiler
it('regression test DefaultLane', () => {
const container = document.createElement('div');
const root = ReactDOM.createRoot(container);
act(() => {
root.render(<div />);
expect(clearedMarks).toContain(
`--schedule-render-${formatLanes(ReactFiberLane.DefaultLane)}`,
);
});
});
// @gate experimental
// @gate enableSchedulingProfiler
// @gate !enableLegacyFBSupport
it('regression test InputDiscreteLane', () => {
const container = document.createElement('div');
const root = ReactDOM.createRoot(container);
const targetRef = React.createRef(null);
function App() {
const [count, setCount] = React.useState(0);
const handleClick = () => {
setCount(count + 1);
};
return <button ref={targetRef} onClick={handleClick} />;
}
act(() => {
root.render(<App />);
});
clearedMarks.splice(0);
act(() => {
targetRef.current.click();
});
expect(clearedMarks).toContain(
`--schedule-state-update-${formatLanes(
ReactFiberLane.InputDiscreteLane,
)}-App`,
);
});
// @gate experimental
// @gate enableSchedulingProfiler
it('regression test InputContinuousLane', () => {
const container = document.createElement('div');
const root = ReactDOM.createRoot(container);
const targetRef = React.createRef(null);
function App() {
const [count, setCount] = React.useState(0);
const handleMouseOver = () => setCount(count + 1);
return <div ref={targetRef} onMouseOver={handleMouseOver} />;
}
act(() => {
root.render(<App />);
});
clearedMarks.splice(0);
act(() => {
const event = document.createEvent('MouseEvents');
event.initEvent('mouseover', true, true);
dispatchAndSetCurrentEvent(targetRef.current, event);
});
expect(clearedMarks).toContain(
`--schedule-state-update-${formatLanes(
ReactFiberLane.InputContinuousLane,
)}-App`,
);
});
});