Skip to content

Commit

Permalink
[DevTools][Profile Event Parents] Added a stack of of component owners
Browse files Browse the repository at this point in the history
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 facebook#24170
  • Loading branch information
blakef committed Jun 17, 2022
1 parent 229c86a commit fe80b29
Show file tree
Hide file tree
Showing 18 changed files with 338 additions and 8 deletions.
1 change: 1 addition & 0 deletions packages/react-devtools-extensions/webpack.backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ module.exports = {
new DefinePlugin({
__DEV__: true,
__PROFILE__: false,
__EXPERIMENTAL__: true,
__DEV____DEV__: true,
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-extensions"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1257,27 +1257,43 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000000100",
"parents": Array [
2,
1,
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
},
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000001000000",
"parents": Array [
2,
1,
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
},
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000001000000",
"parents": Array [
2,
1,
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
},
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000010000",
"parents": Array [
2,
1,
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -1615,6 +1631,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000000001",
"parents": Array [
1,
2,
],
"timestamp": 20,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -1742,6 +1762,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000010000",
"parents": Array [
1,
2,
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -1873,6 +1897,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000000001",
"parents": Array [
1,
2,
],
"timestamp": 21,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -1935,6 +1963,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000010000",
"parents": Array [
2,
1,
],
"timestamp": 21,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -1983,6 +2015,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "Example",
"lanes": "0b0000000000000000000000000010000",
"parents": Array [
1,
2,
],
"timestamp": 20,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -2066,6 +2102,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "ErrorBoundary",
"lanes": "0b0000000000000000000000000000001",
"parents": Array [
1,
2,
],
"timestamp": 20,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -2178,6 +2218,10 @@ describe('Timeline profiler', () => {
Object {
"componentName": "ErrorBoundary",
"lanes": "0b0000000000000000000000000000001",
"parents": Array [
1,
2,
],
"timestamp": 30,
"type": "schedule-state-update",
"warning": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,7 @@ describe('Timeline profiler', () => {
Object {
"componentName": "App",
"lanes": "0b0000000000000000000000000000100",
"parents": null,
"timestamp": 0.021,
"type": "schedule-state-update",
"warning": null,
Expand Down Expand Up @@ -2416,6 +2417,9 @@ describe('Timeline profiler', () => {
Object {
"componentName": "App",
"lanes": "0b0000000000000000000000000010000",
"parents": Array [
"createRoot()",
],
"timestamp": 10,
"type": "schedule-state-update",
"warning": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,24 @@ export function describeUnknownElementTypeFrameInDEV(
source: void | null | Source,
ownerFn: void | null | Function,
currentDispatcherRef: CurrentDispatcherRef,
) {
return describeUnknownElementTypeFrame(
type,
source,
ownerFn,
currentDispatcherRef,
__DEV__,
);
}

export function describeUnknownElementTypeFrame(
type: any,
source: void | null | Source,
ownerFn: void | null | Function,
currentDispatcherRef: CurrentDispatcherRef,
force: boolean = true,
): string {
if (!__DEV__) {
if (!force) {
return '';
}
if (type == null) {
Expand Down
24 changes: 23 additions & 1 deletion packages/react-devtools-shared/src/backend/profilingHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import type {
ReactComponentMeasure,
ReactMeasure,
ReactMeasureType,
TimelineData,
SuspenseEvent,
TimelineData,
} from 'react-devtools-timeline/src/types';

import isArray from 'shared/isArray';
Expand Down Expand Up @@ -96,18 +96,23 @@ type Response = {|

export function createProfilingHooks({
getDisplayNameForFiber,
getDisplayNameForFiberID,
getFiberParentIDs,
getIsProfiling,
getLaneLabelMap,
reactVersion,
}: {|
getDisplayNameForFiber: (fiber: Fiber) => string | null,
getDisplayNameForFiberID: (id: number) => string | null,
getFiberParentIDs: (fiber: Fiber) => number[],
getIsProfiling: () => boolean,
getLaneLabelMap?: () => Map<Lane, string> | null,
reactVersion: string,
|}): Response {
let currentBatchUID: BatchUID = 0;
let currentReactComponentMeasure: ReactComponentMeasure | null = null;
let currentReactMeasuresStack: Array<ReactMeasure> = [];
let componentIDs: Set<number> | null = null;
let currentTimelineData: TimelineData | null = null;
let isProfiling: boolean = false;
let nextRenderShouldStartNewBatch: boolean = false;
Expand Down Expand Up @@ -781,8 +786,13 @@ export function createProfilingHooks({
if (isProfiling) {
// TODO (timeline) Record and cache component stack
if (currentTimelineData) {
const parents = getFiberParentIDs(fiber);
if (componentIDs != null) {
parents.forEach(id => componentIDs.add(id));
}
currentTimelineData.schedulingEvents.push({
componentName,
parents,
lanes: laneToLanesArray(lane),
timestamp: getRelativeTime(),
type: 'schedule-state-update',
Expand Down Expand Up @@ -828,13 +838,18 @@ export function createProfilingHooks({
lane *= 2;
}

// Components we need to track to display names + link to source in the
// UI
componentIDs = new Set();

currentBatchUID = 0;
currentReactComponentMeasure = null;
currentReactMeasuresStack = [];
currentTimelineData = {
// Session wide metadata; only collected once.
internalModuleSourceToRanges,
laneToLabelMap: laneToLabelMap || new Map(),
componentDisplayNames: new Map(),
reactVersion,

// Data logged by React during profiling session.
Expand All @@ -858,6 +873,13 @@ export function createProfilingHooks({
snapshotHeight: 0,
};
nextRenderShouldStartNewBatch = true;
} else if (componentIDs != null && currentTimelineData != null) {
currentTimelineData.componentDisplayNames = new Map(
[...componentIDs].map(id => [
id,
getDisplayNameForFiberID(id) || 'Unknown',
]),
);
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions packages/react-devtools-shared/src/backend/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -653,11 +653,21 @@ export function attach(
};
}

function getFiberParentIDs(fiber: Fiber): number[] {
const ids = [];
for (let ptr = fiber; ptr; ptr = ptr.return) {
ids.push(getOrGenerateFiberID(ptr));
}
return ids;
}

let getTimelineData: null | GetTimelineData = null;
let toggleProfilingStatus: null | ToggleProfilingStatus = null;
if (typeof injectProfilingHooks === 'function') {
const response = createProfilingHooks({
getDisplayNameForFiber,
getDisplayNameForFiberID,
getFiberParentIDs,
getIsProfiling: () => isProfiling,
getLaneLabelMap,
reactVersion: version,
Expand Down Expand Up @@ -4050,6 +4060,7 @@ export function attach(
if (currentTimelineData) {
const {
batchUIDToMeasuresMap,
componentDisplayNames,
internalModuleSourceToRanges,
laneToLabelMap,
laneToReactMeasureMap,
Expand All @@ -4066,6 +4077,9 @@ export function attach(
batchUIDToMeasuresKeyValueArray: Array.from(
batchUIDToMeasuresMap.entries(),
),
componentDisplayNamesValueArray: Array.from(
componentDisplayNames.entries(),
),
internalModuleSourceToRanges: Array.from(
internalModuleSourceToRanges.entries(),
),
Expand Down
10 changes: 10 additions & 0 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,13 @@ export type DevToolsHook = {

...
};

export type ReactFiberMetadata = {
displayName: string,
source?: DebugSource,
};

export type DebugSource = {|
...Source,
columnNumber: number,
|};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import CommitFlamegraph from './CommitFlamegraph';
import CommitRanked from './CommitRanked';
import RootSelector from './RootSelector';
import {Timeline} from 'react-devtools-timeline/src/Timeline';
import SidebarEventInfo from './SidebarEventInfo';
import RecordToggle from './RecordToggle';
import ReloadAndProfileButton from './ReloadAndProfileButton';
import ProfilingImportExportButtons from './ProfilingImportExportButtons';
Expand Down Expand Up @@ -102,6 +103,9 @@ function Profiler(_: {||}) {
}
}
break;
case 'timeline':
sidebar = <SidebarEventInfo />;
break;
default:
break;
}
Expand Down Expand Up @@ -145,9 +149,7 @@ function Profiler(_: {||}) {
<ModalDialog />
</div>
</div>
{isLegacyProfilerSelected && (
<div className={styles.RightColumn}>{sidebar}</div>
)}
<div className={styles.RightColumn}>{sidebar}</div>
<SettingsModal />
</div>
</SettingsModalContextController>
Expand Down
Loading

0 comments on commit fe80b29

Please sign in to comment.