-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add "std-unwrap" feature to defer to std's unwrap in unwrap_throw #3899
Conversation
I'm not in favor of adding a crate feature for this kind of adjustment. However, it would be great if you could dig into why this was guarded behind |
The original motivation in #1219 for these traits was to create variants with "a smaller code size footprint than the normal Option::unwrap and Option::expect methods". Printing the call site was added in #2995. That PR initially unconditionally printed the call site but that was objected to on code size grounds. A compromise was suggested:
and this was implemented by gating on I understand the motivation but I understand the reticence to adding a crate feature so if you have any alternative suggestions please do say. |
Thank you for the research!
I would recommend you to explore generating source maps as this would significantly improve your debugging experience in many ways. Let me know if you have any trouble with it. If that doesn't really work for you, I think the alternative to a crate feature would be something like |
Source maps would help debugging on our machines right? It's specifically crashes that occur on user machines where we need this. For crashes on our machines Chrome's DWARF extension gives us a pretty decent debugging experience. For the JS we've not got around to source maps yet, but as it's only wasm-bindgen shims I don't expect it to be too helpful for debugging. Also in favour of printing the call location: there are a couple of reasons the backtrace can be incomplete (regardless of source maps / DWARF): inlining, and lack of
Ah, good idea! Thanks, I'll go ahead and made this change shortly. |
…wrap in unwrap_throw
Source maps work on user machines as well, they also work on all current browsers without the need for any extension. If you don't want to expose any debug information to users, you may want to take a look at Wasm coredumps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A changelog entry would be necessary as well.
#[cfg(wasm_bindgen_unwrap_throw_debug)] | ||
#[track_caller] | ||
fn unwrap_throw(self) -> T { | ||
self.unwrap() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware that this is the change proposed in OP, but is the debug_assertions
output not good enough, or why do you actually need unwrap()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spend quite a bit of time triaging errors, and in that context consistency and recognisability is important. In this instance, deferring to std
maintains consistency:
-
across build targets. same error under wasm, wasi, emscripten, linux, macos and so on.
-
across calls to the
_throw
or non-_throw
variant. same error and importantly same behaviour, panicking rather than throwing.
We've implemented manual poisoning in our panic hook until #3687 is resolved. The throw inside .unwrap_throw()
bypasses that and results in unsoundness that is unavoidable without patching leptos wasm-bindgen-futures and so on to respect our poison. That's rather painful, hence this fork where we panic rather than throw. We do also implement poison in window.addEventListener("error", ...)
and window.addEventListener("unhandledrejection", ...)
for such throws but I believe e.g. wasm-bindgen-futures
calling queueMicrotask
can preempt that callback, so can't fully mitigate the unsoundness.
Anecdotally since switching to this fork various "impossible" errors e.g. rust-lang/regex#1176 have stopped occurring, so in retrospect I think the throwing plus #3687 plus leptos or wasm-bindgen-futures calling queueMicrotask
and similar was likely the cause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm questioning if wasm-bindgen
should do something here, unwrap_throw()
should explicitly throw, if it panics it would be doing something different then advertised.
I do agree that the lack of module poisoning is quite problematic, unfortunately I don't know a "quick" fix for it apart from what you have been doing.
I have a couple of suggestions:
- Maybe we can figure out a way to fix
wasm-bindgen-futures
:- Now that unwinding is actually supported in Rust Wasm we may want to think about the implementation of
wasm-bindgen-futures
more carefully. - If we can't find a good solution, I'm happy to add a
cfg
flag there to panic instead of throwing.
- Now that unwinding is actually supported in Rust Wasm we may want to think about the implementation of
- Other libraries might do whats suggested here, e.g. switch to panicking behind a
cfg
flag. - Module poisoning needs a solution, I'm happy to look at a PR here in
wasm-bindgen
, even if it should probably be solved in Rustc instead.
Please let me know what you think, I'm sympathetic to your goals but I would like to find a better solution.
fn expect_throw(self, message: &str) -> T { | ||
if cfg!(all( | ||
target_arch = "wasm32", | ||
not(any(target_os = "emscripten", target_os = "wasi")) | ||
not(any( | ||
wasm_bindgen_unwrap_throw_debug, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above. If you want to include the file, line and column as in unwrap_throw()
I think it would be a nice improvement.
fn expect_throw(self, message: &str) -> T { | ||
if cfg!(all( | ||
target_arch = "wasm32", | ||
not(any(target_os = "emscripten", target_os = "wasi")) | ||
not(any( | ||
wasm_bindgen_unwrap_throw_debug, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above.
@@ -1327,7 +1327,7 @@ pub fn anyref_heap_live_count() -> u32 { | |||
pub trait UnwrapThrowExt<T>: Sized { | |||
/// Unwrap this `Option` or `Result`, but instead of panicking on failure, | |||
/// throw an exception to JavaScript. | |||
#[cfg_attr(debug_assertions, track_caller)] | |||
#[cfg_attr(any(debug_assertions, wasm_bindgen_unwrap_throw_debug), track_caller)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be best if you just remove all guards on track_caller
, which would also improve native support.
Is there any documentation that they give Wasm stack traces line numbers? I only found the opposite.
I didn't think this was implemented in any browsers yet? |
I'm not sure where to find any documentation on this, but source maps do include line numbers and from personal testing I can say that it looks just like native stacktraces.
I'm not aware of any actual proposal that needs implementing, these are just tool conventions. If you follow the links on this page you will even find a live demo to try it out yourself. |
Thanks for the pushback @daxpedda! I looked into this a little more and decided this approach doesn't sufficiently mitigate the unsoundness we've seen the effects of. As Alex said,
I'm not seeing source maps affecting
Thanks for making me aware of it but it's sadly not workable. It's not implemented by browsers yet and so requires a sketchy polyfill |
I just tested it with
The Cloudflare blog post you linked has a decent overview how to work with it. Breaking the DWARF debuginfo would defeat the purpose of a core dump, so it actually still works. Wasm post-processing isn't ideal, but it is the status quo right now, Sorry for the delayed response! |
In our release binaries
.unwrap()
gives a useful error:Whereas
.unwrap_throw()
does not:error.stack
Currently, enabling
debug_assertions
is the only way to have.unwrap_throw()
print the call site:wasm-bindgen/src/lib.rs
Lines 1330 to 1344 in 78463a2
Gating on
debug_assertions
was intended to avoid increasing code size in release mode. Unfortunately though it makes debugging panics in release binaries significantly harder, given many fundamental libs includingwasm-bindgen-futures
,yew
andleptos
use.unwrap_throw()
.This PR adds a feature
"std-unwrap"
to makefn unwrap_throw
andfn expect_throw
defer tofn unwrap
andfn expect
respectively. This allows applications to trade off a potentially negligible increase in code size for useful panic messages. Some measurements from our production application:debug_assertions = false
debug_assertions = true
debug_assertions = false, features = ["std-unwrap"]