Closed Bug 1553291 Opened 7 months ago Closed 6 months ago

CodeMirror chokes on large minified files, causing main thread hands in Debugger

Categories

(DevTools :: Debugger, defect, P2)

defect

Tracking

(firefox69 fixed)

RESOLVED FIXED
Firefox 69
Tracking Status
firefox69 --- fixed

People

(Reporter: Harald, Assigned: jlast)

References

(Blocks 1 open bug)

Details

(Whiteboard: [debugger-mvp])

Attachments

(1 file)

Loading any large minified file with long minified lines causes the Debugger to freeze for seconds.

https://perfht.ml/2EnHDQG shows a 19s hang for coda.io.

The expensive indexOf seems to be findColumn https://dxr.mozilla.org/mozilla-central/source/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js#167 , where it is called in a loop to find the next tab character. indexOf seems to be slow for the long strings that minified lines can produce.

findColumn is being called by column https://dxr.mozilla.org/mozilla-central/source/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js#1448

Reduced STR outside of devtools:

  1. Open gist.github.com (uses codemirror as well)
  2. Name the first entry file.js
  3. Start Profiler
  4. Paste the content of https://cdnjs.cloudflare.com/ajax/libs/extjs/6.2.0/ext-all.js
  5. Capture Profile: https://perfht.ml/2Eqk8GB

I did wget https://cdnjs.cloudflare.com/ajax/libs/extjs/6.2.0/ext-all.js and ran the following script in the JS shell (the countColumn function is from the link in comment 0):

let source = os.file.readFile("ext-all.js");

// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
function countColumn(string, end, tabSize, startIndex, startValue) {
  if (end == null) {
    end = string.search(/[^\s\u00a0]/)
    if (end == -1) { end = string.length }
  }
  for (var i = startIndex || 0, n = startValue || 0;;) {
    var nextTab = string.indexOf("\t", i)
    if (nextTab < 0 || nextTab >= end)
    { return n + (end - i) }
    n += nextTab - i
    n += tabSize - (n % tabSize)
    i = nextTab + 1
  }
}

let t0 = Date.now();
for (let i = 0; i < 100; i++) {
  countColumn(source, source.length, 8);
}
print((Date.now() - t0) / 100);

The result is 0.45 msec (to call countColumn on the actual minified code from the STR).

The editor must be calling this function multiple times.

(In reply to Jason Orendorff [:jorendorff] from comment #2)

The editor must be calling this function multiple times.

I imagine it could be helpful if perf-html was telling how many time a given function was called.
We are often slow because we are calling this or that way too many times.
It typically applies to componentWillUpdate or similar React functions.

Duplicate of this bug: 1544957
Priority: -- → P2
Whiteboard: [debugger-mvp]

I imagine it could be helpful if perf-html was telling how many time a given function was called.

The Stack Chart shows how the function is called multiple times, but I agree that it would be nice to have some estimate of calls shown in the UI.

Harld, can you share a debugger STR? we probably just need a page w/ a minified file...

Flags: needinfo?(hkirschner)

STR:

  1. Open Debugger on https://examples.sencha.com/extjs/6.7.0/examples/kitchensink/?modern
  2. File opening hang: Open source /extjs/6.7.0/examples/kitchensink/material-en/app.js
  3. File switching hang: Switch to source /extjs/6.7.0/examples/kitchensink/?modern#all and back to app.js

Profile with screenshots: https://perfht.ml/2KQq3cf

Flags: needinfo?(hkirschner)
Blocks: dbg-70
Assignee: nobody → jlaster
Status: NEW → ASSIGNED
Pushed by jlaster@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/d0823ee61ef5
CodeMirror chokes on large minified files. r=davidwalsh
Status: ASSIGNED → RESOLVED
Closed: 6 months ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 69
Blocks: dbg-69
No longer blocks: dbg-70

More work to do here, filed bug 1578587.

You need to log in before you can comment on or make changes to this bug.