@samitouri / QOS-React-2 / commits / 9a6a19456f

Show commit priority levels in Profiler UI

Brian Vaughn committed May 15, 2019 at 15:36 UTC 9a6a19456f0e07a9f308b079ad7245ca627a4f59
10 files changed +134 -12
shells/dev/app/PriorityLevels/index.js new
+29
@@ -0,0 +1,29 @@
1 +// @flow
2 +
3 +import React, { Fragment, useCallback, useState } from 'react';
4 +import { unstable_next as next } from 'scheduler';
5 +
6 +export default function PriorityLevels() {
7 + const [count, setCount] = useState(0);
8 +
9 + const startSequence = useCallback(() => {
10 + setCount(1);
11 + next(() => setCount(2));
12 + }, []);
13 +
14 + return (
15 + <Fragment>
16 + <h1>Priority Levels</h1>
17 + <button onClick={startSequence}>start sequence</button>
18 + {count >= 1 && <Text>One</Text>}
19 + {count >= 2 && <Text>Two</Text>}
20 + {count >= 2 && (
21 + <div hidden>
22 + <Text>Three</Text>
23 + </div>
24 + )}
25 + </Fragment>
26 + );
27 +}
28 +
29 +const Text = ({ children }) => children;
shells/dev/app/index.js
+9 -2
@@ -3,12 +3,17 @@
3 // This test harness mounts each test app as a separate root to test multi-root applications.
4
5 import { createElement } from 'react';
6 -import { render, unmountComponentAtNode } from 'react-dom';
6 +import {
7 + unmountComponentAtNode,
8 + // $FlowFixMe Flow does not yet know about createRoot()
9 + unstable_createRoot as createRoot,
10 +} from 'react-dom';
11 import DeeplyNestedComponents from './DeeplyNestedComponents';
12 import EditableProps from './EditableProps';
13 import ElementTypes from './ElementTypes';
14 import InspectableElements from './InspectableElements';
15 import InteractionTracing from './InteractionTracing';
16 +import PriorityLevels from './PriorityLevels';
17 import ToDoList from './ToDoList';
18 import Toggle from './Toggle';
19 import SuspenseTree from './SuspenseTree';
@@ -24,7 +29,8 @@ function mountHelper(App) {
29
30 containers.push(container);
31
27 - render(createElement(App), container);
32 + const root = createRoot(container);
33 + root.render(createElement(App));
34 }
35
36 function mountTestApp() {
@@ -33,6 +39,7 @@ function mountTestApp() {
39 mountHelper(InspectableElements);
40 mountHelper(ElementTypes);
41 mountHelper(EditableProps);
42 + mountHelper(PriorityLevels);
43 mountHelper(Toggle);
44 mountHelper(SuspenseTree);
45 mountHelper(DeeplyNestedComponents);
src/__tests__/__snapshots__/profiling-test.js.snap
+19
@@ -11,6 +11,7 @@ Object {
11 },
12 "commitIndex": 0,
13 "interactions": Array [],
14 + "priorityLevel": 99,
15 "rootID": 1,
16 "selfDurations": Map {
17 1 => 0,
@@ -33,6 +34,7 @@ Object {
34 },
35 "commitIndex": 1,
36 "interactions": Array [],
37 + "priorityLevel": 99,
38 "rootID": 1,
39 "selfDurations": Map {
40 3 => 0,
@@ -53,6 +55,7 @@ Object {
55 },
56 "commitIndex": 2,
57 "interactions": Array [],
58 + "priorityLevel": 99,
59 "rootID": 1,
60 "selfDurations": Map {
61 3 => 0,
@@ -70,6 +73,7 @@ Object {
73 },
74 "commitIndex": 3,
75 "interactions": Array [],
76 + "priorityLevel": 99,
77 "rootID": 1,
78 "selfDurations": Map {
79 2 => 10,
@@ -91,6 +95,7 @@ Object {
95 },
96 "commitIndex": 0,
97 "interactions": Array [],
98 + "priorityLevel": 99,
99 "rootID": 1,
100 "selfDurations": Map {
101 1 => 0,
@@ -110,6 +115,7 @@ Object {
115 },
116 "commitIndex": 1,
117 "interactions": Array [],
118 + "priorityLevel": 99,
119 "rootID": 1,
120 "selfDurations": Map {
121 3 => 0,
@@ -127,6 +133,7 @@ Object {
133 },
134 "commitIndex": 2,
135 "interactions": Array [],
136 + "priorityLevel": 99,
137 "rootID": 1,
138 "selfDurations": Map {
139 3 => 0,
@@ -141,6 +148,7 @@ Object {
148 },
149 "commitIndex": 3,
150 "interactions": Array [],
151 + "priorityLevel": 99,
152 "rootID": 1,
153 "selfDurations": Map {
154 2 => 10,
@@ -324,6 +332,7 @@ Object {
332 },
333 "commitIndex": 0,
334 "interactions": Array [],
335 + "priorityLevel": 99,
336 "rootID": 1,
337 "selfDurations": Map {
338 1 => 0,
@@ -344,6 +353,7 @@ Object {
353 },
354 "commitIndex": 0,
355 "interactions": Array [],
356 + "priorityLevel": 99,
357 "rootID": 1,
358 "selfDurations": Map {
359 1 => 0,
@@ -362,6 +372,7 @@ Object {
372 },
373 "commitIndex": 1,
374 "interactions": Array [],
375 + "priorityLevel": 99,
376 "rootID": 1,
377 "selfDurations": Map {
378 5 => 3,
@@ -447,6 +458,7 @@ Object {
458 },
459 "commitIndex": 0,
460 "interactions": Array [],
461 + "priorityLevel": 99,
462 "rootID": 1,
463 "selfDurations": Map {
464 1 => 0,
@@ -464,6 +476,7 @@ Object {
476 },
477 "commitIndex": 1,
478 "interactions": Array [],
479 + "priorityLevel": 99,
480 "rootID": 1,
481 "selfDurations": Map {
482 3 => 0,
@@ -482,6 +495,7 @@ Object {
495 },
496 "commitIndex": 2,
497 "interactions": Array [],
498 + "priorityLevel": 99,
499 "rootID": 1,
500 "selfDurations": Map {
501 3 => 0,
@@ -696,6 +710,7 @@ Object {
710 "timestamp": 0,
711 },
712 ],
713 + "priorityLevel": 99,
714 "rootID": 1,
715 "selfDurations": Map {
716 1 => 0,
@@ -720,6 +735,7 @@ Object {
735 "timestamp": 11,
736 },
737 ],
738 + "priorityLevel": 99,
739 "rootID": 1,
740 "selfDurations": Map {
741 3 => 0,
@@ -906,6 +922,7 @@ Object {
922 },
923 "commitIndex": 0,
924 "interactions": Array [],
925 + "priorityLevel": 99,
926 "rootID": 1,
927 "selfDurations": Map {
928 3 => 0,
@@ -923,6 +940,7 @@ Object {
940 },
941 "commitIndex": 1,
942 "interactions": Array [],
943 + "priorityLevel": 99,
944 "rootID": 1,
945 "selfDurations": Map {
946 3 => 0,
@@ -937,6 +955,7 @@ Object {
955 },
956 "commitIndex": 2,
957 "interactions": Array [],
958 + "priorityLevel": 99,
959 "rootID": 1,
960 "selfDurations": Map {
961 2 => 10,
src/backend/renderer.js
+7 -1
@@ -48,6 +48,7 @@ import type {
48 PathFrame,
49 PathMatch,
50 ProfilingSummaryBackend,
51 + ReactPriorityLevel,
52 ReactRenderer,
53 RendererInterface,
54 } from './types';
@@ -1267,6 +1268,7 @@ export function attach(
1268 })
1269 ),
1270 maxActualDuration: 0,
1271 + priorityLevel: null,
1272 };
1273 }
1274
@@ -1284,7 +1286,7 @@ export function attach(
1286 recordUnmount(fiber, false);
1287 }
1288
1287 - function handleCommitFiberRoot(root) {
1289 + function handleCommitFiberRoot(root, priorityLevel) {
1290 const current = root.current;
1291 const alternate = current.alternate;
1292
@@ -1309,6 +1311,7 @@ export function attach(
1311 })
1312 ),
1313 maxActualDuration: 0,
1314 + priorityLevel: priorityLevel || null,
1315 };
1316 }
1317
@@ -1955,6 +1958,7 @@ export function attach(
1958 durations: Array<number>,
1959 interactions: Array<InteractionBackend>,
1960 maxActualDuration: number,
1961 + priorityLevel: ReactPriorityLevel | null,
1962 |};
1963
1964 type CommitProfilingMetadataMap = Map<number, Array<CommitProfilingData>>;
@@ -1980,6 +1984,7 @@ export function attach(
1984 commitIndex,
1985 durations: commitProfilingData.durations,
1986 interactions: commitProfilingData.interactions,
1987 + priorityLevel: commitProfilingData.priorityLevel,
1988 rootID,
1989 };
1990 }
@@ -1993,6 +1998,7 @@ export function attach(
1998 commitIndex,
1999 durations: [],
2000 interactions: [],
2001 + priorityLevel: null,
2002 rootID,
2003 };
2004 }
src/backend/types.js
+20 -2
@@ -32,6 +32,16 @@ export type HookType =
32 | 'useImperativeHandle'
33 | 'useDebugValue';
34
35 +// Priority level is copied from React and should be kept in sync:
36 +// https://github.com/facebook/react/blob/master/packages/react-reconciler/src/SchedulerWithReactIntegration.js
37 +export opaque type ReactPriorityLevel = 99 | 98 | 97 | 96 | 95 | 90;
38 +export const ImmediatePriority: ReactPriorityLevel = 99;
39 +export const UserBlockingPriority: ReactPriorityLevel = 98;
40 +export const NormalPriority: ReactPriorityLevel = 97;
41 +export const LowPriority: ReactPriorityLevel = 96;
42 +export const IdlePriority: ReactPriorityLevel = 95;
43 +export const NoPriority: ReactPriorityLevel = 90;
44 +
45 // The Fiber type is copied from React and should be kept in sync:
46 // https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiber.js
47 // The properties we don't use in DevTools are omitted.
@@ -126,6 +136,7 @@ export type CommitDetailsBackend = {|
136 // An interleaved array: fiberID at [i], actualDuration at [i + 1], computed selfDuration at [i + 2].
137 durations: Array<number>,
138 interactions: Array<InteractionBackend>,
139 + priorityLevel: ReactPriorityLevel | null,
140 rootID: number,
141 |};
142
@@ -193,7 +204,10 @@ export type RendererInterface = {
204 ) => ExportedProfilingDataFromRenderer,
205 getProfilingSummary: (rootID: number) => ProfilingSummaryBackend,
206 getPathForElement: (id: number) => Array<PathFrame> | null,
196 - handleCommitFiberRoot: (fiber: Object) => void,
207 + handleCommitFiberRoot: (
208 + fiber: Object,
209 + commitPriority?: ReactPriorityLevel
210 + ) => void,
211 handleCommitFiberUnmount: (fiber: Object) => void,
212 inspectElement: (id: number) => InspectedElement | null,
213 logElementToConsole: (id: number) => void,
@@ -234,7 +248,11 @@ export type DevToolsHook = {
248 // React uses these methods.
249 checkDCE: (fn: Function) => void,
250 onCommitFiberUnmount: (rendererID: RendererID, fiber: Object) => void,
237 - onCommitFiberRoot: (rendererID: RendererID, fiber: Object) => void,
251 + onCommitFiberRoot: (
252 + rendererID: RendererID,
253 + fiber: Object,
254 + commitPriority?: ReactPriorityLevel
255 + ) => void,
256 };
257
258 export type HooksNode = {
src/devtools/ProfilingCache.js
+4 -1
@@ -130,8 +130,9 @@ export default class ProfilingCache {
130 rootID,
131 commitIndex,
132 actualDurations: new Map(),
133 - selfDurations: new Map(),
133 + priorityLevel: null,
134 interactions: [],
135 + selfDurations: new Map(),
136 });
137 });
138 },
@@ -346,6 +347,7 @@ export default class ProfilingCache {
347 commitIndex,
348 durations,
349 interactions,
350 + priorityLevel,
351 rootID,
352 }: CommitDetailsBackend) => {
353 const key = `${rootID}-${commitIndex}`;
@@ -365,6 +367,7 @@ export default class ProfilingCache {
367 actualDurations: actualDurationsMap,
368 commitIndex,
369 interactions,
370 + priorityLevel,
371 rootID,
372 selfDurations: selfDurationsMap,
373 });
src/devtools/views/Profiler/SidebarCommitInfo.js
+10 -2
@@ -2,7 +2,7 @@
2
3 import React, { Fragment, useCallback, useContext, useState } from 'react';
4 import { ProfilerContext } from './ProfilerContext';
5 -import { formatDuration, formatTime } from './utils';
5 +import { formatDuration, formatPriorityLevel, formatTime } from './utils';
6 import { StoreContext } from '../context';
7
8 import styles from './SidebarCommitInfo.css';
@@ -56,7 +56,7 @@ export default function SidebarCommitInfo(_: Props) {
56 }
57 );
58
59 - const { interactions } = profilingCache.CommitDetails.read({
59 + const { interactions, priorityLevel } = profilingCache.CommitDetails.read({
60 commitIndex: selectedCommitIndex,
61 rendererID: ((rendererID: any): number),
62 rootID: ((rootID: any): number),
@@ -72,6 +72,14 @@ export default function SidebarCommitInfo(_: Props) {
72 <div className={styles.Toolbar}>Commit information</div>
73 <div className={styles.Content}>
74 <ul className={styles.List}>
75 + {priorityLevel !== null && (
76 + <li className={styles.ListItem}>
77 + <label className={styles.Label}>Priority</label>:{' '}
78 + <span className={styles.Value}>
79 + {formatPriorityLevel(priorityLevel)}
80 + </span>
81 + </li>
82 + )}
83 <li className={styles.ListItem}>
84 <label className={styles.Label}>Committed at</label>:{' '}
85 <span className={styles.Value}>
src/devtools/views/Profiler/types.js
+2
@@ -5,6 +5,7 @@ import type {
5 CommitDetailsBackend,
6 InteractionsBackend,
7 ProfilingSummaryBackend,
8 + ReactPriorityLevel,
9 } from 'src/backend/types';
10
11 export type CommitTreeNodeFrontend = {|
@@ -42,6 +43,7 @@ export type CommitDetailsFrontend = {|
43 actualDurations: Map<number, number>,
44 commitIndex: number,
45 interactions: Array<InteractionFrontend>,
46 + priorityLevel: ReactPriorityLevel | null,
47 rootID: number,
48 selfDurations: Map<number, number>,
49 |};
src/devtools/views/Profiler/utils.js
+32 -2
@@ -1,6 +1,14 @@
1 // @flow
2
3 import { PROFILER_EXPORT_VERSION } from 'src/constants';
4 +import {
5 + ImmediatePriority,
6 + UserBlockingPriority,
7 + NormalPriority,
8 + LowPriority,
9 + IdlePriority,
10 + NoPriority,
11 +} from 'src/backend/types';
12
13 import type {
14 ExportedProfilingSummaryFromFrontend,
@@ -9,7 +17,10 @@ import type {
17 ProfilingSnapshotNode,
18 } from './types';
19
12 -import type { ExportedProfilingDataFromRenderer } from 'src/backend/types';
20 +import type {
21 + ExportedProfilingDataFromRenderer,
22 + ReactPriorityLevel,
23 +} from 'src/backend/types';
24
25 const commitGradient = [
26 'var(--color-commit-gradient-0)',
@@ -152,10 +163,11 @@ export const prepareImportedProfilingData = (
163 }
164 return {
165 actualDurations: actualDurationsMap,
155 - selfDurations: selfDurationsMap,
166 commitIndex: commitDetailsBackendItem.commitIndex,
167 interactions: commitDetailsBackendItem.interactions,
168 + priorityLevel: commitDetailsBackendItem.priorityLevel,
169 rootID: commitDetailsBackendItem.rootID,
170 + selfDurations: selfDurationsMap,
171 };
172 }
173 ),
@@ -184,6 +196,24 @@ export const getGradientColor = (value: number) => {
196 return commitGradient[Math.round(index)];
197 };
198
199 +export const formatPriorityLevel = (priorityLevel: ReactPriorityLevel) => {
200 + switch (priorityLevel) {
201 + case ImmediatePriority:
202 + return 'Immediate';
203 + case UserBlockingPriority:
204 + return 'User-Blocking';
205 + case NormalPriority:
206 + return 'Normal';
207 + case LowPriority:
208 + return 'Low';
209 + case IdlePriority:
210 + return 'Idle';
211 + case NoPriority:
212 + default:
213 + return 'Unknown';
214 + }
215 +};
216 +
217 export const formatDuration = (duration: number) =>
218 Math.round(duration * 10) / 10 || '<0.1';
219 export const formatPercentage = (percentage: number) =>
src/hook.js
+2 -2
@@ -138,7 +138,7 @@ export function installHook(target: any): DevToolsHook | null {
138 }
139 }
140
141 - function onCommitFiberRoot(rendererID, root) {
141 + function onCommitFiberRoot(rendererID, root, priorityLevel) {
142 const mountedRoots = hook.getFiberRoots(rendererID);
143 const current = root.current;
144 const isKnownRoot = mountedRoots.has(root);
@@ -153,7 +153,7 @@ export function installHook(target: any): DevToolsHook | null {
153 }
154 const rendererInterface = rendererInterfaces.get(rendererID);
155 if (rendererInterface != null) {
156 - rendererInterface.handleCommitFiberRoot(root);
156 + rendererInterface.handleCommitFiberRoot(root, priorityLevel);
157 }
158 }
159