Skip to content

[WIP] Readable exceptions StackTraces#937

Closed
andreaTP wants to merge 4 commits intodylibso:mainfrom
andreaTP:issue-829-readable-exceptions
Closed

[WIP] Readable exceptions StackTraces#937
andreaTP wants to merge 4 commits intodylibso:mainfrom
andreaTP:issue-829-readable-exceptions

Conversation

@andreaTP
Copy link
Copy Markdown
Collaborator

@andreaTP andreaTP commented Jun 5, 2025

Opening for visibility and to receive very much needed feedback.

The problem

Currently the StackTraces produced when we reach an unreachable statement are useless, the current output is masking all the useful information in the interpreter, and referring to generated functions in the compiler.

Wasmtime does a much better job, for example:

$ wasmtime ./wasm-corpus/src/main/resources/compiled/count_vowels.rs.wasm --invoke count_vowels 1 -1

...
    1: error while executing at wasm backtrace:
           0: 0x5c49 - count_vowels.wasm!std::panicking::rust_panic_with_hook::hb39abb160cd4038c
           1: 0x522e - count_vowels.wasm!std::panicking::begin_panic_handler::{{closure}}::h83b3d84f04c7372b
           2: 0x5168 - count_vowels.wasm!std::sys::backtrace::__rust_end_short_backtrace::h8eb99c908c86e40b
           3: 0x57d7 - count_vowels.wasm!rust_begin_unwind
           4: 0x711a - count_vowels.wasm!core::panicking::panic_nounwind_fmt::h7a87e102e925dda2
           5: 0x716e - count_vowels.wasm!core::panicking::panic_nounwind::hc189c31fedd6a605
           6:  0xccc - count_vowels.wasm!core::slice::raw::from_raw_parts::precondition_check::h1e15a43dea7fa54e
           7: 0x1c04 - count_vowels.wasm!core::slice::raw::from_raw_parts::h506ffe5f5bebefff
           8: 0x2d71 - count_vowels.wasm!count_vowels
...

With this PR the output of Chicory becomes:

com.dylibso.chicory.runtime.TrapException: Trapped on unreachable instruction in: com.dylibso.chicory.$gen.CompiledMachine
	at count_vowels.wasm.std::panicking::rust_panic_with_hook(Unknown Source)
	at count_vowels.wasm.std::panicking::begin_panic_handler::{{closure}}(Unknown Source)
	at count_vowels.wasm.std::sys::backtrace::__rust_end_short_backtrace(Unknown Source)
	at count_vowels.wasm.rust_begin_unwind(Unknown Source)
	at count_vowels.wasm.core::panicking::panic_nounwind_fmt(Unknown Source)
	at count_vowels.wasm.core::panicking::panic_nounwind(Unknown Source)
	at count_vowels.wasm.core::slice::raw::from_raw_parts::precondition_check(Unknown Source)
	at count_vowels.wasm.core::slice::raw::from_raw_parts(Unknown Source)
	at count_vowels.wasm.count_vowels(Unknown Source)

Open questions

  1. rustc-demangle: where should it live? in this repo, in a separate one(e.g. under roastedroot), or should we try to push the changes up to the rust library or rebundle it ourself and call it a day?
  2. how should we load the "name" custom section? Currently is all fully dynamic at runtime but there are several alternative options

@andreaTP andreaTP requested review from bhelx, chirino and evacchi June 5, 2025 13:59
@evacchi
Copy link
Copy Markdown
Collaborator

evacchi commented Jun 5, 2025

First of all, thanks because this indeed solves a very real problem.

On the other hand, this only solves the problem from a specific angle, and I wish we could try to address the shortcoming from a more generic perspective.

In other words, since this is useful for debugging, we could try to see if we could take a similar approach using gimli to parse the DWARF sections, this way we would get relevant stack traces for all languages.

for somewhat prior art, wazero introduced this a few years ago... using the built-in DWARF parser that comes with the Go stdlib, an equivalent for which unfortunately is not part of the JDK

regardless, we might be able to keep all these experiments in a separate module if we provide a sufficiently generic interface (i.e. maybe not just string -> string, maybe trace->trace? thinking aloud here...)

HTH

@chirino
Copy link
Copy Markdown
Collaborator

chirino commented Jun 5, 2025

In other words, since this is useful for debugging, we could try to see if we could take a similar approach using gimli to parse the DWARF sections, this way we would get relevant stack traces for all languages.

for somewhat prior art, wazero introduced this a few years ago... using the built-in DWARF parser that comes with the Go stdlib, an equivalent for which unfortunately is not part of the JDK

I started a branch at:

https://github.com/chirino/chicory/tree/source-maps

It compiles the rust DWARF parser to WASM and just use it as byte code compiled chicory module. Should I resurrect that branch? Maybe some parts of it can be use by this one.

@andreaTP andreaTP force-pushed the issue-829-readable-exceptions branch from 8d068ce to 7c41613 Compare June 5, 2025 15:25
@andreaTP andreaTP marked this pull request as draft June 5, 2025 15:33
@andreaTP
Copy link
Copy Markdown
Collaborator Author

andreaTP commented Jun 5, 2025

Should I resurrect that branch?

Always welcome @chirino we need those experiments to keep going!

regardless, we might be able to keep all these experiments in a separate module if we provide a sufficiently generic interface (i.e. maybe not just string -> string, maybe trace->trace? thinking aloud here...)

Looking for ideas here, interpreter and compiler are getting the function id in very different ways, and the interpreter is accessing a lot of private state to do so 😓 but it would be a perfect approach if applicable!

@bhelx
Copy link
Copy Markdown
Contributor

bhelx commented Jun 5, 2025

I think the current structure is fine in the short term as it's an improvement on the user experience. Long term, It's a little weird to have guest language specific debugging code in this project. There could perhaps be some separate debugging submodule that handles DWARF in a generic way like @evacchi suggests. But i don't think it's required to do all that to get the change out.

andreaTP added a commit that referenced this pull request Jun 5, 2025
Cargo project names are incorrect, we haven't noticed because we never
looked at those 😅

Noticed in #937 , it reduces the diff.
@andreaTP andreaTP force-pushed the issue-829-readable-exceptions branch from 57786bc to 0ea8982 Compare June 5, 2025 17:33
Copy link
Copy Markdown
Collaborator Author

@andreaTP andreaTP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done an iteration attempting to extract a "generic enough" API, I'm keeping rustc-demangle at the moment as it is a demonstration of the design with the interpreter and compiler implementations.

When we are convinced that the design is good enough I'll remove rustc-demangle as it can live in another repository.

private final ExecutionListener listener;
private final Exports fluentExports;

private final Optional<BiFunction<Instance, Integer, List<StackTraceElement>>>
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted all the callback logic into this callback, we can move to a class or interface if preferable.

var instance =
Instance.builder(CountVowels.load())
.withMachineFactory(CountVowels::create)
.withExceptionConverter(demanglerExceptionConverter(debugModule))
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the new design shines, we can inject the debugging logic using a different WasmModule.

@andreaTP
Copy link
Copy Markdown
Collaborator Author

Closing in favor of #943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants