Skip to content

Commit c7f9c4f

Browse files
committed
[DevTools][Profile Event Parents] Added a stack of of component owners
You now get some idea of the context of a component that causes a state change, with a sidebar listing the component's hierarchy. This additionally allows user to click on components to link to their source code. resolves #24170
1 parent 229c86a commit c7f9c4f

File tree

18 files changed

+352
-8
lines changed

18 files changed

+352
-8
lines changed

packages/react-devtools-extensions/webpack.backend.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module.exports = {
6969
new DefinePlugin({
7070
__DEV__: true,
7171
__PROFILE__: false,
72+
__EXPERIMENTAL__: true,
7273
__DEV____DEV__: true,
7374
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-extensions"`,
7475
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,

packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,27 +1257,43 @@ describe('Timeline profiler', () => {
12571257
Object {
12581258
"componentName": "Example",
12591259
"lanes": "0b0000000000000000000000000000100",
1260+
"parents": Array [
1261+
2,
1262+
1,
1263+
],
12601264
"timestamp": 10,
12611265
"type": "schedule-state-update",
12621266
"warning": null,
12631267
},
12641268
Object {
12651269
"componentName": "Example",
12661270
"lanes": "0b0000000000000000000000001000000",
1271+
"parents": Array [
1272+
2,
1273+
1,
1274+
],
12671275
"timestamp": 10,
12681276
"type": "schedule-state-update",
12691277
"warning": null,
12701278
},
12711279
Object {
12721280
"componentName": "Example",
12731281
"lanes": "0b0000000000000000000000001000000",
1282+
"parents": Array [
1283+
2,
1284+
1,
1285+
],
12741286
"timestamp": 10,
12751287
"type": "schedule-state-update",
12761288
"warning": null,
12771289
},
12781290
Object {
12791291
"componentName": "Example",
12801292
"lanes": "0b0000000000000000000000000010000",
1293+
"parents": Array [
1294+
2,
1295+
1,
1296+
],
12811297
"timestamp": 10,
12821298
"type": "schedule-state-update",
12831299
"warning": null,
@@ -1615,6 +1631,10 @@ describe('Timeline profiler', () => {
16151631
Object {
16161632
"componentName": "Example",
16171633
"lanes": "0b0000000000000000000000000000001",
1634+
"parents": Array [
1635+
1,
1636+
2,
1637+
],
16181638
"timestamp": 20,
16191639
"type": "schedule-state-update",
16201640
"warning": null,
@@ -1742,6 +1762,10 @@ describe('Timeline profiler', () => {
17421762
Object {
17431763
"componentName": "Example",
17441764
"lanes": "0b0000000000000000000000000010000",
1765+
"parents": Array [
1766+
1,
1767+
2,
1768+
],
17451769
"timestamp": 10,
17461770
"type": "schedule-state-update",
17471771
"warning": null,
@@ -1873,6 +1897,10 @@ describe('Timeline profiler', () => {
18731897
Object {
18741898
"componentName": "Example",
18751899
"lanes": "0b0000000000000000000000000000001",
1900+
"parents": Array [
1901+
1,
1902+
2,
1903+
],
18761904
"timestamp": 21,
18771905
"type": "schedule-state-update",
18781906
"warning": null,
@@ -1935,6 +1963,10 @@ describe('Timeline profiler', () => {
19351963
Object {
19361964
"componentName": "Example",
19371965
"lanes": "0b0000000000000000000000000010000",
1966+
"parents": Array [
1967+
2,
1968+
1,
1969+
],
19381970
"timestamp": 21,
19391971
"type": "schedule-state-update",
19401972
"warning": null,
@@ -1983,6 +2015,10 @@ describe('Timeline profiler', () => {
19832015
Object {
19842016
"componentName": "Example",
19852017
"lanes": "0b0000000000000000000000000010000",
2018+
"parents": Array [
2019+
1,
2020+
2,
2021+
],
19862022
"timestamp": 20,
19872023
"type": "schedule-state-update",
19882024
"warning": null,
@@ -2066,6 +2102,10 @@ describe('Timeline profiler', () => {
20662102
Object {
20672103
"componentName": "ErrorBoundary",
20682104
"lanes": "0b0000000000000000000000000000001",
2105+
"parents": Array [
2106+
1,
2107+
2,
2108+
],
20692109
"timestamp": 20,
20702110
"type": "schedule-state-update",
20712111
"warning": null,
@@ -2178,6 +2218,10 @@ describe('Timeline profiler', () => {
21782218
Object {
21792219
"componentName": "ErrorBoundary",
21802220
"lanes": "0b0000000000000000000000000000001",
2221+
"parents": Array [
2222+
1,
2223+
2,
2224+
],
21812225
"timestamp": 30,
21822226
"type": "schedule-state-update",
21832227
"warning": null,

packages/react-devtools-shared/src/__tests__/preprocessData-test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,7 @@ describe('Timeline profiler', () => {
11161116
Object {
11171117
"componentName": "App",
11181118
"lanes": "0b0000000000000000000000000000100",
1119+
"parents": null,
11191120
"timestamp": 0.021,
11201121
"type": "schedule-state-update",
11211122
"warning": null,
@@ -2416,6 +2417,9 @@ describe('Timeline profiler', () => {
24162417
Object {
24172418
"componentName": "App",
24182419
"lanes": "0b0000000000000000000000000010000",
2420+
"parents": Array [
2421+
"createRoot()",
2422+
],
24192423
"timestamp": 10,
24202424
"type": "schedule-state-update",
24212425
"warning": null,

packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,24 @@ export function describeUnknownElementTypeFrameInDEV(
230230
source: void | null | Source,
231231
ownerFn: void | null | Function,
232232
currentDispatcherRef: CurrentDispatcherRef,
233+
) {
234+
return describeUnknownElementTypeFrame(
235+
type,
236+
source,
237+
ownerFn,
238+
currentDispatcherRef,
239+
__DEV__,
240+
);
241+
}
242+
243+
export function describeUnknownElementTypeFrame(
244+
type: any,
245+
source: void | null | Source,
246+
ownerFn: void | null | Function,
247+
currentDispatcherRef: CurrentDispatcherRef,
248+
force: boolean = true,
233249
): string {
234-
if (!__DEV__) {
250+
if (!force) {
235251
return '';
236252
}
237253
if (type == null) {

packages/react-devtools-shared/src/backend/profilingHooks.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import type {
2020
ReactComponentMeasure,
2121
ReactMeasure,
2222
ReactMeasureType,
23-
TimelineData,
2423
SuspenseEvent,
24+
TimelineData,
2525
} from 'react-devtools-timeline/src/types';
2626

2727
import isArray from 'shared/isArray';
@@ -96,18 +96,23 @@ type Response = {|
9696

9797
export function createProfilingHooks({
9898
getDisplayNameForFiber,
99+
getDisplayNameForFiberID,
100+
getFiberParentIDs,
99101
getIsProfiling,
100102
getLaneLabelMap,
101103
reactVersion,
102104
}: {|
103105
getDisplayNameForFiber: (fiber: Fiber) => string | null,
106+
getDisplayNameForFiberID: (id: number) => string | null,
107+
getFiberParentIDs: (fiber: Fiber) => number[],
104108
getIsProfiling: () => boolean,
105109
getLaneLabelMap?: () => Map<Lane, string> | null,
106110
reactVersion: string,
107111
|}): Response {
108112
let currentBatchUID: BatchUID = 0;
109113
let currentReactComponentMeasure: ReactComponentMeasure | null = null;
110114
let currentReactMeasuresStack: Array<ReactMeasure> = [];
115+
let componentIDs: Set<number> | null = null;
111116
let currentTimelineData: TimelineData | null = null;
112117
let isProfiling: boolean = false;
113118
let nextRenderShouldStartNewBatch: boolean = false;
@@ -781,8 +786,13 @@ export function createProfilingHooks({
781786
if (isProfiling) {
782787
// TODO (timeline) Record and cache component stack
783788
if (currentTimelineData) {
789+
const parents = getFiberParentIDs(fiber);
790+
if (componentIDs != null) {
791+
parents.forEach(id => componentIDs && componentIDs.add(id));
792+
}
784793
currentTimelineData.schedulingEvents.push({
785794
componentName,
795+
parents,
786796
lanes: laneToLanesArray(lane),
787797
timestamp: getRelativeTime(),
788798
type: 'schedule-state-update',
@@ -828,13 +838,18 @@ export function createProfilingHooks({
828838
lane *= 2;
829839
}
830840

841+
// Components we need to track to display names + link to source in the
842+
// UI
843+
componentIDs = new Set();
844+
831845
currentBatchUID = 0;
832846
currentReactComponentMeasure = null;
833847
currentReactMeasuresStack = [];
834848
currentTimelineData = {
835849
// Session wide metadata; only collected once.
836850
internalModuleSourceToRanges,
837851
laneToLabelMap: laneToLabelMap || new Map(),
852+
componentDisplayNames: new Map(),
838853
reactVersion,
839854

840855
// Data logged by React during profiling session.
@@ -858,6 +873,13 @@ export function createProfilingHooks({
858873
snapshotHeight: 0,
859874
};
860875
nextRenderShouldStartNewBatch = true;
876+
} else if (componentIDs != null && currentTimelineData != null) {
877+
currentTimelineData.componentDisplayNames = new Map(
878+
[...componentIDs].map(id => [
879+
id,
880+
getDisplayNameForFiberID(id) || 'Unknown',
881+
]),
882+
);
861883
}
862884
}
863885
}

packages/react-devtools-shared/src/backend/renderer.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,11 +653,21 @@ export function attach(
653653
};
654654
}
655655

656+
function getFiberParentIDs(fiber: Fiber): number[] {
657+
const ids = [];
658+
for (let ptr = fiber; ptr; ptr = ptr.return) {
659+
ids.push(getOrGenerateFiberID(ptr));
660+
}
661+
return ids;
662+
}
663+
656664
let getTimelineData: null | GetTimelineData = null;
657665
let toggleProfilingStatus: null | ToggleProfilingStatus = null;
658666
if (typeof injectProfilingHooks === 'function') {
659667
const response = createProfilingHooks({
660668
getDisplayNameForFiber,
669+
getDisplayNameForFiberID,
670+
getFiberParentIDs,
661671
getIsProfiling: () => isProfiling,
662672
getLaneLabelMap,
663673
reactVersion: version,
@@ -4050,6 +4060,7 @@ export function attach(
40504060
if (currentTimelineData) {
40514061
const {
40524062
batchUIDToMeasuresMap,
4063+
componentDisplayNames,
40534064
internalModuleSourceToRanges,
40544065
laneToLabelMap,
40554066
laneToReactMeasureMap,
@@ -4066,6 +4077,9 @@ export function attach(
40664077
batchUIDToMeasuresKeyValueArray: Array.from(
40674078
batchUIDToMeasuresMap.entries(),
40684079
),
4080+
componentDisplayNamesValueArray: Array.from(
4081+
componentDisplayNames.entries(),
4082+
),
40694083
internalModuleSourceToRanges: Array.from(
40704084
internalModuleSourceToRanges.entries(),
40714085
),

packages/react-devtools-shared/src/backend/types.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,13 @@ export type DevToolsHook = {
490490

491491
...
492492
};
493+
494+
export type ReactFiberMetadata = {
495+
displayName: string,
496+
source?: DebugSource,
497+
};
498+
499+
export type DebugSource = {|
500+
...Source,
501+
columnNumber: number,
502+
|};

packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import CommitFlamegraph from './CommitFlamegraph';
1717
import CommitRanked from './CommitRanked';
1818
import RootSelector from './RootSelector';
1919
import {Timeline} from 'react-devtools-timeline/src/Timeline';
20+
import SidebarEventInfo from './SidebarEventInfo';
2021
import RecordToggle from './RecordToggle';
2122
import ReloadAndProfileButton from './ReloadAndProfileButton';
2223
import ProfilingImportExportButtons from './ProfilingImportExportButtons';
@@ -102,6 +103,9 @@ function Profiler(_: {||}) {
102103
}
103104
}
104105
break;
106+
case 'timeline':
107+
sidebar = <SidebarEventInfo />;
108+
break;
105109
default:
106110
break;
107111
}
@@ -145,9 +149,7 @@ function Profiler(_: {||}) {
145149
<ModalDialog />
146150
</div>
147151
</div>
148-
{isLegacyProfilerSelected && (
149-
<div className={styles.RightColumn}>{sidebar}</div>
150-
)}
152+
<div className={styles.RightColumn}>{sidebar}</div>
151153
<SettingsModal />
152154
</div>
153155
</SettingsModalContextController>

0 commit comments

Comments
 (0)