Skip to content

Commit

Permalink
Add: reload to profile for Fusebox
Browse files Browse the repository at this point in the history
  • Loading branch information
EdmondChuiHW committed Sep 22, 2024
1 parent d4688df commit daba3d8
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 56 deletions.
35 changes: 28 additions & 7 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type {
PathMatch,
RendererID,
RendererInterface,
DevToolsHook,
DevToolsHookSettings,
} from './types';
import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
Expand Down Expand Up @@ -139,6 +140,24 @@ type PersistedSelection = {
path: Array<PathFrame>,
};

function getShouldProfileOnReload(): boolean {
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;

return (
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' ||
hook?.getShouldProfileOnReload?.() === true
);
}

function getRecordChangeDescriptions(): boolean {
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;

return (
sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
'true' || hook?.getProfileRecordChangeDescriptions?.() === true
);
}

export default class Agent extends EventEmitter<{
hideNativeHighlight: [],
showNativeHighlight: [HostInstance],
Expand All @@ -163,17 +182,15 @@ export default class Agent extends EventEmitter<{
constructor(bridge: BackendBridge) {
super();

if (
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
) {
this._recordChangeDescriptions =
sessionStorageGetItem(
SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
) === 'true';
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (getShouldProfileOnReload()) {
this._recordChangeDescriptions = getRecordChangeDescriptions();
this._isProfiling = true;

sessionStorageRemoveItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY);
sessionStorageRemoveItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY);

hook?.setShouldProfileOnReload?.(false);
}

const persistedSelectionString = sessionStorageGetItem(
Expand Down Expand Up @@ -677,6 +694,10 @@ export default class Agent extends EventEmitter<{

reloadAndProfile: (recordChangeDescriptions: boolean) => void =
recordChangeDescriptions => {
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
hook?.setShouldProfileOnReload?.(true);
hook?.setProfileRecordChangeDescriptions?.(recordChangeDescriptions);

sessionStorageSetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
sessionStorageSetItem(
SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
Expand Down
9 changes: 5 additions & 4 deletions packages/react-devtools-shared/src/backend/fiber/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5193,12 +5193,13 @@ export function attach(

// Automatically start profiling so that we don't miss timing info from initial "mount".
if (
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' ||
hook?.getShouldProfileOnReload?.() === true
) {
startProfiling(
const shouldRecordChangeDescriptions =
sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
'true',
);
'true' || hook?.getProfileRecordChangeDescriptions?.() === true;
startProfiling(shouldRecordChangeDescriptions);
}

function getNearestFiber(devtoolsInstance: DevToolsInstance): null | Fiber {
Expand Down
7 changes: 7 additions & 0 deletions packages/react-devtools-shared/src/backend/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,13 @@ export type DevToolsHook = {
didError?: boolean,
) => void,

setShouldProfileOnReload: ?(shouldProfileOnReload: boolean) => void,
getShouldProfileOnReload: ?() => boolean,
setProfileRecordChangeDescriptions: ?(
recordChangeDescriptions: boolean,
) => void,
getProfileRecordChangeDescriptions: ?() => boolean,

// Timeline internal module filtering
getInternalModuleRanges: () => Array<[string, string]>,
registerInternalModuleStart: (moduleStartError: Error) => void,
Expand Down
50 changes: 26 additions & 24 deletions packages/react-devtools-shared/src/devtools/ProfilerStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ export default class ProfilerStore extends EventEmitter<{
};

onProfilingStatus: (isProfiling: boolean) => void = isProfiling => {
if (this._isProfiling === isProfiling) {
return;
}

if (isProfiling) {
this._dataBackends.splice(0);
this._dataFrontend = null;
Expand All @@ -315,36 +319,34 @@ export default class ProfilerStore extends EventEmitter<{
});
}

if (this._isProfiling !== isProfiling) {
this._isProfiling = isProfiling;
this._isProfiling = isProfiling;

// Invalidate suspense cache if profiling data is being (re-)recorded.
// Note that we clear again, in case any views read from the cache while profiling.
// (That would have resolved a now-stale value without any profiling data.)
this._cache.invalidate();
// Invalidate suspense cache if profiling data is being (re-)recorded.
// Note that we clear again, in case any views read from the cache while profiling.
// (That would have resolved a now-stale value without any profiling data.)
this._cache.invalidate();

this.emit('isProfiling');
this.emit('isProfiling');

// If we've just finished a profiling session, we need to fetch data stored in each renderer interface
// and re-assemble it on the front-end into a format (ProfilingDataFrontend) that can power the Profiler UI.
// During this time, DevTools UI should probably not be interactive.
if (!isProfiling) {
this._dataBackends.splice(0);
this._rendererQueue.clear();
// If we've just finished a profiling session, we need to fetch data stored in each renderer interface
// and re-assemble it on the front-end into a format (ProfilingDataFrontend) that can power the Profiler UI.
// During this time, DevTools UI should probably not be interactive.
if (!isProfiling) {
this._dataBackends.splice(0);
this._rendererQueue.clear();

// Only request data from renderers that actually logged it.
// This avoids unnecessary bridge requests and also avoids edge case mixed renderer bugs.
// (e.g. when v15 and v16 are both present)
this._rendererIDsThatReportedProfilingData.forEach(rendererID => {
if (!this._rendererQueue.has(rendererID)) {
this._rendererQueue.add(rendererID);
// Only request data from renderers that actually logged it.
// This avoids unnecessary bridge requests and also avoids edge case mixed renderer bugs.
// (e.g. when v15 and v16 are both present)
this._rendererIDsThatReportedProfilingData.forEach(rendererID => {
if (!this._rendererQueue.has(rendererID)) {
this._rendererQueue.add(rendererID);

this._bridge.send('getProfilingData', {rendererID});
}
});
this._bridge.send('getProfilingData', {rendererID});
}
});

this.emit('isProcessingData');
}
this.emit('isProcessingData');
}
};
}
21 changes: 15 additions & 6 deletions packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export type Config = {
supportsInspectMatchingDOMElement?: boolean,
supportsClickToInspect?: boolean,
supportsReloadAndProfile?: boolean,
bypassReloadAndProfileCapabilityChecks?: boolean,
supportsTimeline?: boolean,
supportsTraceUpdates?: boolean,
};
Expand Down Expand Up @@ -179,6 +180,7 @@ export default class Store extends EventEmitter<{
_supportsInspectMatchingDOMElement: boolean = false;
_supportsClickToInspect: boolean = false;
_supportsReloadAndProfile: boolean = false;
_bypassReloadAndProfileCapabilityChecks: boolean = false;
_supportsTimeline: boolean = false;
_supportsTraceUpdates: boolean = false;

Expand Down Expand Up @@ -222,6 +224,7 @@ export default class Store extends EventEmitter<{
supportsInspectMatchingDOMElement,
supportsClickToInspect,
supportsReloadAndProfile,
bypassReloadAndProfileCapabilityChecks,
supportsTimeline,
supportsTraceUpdates,
checkBridgeProtocolCompatibility,
Expand All @@ -235,6 +238,9 @@ export default class Store extends EventEmitter<{
if (supportsReloadAndProfile) {
this._supportsReloadAndProfile = true;
}
if (bypassReloadAndProfileCapabilityChecks) {
this._bypassReloadAndProfileCapabilityChecks = true;
}
if (supportsTimeline) {
this._supportsTimeline = true;
}
Expand Down Expand Up @@ -451,14 +457,17 @@ export default class Store extends EventEmitter<{
return this._isNativeStyleEditorSupported;
}

// Does the DevTools shell support reloading and eagerly injecting the renderer interface?
// And if so, can the backend use the localStorage API and sync XHR?
// All of these are currently required for the reload-and-profile feature to work.
get supportsReloadAndProfile(): boolean {
// Does the DevTools shell support reloading and eagerly injecting the renderer interface?
// And if so, can the backend use the localStorage API and sync XHR?
// All of these are currently required for the reload-and-profile feature to work.
if (!this._supportsReloadAndProfile) {
return false;
}

return (
this._supportsReloadAndProfile &&
this._isBackendStorageAPISupported &&
this._isSynchronousXHRSupported
this._bypassReloadAndProfileCapabilityChecks ||
(this._isBackendStorageAPISupported && this._isSynchronousXHRSupported)
);
}

Expand Down
5 changes: 5 additions & 0 deletions packages/react-devtools-shared/src/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,11 @@ export function installHook(
onPostCommitFiberRoot,
setStrictMode,

setShouldProfileOnReload: undefined,
getShouldProfileOnReload: undefined,
setProfileRecordChangeDescriptions: undefined,
getProfileRecordChangeDescriptions: undefined,

// Schedule Profiler runtime helpers.
// These internal React modules to report their own boundaries
// which in turn enables the profiler to dim or filter internal frames.
Expand Down
16 changes: 1 addition & 15 deletions packages/shared/ReactVersion.js
Original file line number Diff line number Diff line change
@@ -1,15 +1 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// TODO: this is special because it gets imported during build.
//
// It exists as a placeholder so that DevTools can support work tag changes between releases.
// When we next publish a release, update the matching TODO in backend/renderer.js
// TODO: This module is used both by the release scripts and to expose a version
// at runtime. We should instead inject the version number as part of the build
// process, and use the ReactVersions.js module as the single source of truth.
export default '19.0.0';
export default '19.0.0-rc-bd788b41-20240906';

0 comments on commit daba3d8

Please sign in to comment.