Skip to content

feat[DevTools]: Use Chrome DevTools Performance extension API #29231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ describe('Timeline profiler', () => {
markOptions.startTime++;
}
},
measure() {},
clearMeasures() {},
};
}

Expand Down Expand Up @@ -364,9 +366,10 @@ describe('Timeline profiler', () => {
"--render-start-128",
"--component-render-start-Foo",
"--component-render-stop",
"--render-yield",
"--render-yield-stop",
]
`);
await waitForPaint(['Bar']);
});

it('should mark concurrent render with suspense that resolves', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Timeline profiler', () => {
let utils;
let assertLog;
let waitFor;

let waitForPaint;
describe('User Timing API', () => {
let currentlyNotClearedMarks;
let registeredMarks;
Expand Down Expand Up @@ -75,6 +75,8 @@ describe('Timeline profiler', () => {
markOptions.startTime++;
}
},
measure() {},
clearMeasures() {},
};
}

Expand All @@ -101,7 +103,7 @@ describe('Timeline profiler', () => {
const InternalTestUtils = require('internal-test-utils');
assertLog = InternalTestUtils.assertLog;
waitFor = InternalTestUtils.waitFor;

waitForPaint = InternalTestUtils.waitForPaint;
setPerformanceMock =
require('react-devtools-shared/src/backend/profilingHooks').setPerformanceMock_ONLY_FOR_TESTING;
setPerformanceMock(createUserTimingPolyfill());
Expand Down Expand Up @@ -1301,6 +1303,8 @@ describe('Timeline profiler', () => {
const data = await preprocessData(testMarks);
const event = data.nativeEvents.find(({type}) => type === 'click');
expect(event.warning).toBe(null);

await waitForPaint([]);
});

// @reactVersion >= 18.0
Expand Down
94 changes: 70 additions & 24 deletions packages/react-devtools-shared/src/backend/profilingHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export function createProfilingHooks({
let currentBatchUID: BatchUID = 0;
let currentReactComponentMeasure: ReactComponentMeasure | null = null;
let currentReactMeasuresStack: Array<ReactMeasure> = [];
const currentBeginMarksStack: Array<string> = [];
let currentTimelineData: TimelineData | null = null;
let currentFiberStacks: Map<SchedulingEvent, Array<Fiber>> = new Map();
let isProfiling: boolean = false;
Expand Down Expand Up @@ -214,6 +215,56 @@ export function createProfilingHooks({
((performanceTarget: any): Performance).clearMarks(markName);
}

function beginMark(taskName: string, ending: string | number) {
// This name format is used in preprocessData.js so it cannot just be changed.
const startMarkName = `--${taskName}-start-${ending}`;
currentBeginMarksStack.push(startMarkName);
// This method won't be called unless these functions are defined, so we can skip the extra typeof check.
((performanceTarget: any): Performance).mark(startMarkName);
}

function endMarkAndClear(taskName: string) {
const startMarkName = currentBeginMarksStack.pop();
if (!startMarkName) {
console.error(
'endMarkAndClear was unexpectedly called without a corresponding start mark',
);
return;
}
Comment on lines +228 to +233
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: check if these can be safely stored in a stack, especially with concurrent root case

const markEnding = startMarkName.split('-').at(-1) || '';
const endMarkName = `--${taskName}-stop`;
const measureName = `${taskName} ${markEnding}`;

// Use different color for rendering tasks.
const color = taskName.includes('render') ? 'primary' : 'tertiary';
// If the ending is not a number, then it's a component name.
const properties = isNaN(parseInt(markEnding, 10))
? [['Component', markEnding]]
: [];
// This method won't be called unless these functions are defined, so we can skip the extra typeof check.
((performanceTarget: any): Performance).mark(endMarkName);
// Based on the format in https://bit.ly/rpp-e11y
const measureOptions = {
start: startMarkName,
end: endMarkName,
detail: {
devtools: {
dataType: 'track-entry',
color,
track: '⚛️ React',
properties,
},
},
};
((performanceTarget: any): Performance).measure(
measureName,
measureOptions,
);
((performanceTarget: any): Performance).clearMarks(startMarkName);
((performanceTarget: any): Performance).clearMarks(endMarkName);
((performanceTarget: any): Performance).clearMeasures(measureName);
}

function recordReactMeasureStarted(
type: ReactMeasureType,
lanes: Lanes,
Expand Down Expand Up @@ -301,7 +352,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--commit-start-${lanes}`);
beginMark('commit', lanes);

// Some metadata only needs to be logged once per session,
// but if profiling information is being recorded via the Performance tab,
Expand All @@ -318,7 +369,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--commit-stop');
endMarkAndClear('commit');
}
}

Expand All @@ -340,7 +391,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--component-render-start-${componentName}`);
beginMark('component-render', componentName);
}
}
}
Expand All @@ -361,9 +412,8 @@ export function createProfilingHooks({
currentReactComponentMeasure = null;
}
}

if (supportsUserTimingV3) {
markAndClear('--component-render-stop');
endMarkAndClear('component-render');
}
}

Expand All @@ -385,7 +435,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--component-layout-effect-mount-start-${componentName}`);
beginMark('component-layout-effect-mount', componentName);
}
}
}
Expand All @@ -408,7 +458,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--component-layout-effect-mount-stop');
endMarkAndClear('component-layout-effect-mount');
}
}

Expand All @@ -430,9 +480,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(
`--component-layout-effect-unmount-start-${componentName}`,
);
beginMark('component-layout-effect-unmount', componentName);
}
}
}
Expand All @@ -455,7 +503,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--component-layout-effect-unmount-stop');
endMarkAndClear('component-layout-effect-unmount');
}
}

Expand All @@ -477,7 +525,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--component-passive-effect-mount-start-${componentName}`);
beginMark('component-passive-effect-mount', componentName);
}
}
}
Expand All @@ -500,7 +548,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--component-passive-effect-mount-stop');
endMarkAndClear('component-passive-effect-mount');
}
}

Expand All @@ -522,9 +570,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(
`--component-passive-effect-unmount-start-${componentName}`,
);
beginMark('component-passive-effect-unmount', componentName);
}
}
}
Expand All @@ -547,7 +593,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--component-passive-effect-unmount-stop');
endMarkAndClear('component-passive-effect-unmount');
}
}

Expand Down Expand Up @@ -679,7 +725,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--layout-effects-start-${lanes}`);
beginMark('layout-effects', lanes);
}
}

Expand All @@ -689,7 +735,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--layout-effects-stop');
endMarkAndClear('layout-effects');
}
}

Expand All @@ -699,7 +745,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--passive-effects-start-${lanes}`);
beginMark('passive-effects', lanes);
}
}

Expand All @@ -709,7 +755,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--passive-effects-stop');
endMarkAndClear('passive-effects');
}
}

Expand All @@ -734,7 +780,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear(`--render-start-${lanes}`);
beginMark('render', lanes);
}
}

Expand All @@ -744,7 +790,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--render-yield');
endMarkAndClear('render-yield');
}
}

Expand All @@ -754,7 +800,7 @@ export function createProfilingHooks({
}

if (supportsUserTimingV3) {
markAndClear('--render-stop');
endMarkAndClear('render');
}
}

Expand Down
Loading