Closed Bug 1622191 Opened 4 months ago Closed 1 month ago

Pages that constantly call `eval`/`new Function` can bring the devtools parent-process UI to its knees

Categories

(DevTools :: Debugger, defect, P3)

76 Branch
defect

Tracking

(firefox79 fixed)

RESOLVED FIXED
Firefox 79
Tracking Status
firefox79 --- fixed

People

(Reporter: raphael.mozilla, Assigned: jlast)

References

(Blocks 3 open bugs)

Details

Attachments

(2 files)

User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0

Steps to reproduce:

I opened oui.sncf (in a small window trigger the mobile version of the layout) in Firefox nightly.
I searched for a train, and then clicked on one of the results, as per the video.
Then, I right-clicked on the arrow to close the result and chose "Inspect element"
On the parent of the element, there is an event. I clicked on the button to open the event handler in the debugger tab.

On the current stable version of Firefox, opening oui.sncf and then opening the debugger tab suffices.

Actual results:

The developer tools became slugging, the CPU and memory usage spikes, the whole browser eventually becomes sluggish, even freezes until the developer tools are closed.

The situation is way worse on current stable Firefox version.

Expected results:

Everything should remain smooth.

Bugbug thinks this bug should belong to this component, but please revert this change in case of error.

Component: Untriaged → Debugger
Product: Firefox → DevTools

Yes, sure. Sorry about that.

Alright, this sounded like a potentially bad issue so I did some digging into this to try to sort things out a bit.

I connected to Firefox and dumped a stack trace a few times and almost every time I get something along the lines of:

#0   7ffee4a2fc90 I   resource://devtools/client/debugger/src/utils/resource/base-query.js:64 (2576efc85c90 @ 9)
#1   7ffee4a2fc90 I   resource://devtools/client/debugger/src/utils/resource/base-query.js:82 (2576efc85e20 @ 116)
#2   7ffee4a2fc90 I   resource://devtools/client/debugger/src/utils/resource/base-query.js:66 (2576efc85ce0 @ 29)
#3   7ffee4a2fc90 I   resource://devtools/client/debugger/src/utils/resource/base-query.js:31 (2576efc85ba0 @ 24)
#4   7ffee4a2fcf0 I   self-hosted:251 (2395af89e600 @ 295)
#5   7ffee4a2fdc0 b   resource://devtools/client/debugger/src/utils/resource/base-query.js:31 (2576efc85b50 @ 76)
#6   7ffee4a2fe30 I   resource://devtools/client/debugger/src/utils/resource/query-cache.js:84 (2576efc8b2e0 @ 349)
#7   7ffee4a2fef0 b   resource://devtools/client/debugger/src/reducers/sources.js:673 (2576efc91830 @ 237)
#8   7ffee4a2ff40 I   resource://devtools/client/shared/vendor/reselect.js:99 (2576efc79fb0 @ 95)
#9   7ffee4a30ab0 I   resource://devtools/client/shared/vendor/reselect.js:49 (2576efc79d80 @ 57)
#10   7ffee4a30b00 I   resource://devtools/client/shared/vendor/reselect.js:99 (2576efc79fb0 @ 95)
#11   7ffee4a31670 I   resource://devtools/client/shared/vendor/reselect.js:49 (2576efc79d80 @ 57)
#12   7ffee4a31740 b   resource://devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js:298 (bd8e61e9c0 @ 78)
#13   7ffee4a317c0 I   resource://devtools/client/shared/vendor/react-redux.js:1776 (2576efc43dd0 @ 38)
#14   7ffee4a317c0 I   resource://devtools/client/shared/vendor/react-redux.js:1937 (2576efc46600 @ 16)
#15   7ffee4a31810 I   resource://devtools/client/shared/vendor/react-redux.js:1954 (2576efc46650 @ 162)
#16   7ffee4a31870 I   resource://devtools/client/shared/vendor/react-redux.js:1959 (2576efc466a0 @ 27)
#17   7ffee4a318d0 I   resource://devtools/client/shared/vendor/react-redux.js:1233 (2576efc431f0 @ 29)
#18   7ffee4a31910 I   resource://devtools/client/shared/vendor/react-redux.js:1406 (2576efc436a0 @ 31)
#19   7ffee4a31950 I   self-hosted:873 (fc81c5f4c40 @ 398)
#20   7ffee4a319b0 I   resource://devtools/client/shared/vendor/react-redux.js:1159 (2576efc38fb0 @ 62)
#21   7ffee4a319b0 I   resource://devtools/client/shared/vendor/react-redux.js:1198 (2576efc48bf0 @ 22)
#22   7ffee4a319f0 I   self-hosted:873 (fc81c5f4c40 @ 398)
#23   7ffee4a31a30 I   resource://devtools/client/shared/vendor/react-redux.js:1409 (2576efc436a0 @ 71)
#24   7ffee4a31a70 I   self-hosted:873 (fc81c5f4c40 @ 398)
#25   7ffee4a31ad0 I   resource://devtools/client/shared/vendor/react-redux.js:1159 (2576efc38fb0 @ 62)
#26   7ffee4a31ad0 I   resource://devtools/client/shared/vendor/react-redux.js:1198 (2576efc48bf0 @ 22)
#27   7ffee4a31b10 I   self-hosted:873 (fc81c5f4c40 @ 398)
#28   7ffee4a31b50 I   resource://devtools/client/shared/vendor/react-redux.js:1409 (2576efc436a0 @ 71)
#29   7ffee4a31b90 I   self-hosted:873 (fc81c5f4c40 @ 398)
#30   7ffee4a31c50 b   resource://devtools/client/shared/vendor/redux.js:265 (2576efc4c0b0 @ 283)
#31   7ffee4a31d10 b   resource://devtools/client/debugger/src/actions/utils/middleware/wait-service.js:71 (bd8e6e9150 @ 72)
#32   7ffee4a31dd0 b   resource://devtools/client/debugger/src/actions/utils/middleware/promise.js:46 (bd8e6e2e20 @ 43)
#33   7ffee4a31e80 b   resource://devtools/client/debugger/src/actions/utils/middleware/context.js:35 (bd8e6e2ba0 @ 55)
#34   7ffee4a31f30 b   resource://devtools/client/debugger/src/actions/utils/middleware/thunk.js:29 (bd8e6e2a10 @ 114)
#35   7ffee4a31ff0 b   resource://devtools/client/shared/vendor/redux.js:520 (2576efc4c5b0 @ 41)
#36   7ffee4a320a0 b   resource://devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js:95 (bd8e61e560 @ 21)
#37   7ffee4a32190 b   resource://devtools/client/debugger/src/components/shared/ManagedTree.js:64 (bd8e60c290 @ 819)
#38   7ffee4a32260 b   resource://devtools/client/debugger/src/components/shared/ManagedTree.js:129 (bd8e60c560 @ 19)
#39   7ffee4a322d0 I   resource://devtools/client/debugger/dist/vendors.js:738 (fc81c5bdba0 @ 274)
#40   7ffee4a323b0 b   resource://devtools/client/debugger/dist/vendors.js:757 (fc81c5bdb50 @ 313)
#41   7ffee4a323f0 I   self-hosted:873 (fc81c5f4c40 @ 398)
#42   7ffee4a324a0 b   resource://devtools/client/debugger/dist/vendors.js:700 (fc81c5bdab0 @ 17)
#43   7ffee4a32510 I   resource://devtools/client/shared/vendor/react-dom.js:8303 (2395af87ca60 @ 72)
#44   7ffee4a325d0 I   resource://devtools/client/shared/vendor/react-dom.js:8482 (2395af87cb50 @ 453)
#45   7ffee4a32660 I   resource://devtools/client/shared/vendor/react-dom.js:10599 (2395af884970 @ 314)
#46   7ffee4a32700 I   resource://devtools/client/shared/vendor/react-dom.js:11419 (2395af884e70 @ 1528)
#47   7ffee4a32760 I   resource://devtools/client/shared/vendor/react-dom.js:14702 (2395af8897e0 @ 204)
#48   7ffee4a327a0 I   resource://devtools/client/shared/vendor/react-dom.js:14720 (2395af889830 @ 48)
#49   7ffee4a32900 b   resource://devtools/client/shared/vendor/react-dom.js:14803 (2395af889880 @ 601)
#50   7ffee4a329e0 b   resource://devtools/client/shared/vendor/react-dom.js:15655 (2395af88c3d0 @ 217)
#51   7ffee4a32ac0 b   resource://devtools/client/shared/vendor/react-dom.js:15567 (2395af88c2e0 @ 400)
#52   7ffee4a32b80 b   resource://devtools/client/shared/vendor/react-dom.js:15541 (2395af88c290 @ 12)
#53   7ffee4a32c20 b   resource://devtools/client/shared/vendor/react-dom.js:15410 (2395af88c100 @ 144)
#54   7ffee4a32cf0 b   resource://devtools/client/shared/vendor/react-dom.js:15224 (2395af889ce0 @ 233)
#55   7ffee4a32dd0 b   resource://devtools/client/shared/vendor/react-dom.js:8192 (2395af8955b0 @ 184)
#56   7ffee4a32ec0 b   resource://devtools/client/shared/vendor/react.js:328 (2395af89e0b0 @ 132)
#57   7ffee4a32f10 I   resource://devtools/client/shared/vendor/react-redux.js:1412 (2576efc436a0 @ 120)
#58   7ffee4a32f50 I   self-hosted:873 (fc81c5f4c40 @ 398)
#59   7ffee4a32fb0 I   resource://devtools/client/shared/vendor/react-redux.js:1159 (2576efc38fb0 @ 62)
#60   7ffee4a32fb0 I   resource://devtools/client/shared/vendor/react-redux.js:1198 (2576efc48bf0 @ 22)
#61   7ffee4a32ff0 I   self-hosted:873 (fc81c5f4c40 @ 398)
#62   7ffee4a33030 I   resource://devtools/client/shared/vendor/react-redux.js:1409 (2576efc436a0 @ 71)
#63   7ffee4a33070 I   self-hosted:873 (fc81c5f4c40 @ 398)
#64   7ffee4a330d0 I   resource://devtools/client/shared/vendor/react-redux.js:1159 (2576efc38fb0 @ 62)
#65   7ffee4a330d0 I   resource://devtools/client/shared/vendor/react-redux.js:1198 (2576efc48bf0 @ 22)
#66   7ffee4a33110 I   self-hosted:873 (fc81c5f4c40 @ 398)
#67   7ffee4a33150 I   resource://devtools/client/shared/vendor/react-redux.js:1409 (2576efc436a0 @ 71)
#68   7ffee4a33190 I   self-hosted:873 (fc81c5f4c40 @ 398)
#69   7ffee4a33250 b   resource://devtools/client/shared/vendor/redux.js:265 (2576efc4c0b0 @ 283)
#70   7ffee4a33310 b   resource://devtools/client/debugger/src/actions/utils/middleware/wait-service.js:71 (bd8e6e9150 @ 72)
#71   7ffee4a333d0 b   resource://devtools/client/debugger/src/actions/utils/middleware/promise.js:46 (bd8e6e2e20 @ 43)
#72   7ffee4a33480 b   resource://devtools/client/debugger/src/actions/utils/middleware/context.js:35 (bd8e6e2ba0 @ 55)
#73   7ffee4a33530 b   resource://devtools/client/debugger/src/actions/utils/middleware/thunk.js:29 (bd8e6e2a10 @ 114)
#74   7ffee4a335f0 b   resource://devtools/client/shared/vendor/redux.js:681 (2576efc4cab0 @ 24)
#75   7ffee4a336b0 b   resource://devtools/client/debugger/src/actions/sources/newSources.js:400 (2576efce5060 @ 67)
#76   7ffee4a33760 b   resource://devtools/client/debugger/src/actions/utils/middleware/thunk.js:29 (bd8e6e2a10 @ 92)
#77   7ffee4a33820 b   resource://devtools/client/shared/vendor/redux.js:681 (2576efc4cab0 @ 24)
#78   7ffee4a338d0 b   resource://devtools/client/debugger/src/actions/sources/newSources.js:362 (2576efce1e70 @ 1078)
#79   7ffee4a33980 b   resource://devtools/client/debugger/src/actions/utils/middleware/thunk.js:29 (bd8e6e2a10 @ 92)
#80   7ffee4a33a40 b   resource://devtools/client/shared/vendor/redux.js:681 (2576efc4cab0 @ 24)
#81   7ffee4a33af0 b   resource://devtools/client/debugger/src/actions/sources/newSources.js:234 (2576efce1bf0 @ 421)
#82   7ffee4a33ba0 b   resource://devtools/client/debugger/src/actions/utils/middleware/thunk.js:29 (bd8e6e2a10 @ 92)
#83   7ffee4a33c60 b   resource://devtools/client/shared/vendor/redux.js:520 (2576efc4c5b0 @ 41)
#84   7ffee4a33d20 b   resource://devtools/client/debugger/src/utils/source-queue.js:20 (2395af89ece0 @ 64)
#85   7ffee4a34d00 b   resource://devtools/client/shared/vendor/lodash.js:10333 (1dcd73625330 @ 65)
#86   7ffee4a34db0 b   resource://devtools/client/shared/vendor/lodash.js:10343 (1dcd73625380 @ 58)
#87   7ffee4a34e80 b   resource://devtools/client/shared/vendor/lodash.js:10410 (1dcd736255b0 @ 120)
#88   7ffee4a34f20 b   resource://devtools/client/debugger/src/utils/source-queue.js:31 (2395af89ed80 @ 29)
#89   7ffee4a34fe0 b   resource://devtools/client/debugger/src/client/firefox/events.js:135 (bd8e6e9600 @ 76)
#90   7ffee4a35030 I   self-hosted:919 (fc81c5e8c40 @ 417)
#91   7ffee4a35840 b   resource://devtools/shared/event-emitter.js:226 (63261824f10 @ 727)
#92   7ffee4a35910 b   resource://devtools/shared/event-emitter.js:172 (63261824e70 @ 134)
#93   7ffee4a359e0 b   resource://devtools/shared/event-emitter.js:324 (632618201f0 @ 126)
#94   7ffee4a35ac0 b   resource://devtools/shared/protocol/Front.js:294 (4b23295e510 @ 603)
#95   7ffee4a35b90 b   resource://devtools/shared/client/devtools-client.js:495 (4b232997510 @ 287)
#96   7ffee4a35c40 b   resource://devtools/shared/transport/local-transport.js:68 (4b232997e70 @ 132)
#97   7ffee4a36c30 b   resource://devtools/shared/ThreadSafeDevToolsUtils.js:111 (4b2329cc880 @ 42)
#98   7ffee4a37c20 b   resource://devtools/shared/ThreadSafeDevToolsUtils.js:111 (4b2329cc880 @ 42)

Digging into that further, it isn't actually that we are slow rendering the sidebar, the issue is that we are constantly getting newSource notifications and therefore resource://devtools/client/debugger/src/actions/sources/newSources.js is constantly triggering updates that require us to regenerate the list of files for the source tree.

If you edit the TabSources.js file to inspect all the new sources, it appears that this page makes super aggressive use of new Function/eval to execute logic on the page, and every time it does so, the DevTools are notified and the UI tries to decide if any new files need to be shown in the list of sidebar files. Since evaled files don't show up in the sidebar, there aren't any re-renders happening AFAIK, but we have to constantly query the redux store to decide if there is anything new.

I'm sure there's an long discussion that we could have here about optimizing the client and server to handle constant eval calls.

In the short term, I'd recommend that this site explore alternatives to constant creation of new functions. The script https://en.oui.sncf/abtasty/ww/WW.js contains a payload with tons and tons of strings that get evaluated somewhere. Search for if(window.customerLib.customer) { for an example. It appears that many of these strings are re-evaled over and over, so one option might be for this site to cache the functions instead of re-evaling the string each time the function runs. Is that something that could be explored Raphaël?

Flags: needinfo?(raphael.mozilla)

Hello Logan,

Thanks for the investigation. Unfortunately, I am not involved with the development of this website. I develop Oui Light, a Firefox extension to debloat oui.sncf :-)

Flags: needinfo?(raphael.mozilla)

Ahh well then, nevermind!

I just verified and it looks like injecting

Function = (function(Function){
  const cache = new Map();
  return function(...args){
    const key = JSON.stringify(args);
    let value = cache.get(key);
    if (!value) {
      value = new Function(...args);
      cache.set(key, value);
    }
    return value;
  };
})(Function);

via the Console tool before switching to Debugger tool allows the debugger to load successfully on that page, since that prevents the page from constantly recreating functions with the same arguments. Hard to say if that would cause issues though, I suppose it depends on whether the object identity of any of the evaled values is important to the functionality of the page.

Summary: Debugger tab in developer tools slugging on https://oui.sncf/ → Pages that constantly call `eval`/`new Function` can bring the devtools parent-process UI to its knees

The priority flag is not set for this bug.
:jlast, could you have a look please?

For more information, please visit auto_nag documentation.

Flags: needinfo?(jlaster)
Status: UNCONFIRMED → NEW
Ever confirmed: true
Priority: -- → P3

Logan, given how bad the impact of this is; I wonder if it can affect developers that depend on new Function/eval as it was a common pattern in the past with frameworks like Angular. If it has wider reach, it might be worth to elevate it to P2. WDYT?

Flags: needinfo?(jlaster) → needinfo?(loganfsmyth)

The issue here is specifically that new Function/eval is called constantly. Any framework would generally keep those calls to an absolute minimum by running them once up front and then using the returned function to call the logic multiple times, or else is at least executing a string that is static and evals the same string every time and can therefore cached by SpiderMonkey.

I'd personally expect the conditions required to trigger this to be pretty uncommon. That said, it's certainly a frustrating issue, so it probably would make sense to try to get to sooner rather than later.

Flags: needinfo?(loganfsmyth)

Because this bug's Severity has not been changed from the default since it was filed, and it's Priority is P3 (Backlog,) indicating it has been triaged, the bug's Severity is being updated to S3 (normal.)

Severity: normal → S3
Assignee: nobody → jlaster
Status: NEW → ASSIGNED
Pushed by jlaster@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/fd9f980e3681
Improve performance of evals in the debugger. r=loganfsmyth
Status: ASSIGNED → RESOLVED
Closed: 1 month ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 79
No longer blocks: 1642034
You need to log in before you can comment on or make changes to this bug.