From 5d19e1c8d1a6c0b5cd7532d43b707191eaf105b7 Mon Sep 17 00:00:00 2001 From: Edmond Chui <1967998+EdmondChuiHW@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:25:44 +0100 Subject: [PATCH] Fix: profiling crashes #30661 #28838 (#31024) ## Summary Profiling fails sometimes because `onProfilingStatus` is called repeatedly on some occasions, e.g. multiple calls to `getProfilingStatus`. Subsequent calls should be a no-op if the profiling status hasn't changed. Reported via #30661 #28838. > [!TIP] > Hide whitespace changes on this PR screenshot showing the UI controls for hiding
whitespace changes on GitHub ## How did you test this change? Tested as part of Fusebox implementation of reload-to-profile. https://github.com/facebook/react/pull/31021?#discussion_r1770589753 --- .../src/devtools/ProfilerStore.js | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/ProfilerStore.js b/packages/react-devtools-shared/src/devtools/ProfilerStore.js index 46b2edcab4d32..a55487ec19a37 100644 --- a/packages/react-devtools-shared/src/devtools/ProfilerStore.js +++ b/packages/react-devtools-shared/src/devtools/ProfilerStore.js @@ -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; @@ -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'); } }; }