devtools icon indicating copy to clipboard operation
devtools copied to clipboard

Ship DevTools with dart2wasm

Open kenzieschmoll opened this issue 1 year ago • 18 comments

The DevTools codebase is prepared for building DevTools with dart2wasm thanks to the unblocking work here.

There are still a few requirements that we need to meet before moving this effort forward:

  • [x] Land the bootstrapping changes to DevTools in a way that does not break our g3 roll.
    • CC @eyebrowsoffire @xster we will need support from the Fl@G team to fully support the new bootstrapping syntax. If adding full support is infeasible right now, we may have to workaround this by diverging the g3 bootstrapping code from the external. This is not ideal since this will cause a continued maintenance burden and potential regressions in g3, given we will not be testing externally with the same bootstrapping used in g3. See b/343786318 for tracking issue.
    • Done in https://github.com/flutter/devtools/pull/7865. G3 change to use legacy index.html: cl/638797386.
  • [ ] Ensure that the VS Code embedded web views support WasmGC (see background information on Chromium and V8)
    • @DanTup can you confirm?
  • [x] Ensure that the IntelliJ embedded web views (JXBrowser) support WasmGC (see background information on Chromium and V8)
    • @helin24 can you confirm?
  • [ ] Add support for opt in / opt out behavior. See these web bootstrapping docs for an example of customizing configuration based on query parameters.
  • [ ] Add support for building DevTools with wasm and serving it from the DevTools server with the proper headers
    • https://github.com/flutter/devtools/pull/7867
    • https://github.com/flutter/devtools/pull/7888
    • https://dart-review.googlesource.com/c/sdk/+/369100
  • [ ] Add support for building with Wasm to the recipe running the automated LUCI builds of DevTools. These builds are what ships with the Dart SDK

Compare the dart2wasm DevTools build to dart2js and file any issues that come up.

Blocking:

  • https://github.com/flutter/flutter/issues/149909

kenzieschmoll avatar May 30 '24 16:05 kenzieschmoll

@kenzieschmoll – keep in mind, we can use query param magic to force loading certain versions. So if there are contexts where wasm won't work, we can just disable them. Or we can make loading the wasm version opt-in for testing, etc.

kevmoo avatar May 30 '24 16:05 kevmoo

Ha! just read your last bullet. 🤦

kevmoo avatar May 30 '24 16:05 kevmoo

Ensure that the VS Code embedded web views support WasmGC (see background information on Chromium and V8)

The current version of VS Code uses Chromium v120 which sounds like it should support this. However, I don't know if there are things in Electron/VS Code themselves that might impact the user of this. @kenzieschmoll what's the easiest way for me to build DevTools using WASM (can I do it through devtools_tool serve? hacking scripts locally to make this work would be fine for testing).

And when it is running, how do I confirm it is using the wasm version and didn't fall back to js?

DanTup avatar May 31 '24 10:05 DanTup

what's the easiest way for me to build DevTools using WASM (can I do it through devtools_tool serve? hacking scripts locally to make this work would be fine for testing).

And when it is running, how do I confirm it is using the wasm version and didn't fall back to js?

Patch the changes from https://github.com/flutter/devtools/pull/7867 and https://dart-review.googlesource.com/c/sdk/+/369100. Then run devtools_tool serve --wasm.

If the wasm changes are picked up, you should see main.dart.wasm, main.dart.mjs, and canvaskit/skwasm.js, canvaskit/skwasm.wasm in the loaded sources. Here is what I see locally. Screenshot 2024-05-31 at 8 45 45 AM

kenzieschmoll avatar May 31 '24 16:05 kenzieschmoll

IntelliJ uses JxBrowser version 7.38.2 currently, which includes Chromium 124.0.6367.92 and should support WasmGC.

It's hard for me to tell what version of JCEF IntelliJ uses when that option is chosen by user setting, but JCEF is a few versions behind the CEF library, which currently is using Chromium 125 (https://cef-builds.spotifycdn.com/index.html).

In summary I think either implementation of embedded browser will be fine for wasm.

helin24 avatar May 31 '24 16:05 helin24

@yjbanov the sequencing will likely be that we'll work on shipping devtools in wasm in google after we decide to ship flutter web in wasm in google3

xster avatar May 31 '24 22:05 xster

@kenzieschmoll

Patch the changes from https://github.com/flutter/devtools/pull/7867 and https://dart-review.googlesource.com/c/sdk/+/369100. Then run devtools_tool serve --wasm.

The first has already landed but I patched in the second. I added "--wasm" to my dart.customDevTools setting. Inside VS Code, it seems to have loaded the JS version:

image

However if I open in an external browser it loads the wasm version, however it throws and the app doesn't load:

image

I presume the first issue is that whatever is being used to detect whether wasm is available is failing. @kevmoo is there an easy way for me to review (or step through) these conditions? Looking through flutter_bootstrap.js I found a function that looked like it was doing that which runs:

let o = [0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 95, 1, 120, 0];
return WebAssembly.validate(new Uint8Array(o))

However if I run that myself in the console, it appears to return true, so I think there may be other conditions.

image

DanTup avatar Jun 04 '24 12:06 DanTup

Seems like the issue is that window.crossOriginIsolated is false

image

The docs here confirm the headers in the CL above are required, but I see those are also set:

image

It's not clear to me why this value is false. There's a similar issue at https://github.com/microsoft/vscode/issues/183765 from last year about this being the case on Github.dev and a link to a private repo with a fix, which I suspect is just setting the same headers.

DanTup avatar Jun 04 '24 12:06 DanTup

I tracked this back to https://github.com/microsoft/vscode/issues/186614. It seems cross-origin-isolatation is currently disabled by VS Code. When it was enabled the were performance issues that resulted in it being reverted.

I tried running code --enable-coi to force this enabled for testing, however the iframe with DevTools in it won't load at all:

image

Which seems to be because another header is not present:

image

I don't know the implications of adding that header.

DanTup avatar Jun 04 '24 12:06 DanTup

Yes, if you're loading the app in an iframe, you will need to set a CORP header for the child document if you want cross origin isolation (which is needed for the multi-threaded rendering that the skwasm renderer does). I don't know exactly how the embedding of this works with VS Code. I think Cross-Origin-Resource-Policy: cross-origin should be fine for this use case, as long as all the assets that devtools uses are also served with Cross-Origin-Resource-Policy: cross-origin.

eyebrowsoffire avatar Jun 04 '24 15:06 eyebrowsoffire

Thanks! Adding that header on its own didn't work, but then I found I needed allow="cross-origin-isolated" on the iframe. With that, the flags are all true and the wasm version is used - and fails with:

image

This is the same error I posted above when running outside of VS Code. It's not clear to me if that's a DevTools or wasm bug?

DanTup avatar Jun 05 '24 10:06 DanTup

This is the same error I posted above when running outside of VS Code. It's not clear to me if that's a DevTools or wasm bug?

I can't reproduce on Mac, so I am wondering if this is a Windows issue with wasm. CC @eyebrowsoffire

kenzieschmoll avatar Jun 05 '24 17:06 kenzieschmoll

@DanTup Could you try building again with --name-section enabled so we get a more useful stack trace? flutter build web --wasm --name-section

eyebrowsoffire avatar Jun 05 '24 19:06 eyebrowsoffire

@eyebrowsoffire that flag didn't seem to work:

C:\Dev\Google\devtools\packages\devtools_app > C:\Dev\Google\devtools\tool\flutter-sdk\bin\flutter.bat build web --wasm --name-section --pwa-strategy=offline-first --no-tree-shake-icons
Could not find an option named "name-section".

I tried --no-strip-wasm (which I saw in https://github.com/dart-lang/sdk/commit/a98ce03e13c530f919e79512119271a659c35974) which initially gave the same error, but Ctrl+refresh resulted in:

Uncaught RuntimeError: illegal cast
    at _isFlutterGAEnabled inner (main.dart.wasm:0x38f4da)
    at _awaitHelperWithTypeCheck closure at org-dartlang-sdk:///dart-sdk/lib/_internal/wasm/lib/async_patch.dart:97:16 (main.dart.wasm:0x1fdf8e)
    at closure wrapper at org-dartlang-sdk:///dart-sdk/lib/_internal/wasm/lib/async_patch.dart:97:16 trampoline (main.dart.wasm:0x1fe083)
    at _rootRunUnary (main.dart.wasm:0x1febae)
    at _CustomZone.runUnary (main.dart.wasm:0x50f355)
    at _Future._propagateToListeners (main.dart.wasm:0x1fe77a)
    at _Future._completeWithValue (main.dart.wasm:0x1fedfc)
    at _Future._asyncCompleteWithValue closure at org-dartlang-sdk:///dart-sdk/lib/async/future_impl.dart:735:29 (main.dart.wasm:0x20fbd6)
    at closure wrapper at org-dartlang-sdk:///dart-sdk/lib/async/future_impl.dart:735:29 trampoline (main.dart.wasm:0x20fbec)
    at _rootRun (main.dart.wasm:0x1fd210)

The code for that method is here:

https://github.com/flutter/devtools/blob/56dcf7420a2a0f03d2b754b5a3a0488844db1fdd/packages/devtools_app/lib/src/shared/server/_analytics_api.dart#L66

The response from the server appears to be an empty string:

image

Which I guess is coming from here:

https://github.com/flutter/devtools/blob/56dcf7420a2a0f03d2b754b5a3a0488844db1fdd/packages/devtools_shared/lib/src/server/server_api.dart#L70-L72

So possibly this code here is triggering the exception?

      // A return value of 'null' implies Flutter tool has never been run so
      // return false for Flutter GA enabled.
      final responseValue = json.decode(resp!.body);
      enabled = responseValue ?? false;

DanTup avatar Jun 05 '24 19:06 DanTup

Yes, sorry, I meant --no-strip-wasm, I got the dart compile wasm commands confused with the flutter tool's.

There are no casts in that function from the user code that I can see. I wonder if something is being inlined? You can also pass -O0 and see if that disables some inlining and that might clarify what's going on here a bit.

eyebrowsoffire avatar Jun 05 '24 20:06 eyebrowsoffire

There are no casts in that function from the user code that I can see

It's assigning responseValue (if not null) to enabled. enabled is a bool, responseValue is a string (and my IDE says responseValue is dynamic. It's not clear to me why this would behave different for wasm though.

Using -O0 I get this (which does seem to line up):

main.dart.mjs:56 Type 'String' is not a subtype of type 'bool' in type cast
main.dart.mjs:56     at _isFlutterGAEnabled inner (http://127.0.0.1:9101/main.dart.wasm:wasm-function[34885]:0x5b2405)
    at _awaitHelper closure at org-dartlang-sdk:///dart-sdk/lib/_internal/wasm/lib/async_patch.dart:83:16 (http://127.0.0.1:9101/main.dart.wasm:wasm-function[62766]:0x8183af)
    at closure wrapper at org-dartlang-sdk:///dart-sdk/lib/_internal/wasm/lib/async_patch.dart:83:16 trampoline (http://127.0.0.1:9101/main.dart.wasm:wasm-function[62772]:0x8184b5)
    at _rootRunUnary (http://127.0.0.1:9101/main.dart.wasm:wasm-function[2992]:0x3057e0)
    at _rootRunUnary tear-off trampoline (http://127.0.0.1:9101/main.dart.wasm:wasm-function[62738]:0x817ef3)
    at _CustomZone.runUnary (http://127.0.0.1:9101/main.dart.wasm:wasm-function[62613]:0x816525)
    at _FutureListener.handleValue (http://127.0.0.1:9101/main.dart.wasm:wasm-function[2985]:0x30556f)
    at _Future._propagateToListeners closure handleValueCallback at org-dartlang-sdk:///dart-sdk/lib/async/future_impl.dart:859:33 (http://127.0.0.1:9101/main.dart.wasm:wasm-function[2963]:0x304f15)

DanTup avatar Jun 06 '24 16:06 DanTup

Since that code looks bogus to me, I retried in non-wasm mode and I see errors in the console - however unlike the wasm version, it doesn't stop DevTools loading (which is why I didn't notice it before). In wasm mode, I just get a white page with nothing rendered, but with JS DevTools loads and appears to function.

image

(@kenzieschmoll this seems like a bug though - that API seems to be hard-coded to return an empty string and the code doesn't seem to handle empty strings, it assumes null or a boolean?)

DanTup avatar Jun 06 '24 16:06 DanTup

this seems like a bug though - that API seems to be hard-coded to return an empty string and the code doesn't seem to handle empty strings, it assumes null or a boolean?).

Yes this regressed recently. Fix is here: https://github.com/flutter/devtools/pull/7896

kenzieschmoll avatar Jun 07 '24 16:06 kenzieschmoll

@DanTup now that the regression with accessing the Flutter store file has been fixed, are you able to verify whether the wasm build works properly when embedded in VS Code now?

kenzieschmoll avatar Jul 10 '24 16:07 kenzieschmoll

cross-origin-isolate is still gated with a flag in VS Code (so normal users can't get a wasm version), but if I:

  1. Run code --enable-coi to enable cross-origin-isolate
  2. Patch in ..add('Cross-Origin-Resource-Policy', 'cross-origin') into the DevTools server
  3. Set customDevTools in VS Code to run with --wasm

Then it loads:

image

However, I can't interact with the inspector.. clicking on items in the tree just doesn't do anything (no visible selection or anything). I do now have this error in the console, though it appears as the page loads (not when I click) so I'm not sure it's related:

image

However, if I open externally in the browser, then all is good (clicking in the inspector works), so it does seem like there's more broken in VS Code (I don't know if this might be why COI is still behind a flag or unrelated). I'll need to do some more debugging to try and figure out what's wrong here.

DanTup avatar Jul 10 '24 18:07 DanTup

@DanTup – the cast is likely here: https://github.com/flutter/devtools/blob/86ef045a4cca71c8fdbd6a5931d80b049f93440e/packages/devtools_app/lib/src/shared/server/_release_notes_api.dart#L32

How hard is it to create a _decodeBoyAsBool function with some debug printing (instead of (json.decode(respo.body) as bool) – so we can see what's happening there?

kevmoo avatar Jul 10 '24 18:07 kevmoo

Ah, thanks for the pointer. I can just use the dev tools to see the network response - and it's a string:

image

Looks like this was actually fixed recently via https://github.com/flutter/devtools/pull/7997 but I didn't have a dependency_override in DDS to get that fix in the server.

I wasn't expecting it, but re-running with that fix included I can use the inspector tree fine :)

image

Maybe the release notes overlay was partially there absorbing the clicks or something? 🤷‍♂️

So, in summary:

  • if the new version of devtools_shared gets rolled into the SDK
  • and we add ..add('Cross-Origin-Resource-Policy', 'cross-origin') to the DevTools server (I don't if there are security implications to consider before doing this)
  • and we run VS Code with COI enabled (currently code --enable-coi)

Then it does appear to work (at least in my limited testing of clicking around the inspector). I don't know if/when VS Code will enable COI by default though (there's an issue at https://github.com/microsoft/vscode/issues/186614 that I think is related).

DanTup avatar Jul 10 '24 19:07 DanTup

Then it does appear to work (at least in my limited testing of clicking around the inspector). I don't know if/when VS Code will enable COI by default though (there's an issue at https://github.com/microsoft/vscode/issues/186614 that I think is related).

Is there a way we can detect whether this is enabled, and fallback to using the dart2js version of DevTools if it is not?

kenzieschmoll avatar Jul 10 '24 20:07 kenzieschmoll

@kenzieschmoll – we'll have to ask @eyebrowsoffire – or just try it out. I'm pretty sure we do automatically fallback in most failure cases, just not sure of this one.

kevmoo avatar Jul 10 '24 20:07 kevmoo

Is there a way we can detect whether this is enabled, and fallback to using the dart2js version of DevTools if it is not?

This already happens, because window.crossOriginIsolated is false in that case and the WASM version is only selected when it's true (plus some other conditions noted further up).

DanTup avatar Jul 10 '24 20:07 DanTup

But, I think we do need to add that extra header in to the DevTools server, otherwise I think we could load the WASM version but then fail with the error shown above at https://github.com/flutter/devtools/issues/7856#issuecomment-2147440081.

DanTup avatar Jul 10 '24 20:07 DanTup

Note: We do fall back to CanvasKit + JS if window.crossOriginIsolated is false

eyebrowsoffire avatar Jul 11 '24 23:07 eyebrowsoffire

This work is complete. DevTools + Wasm will be shipped as an opt-in feature behind a DevTools setting for the Q4 Dart / Flutter stable release. We will aim to turn this on by default for the Q1 2025 stable release, tracked here: https://github.com/flutter/devtools/issues/8395

kenzieschmoll avatar Oct 03 '24 16:10 kenzieschmoll

@kenzieschmoll FYI, I did more testing of the wasm version in VS Code this morning, and I was unable to repro the performance issues I'd recalled (which we discussed a little in https://github.com/Dart-Code/Dart-Code/pull/5367). I also tried embedding DevTools in an iframe outside of VS Code (so no Cross-Origin-Isolation) and failed to reproduce there.

I'll leave the wasm experiment on and keep an eye on it, but as things are I think the issue is gone (or, it's a much smaller issue than it was when I first hit it).

DanTup avatar Apr 07 '25 11:04 DanTup

That's great new @DanTup ! Thanks for reporting!

kevmoo avatar Apr 07 '25 22:04 kevmoo