rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.63k stars 2.4k forks source link

Cargo timings graph is broken in Firefox #8850

Open Lucretiel opened 3 years ago

Lucretiel commented 3 years ago

When I run cargo +nightly -Z timings, it produces a timings report in HTML as expected. This renders correctly in Chrome and Safari. However, in Firefox, I only see the summary table and timings breakdown table. I can see the controls for the graph, but the graph itself doesn't render:

image

In the error console, I see the following uncaught exception:

Uncaught 
    Exception
        columnNumber: 0
        data: null
        filename: "http://localhost:8080/cargo-timing.html"
        lineNumber: 24173
        message: ""
        name: "NS_ERROR_FAILURE"
        result: 2147500037
        stack: "setup_canvas@http://localhost:8080/cargo-timing.html:24173:7\ndraw_graph_axes@http://localhost:8080/cargo-timing.html:24187:25\nrender_pipeline_graph@http://localhost:8080/cargo-timing.html:23930:86\n@http://localhost:8080/cargo-timing.html:24331:1\n"
        <prototype>: ExceptionPrototype { toString: toString(), name: Getter, message: Getter, … }

This exception is being thrown from the following function, specifically at ctx.scale(dpr, dpr) near the end:

function setup_canvas(id, width, height) {
  let g = document.getElementById(id);
  let dpr = window.devicePixelRatio || 1;
  g.width = width * dpr;
  g.height = height * dpr;
  g.style.width = width;
  g.style.height = height;
  let ctx = g.getContext('2d');
  ctx.scale(dpr, dpr);  // Exception thrown here
  return ctx;
}

Digging into the debugger, there don't seem to be any obvious issue with that line of code. ctx is of prototype CanvasRenderingContext2DPrototype, and the scale method has been supported in firefox pretty much forever. dpr == 2.

Meta

rustc --version --verbose:

cargo 1.49.0-nightly (79b397d72 2020-10-15)
release: 1.49.0
commit-hash: 79b397d72c557eb6444a2ba0dc00a211a226a35a
commit-date: 2020-10-15

Lucretiel commented 3 years ago

Here's the complete HTML report triggering this issue: cargo-timing.html.zip

ehuss commented 3 years ago

Transferred to the cargo repo.

Set the scale slider to the far left and reload the page, and then slowly raise the scale slider up to whatever limit it can handle. The problem is that the canvas is too large. AFAIK, there isn't a way to get the maximum canvas size (I think it may be graphics-card dependent), so it's kinda hard to know how to handle that.

Lucretiel commented 3 years ago

Thanks, will give it a try

Lucretiel commented 3 years ago

Yeah, that fixes it. Sliding the scale down doesn't do anything immediately, but refreshing afterwards does show the graphs.

upsuper commented 1 year ago

I looked into this issue a bit. For me the rendering works until scale level 18, where canvas size being 4,876 x 25,406 = 123,879,656, and at scale level 19, where canvas size is 5,138 x 25,406 = 130,536,028, it stops rendering.

This is indeed a huge canvas, but not too crazy, and it's still smaller than what's documented on MDN that Firefox supports up to 472,907,776 px of area.

So I dug into Firefox source code a bit, and I believe this is not limited by something graphics card dependent, but a fixed value in the prefs, specifically gfx.max-alloc-size, which has a default value of 500,000,000 and its unit is bytes.

It can be seen that

to confirm that, I tried changing this value to 600,000,000 and restarted Firefox (as this value is read only once at startup), and indeed I could now get to scale level 21, where the canvas is 5,662 x 25,406, and failed again at level 22, where the canvas is 5,924 x 25,406. Repeat the computation above:

So it can be seen, this pref is the limiting factor.

Currently, the code that Cargo generates seems to try to limit the width of the graph in order to ensure that it can be rendered: https://github.com/rust-lang/cargo/blob/5ccea519d43d09c0edf4b5dffc32e068fbf46296/src/cargo/core/compiler/timings.js#L297-L301

given that browsers have limitation on total area in addition to max single dimension, it might be better to limit it based on some area estimation rather than a hardcoded width limit.

Also, at this large canvas size, many browsers may have lost the graphics acceleration anyway, making the rendering very slow. I would suggest to have a canvas size relative to the viewport, and do windowing manually, rather than stressing browsers on their limits.

nagisa commented 8 months ago

For me the problem seems to have to do with the height of the canvas being more than 32k pixels. The graph won’t render until I filter out enough crates with the "min duration" slider. Here's the relevant bugzilla: https://bugzilla.mozilla.org/show_bug.cgi?id=1282074

I don’t think this can be fixed holistically by cosmetic changes to the canvas-based code. Rewriting the entire thing to DOM, SVG or if canvas must be kept, implementing some sort of scrolling and a rendering loop will be necessary.

bbb651 commented 6 months ago

I'm also in favor of a DOM/SVG based solution, it offers several immediate benefits:

There are existing tools that use both approaches, for example FlameGraph uses an SVG and speedscope uses an interactive canvas interface. I think making use of an existing tool, by either utilizing an existing benchmark file format or embedding the viewer it in the generated html would provide the best experience, but either way would probably be as much work as rewriting the entire thing...