Skip to content
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

wasm2js aborts when allocating pages. #7126

Open
RmStorm opened this issue Nov 28, 2024 · 6 comments
Open

wasm2js aborts when allocating pages. #7126

RmStorm opened this issue Nov 28, 2024 · 6 comments

Comments

@RmStorm
Copy link

RmStorm commented Nov 28, 2024

I'm running into some pretty serious issues using wasm2js. It looks to me like the entire wasm memory is getting corrupted. Sometimes they result in clean-ish aborts like this:

➜ node index.js 
allocating pages: 4
allocating pages: 4
allocating pages: 7
first done
allocating pages: 13
Error calling wasm function: Error: abort
    at wasm2js_trap (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/pkg/wasm2js_memory_problem_bg.wasm.js:25:33)
    at __rust_start_panic (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/pkg/wasm2js_memory_problem_bg.wasm.js:4319:3)
    at rust_panic (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/pkg/wasm2js_memory_problem_bg.wasm.js:4218:3)
    at std__panicking__rust_panic_with_hook__he5c089ac7305193e

But sometimes they result in just complete corruption and I get output like:
I%@�<#�f�[email protected]�n~I%@��r�f�M@q��$~I%@�><�f�M@�a�}

I've made a repo with a minimal reproducible example. The wasm file that I'm using is generated using wasm-bindgen. I have tested with versions 0.2.90, 0.2.91 and 0.2.92 I have also tested with wasm2js version 105 (the one I originally had installed) and the latest version (119). It occurs in all situations!

@RmStorm
Copy link
Author

RmStorm commented Nov 28, 2024

It looks similar to: #6628 but since he reports downgrading resolved his issue I suspect mine is distinct.

@kripken
Copy link
Member

kripken commented Nov 28, 2024

The minimal testcase here looks like it should have worked in the past - I mean that it is so minimal, that given things used to work in general, it must have.

Given that, what I would do is bisect. You should be able to bisect separately over wasm-bindgen and wasm-opt, and only one should be needed (but you might need to try both if the first finds nothing). I see you mentioned you tried wasm-opt 105, which is from 2 years ago, so I would go even further back, until you find where it used to work. Both going back and bisecting should take logarithmic time, so it should be practical.

@mtb0x1
Copy link
Contributor

mtb0x1 commented Nov 28, 2024

I changed the default allocator by wee_alloc and interestingly enough no issue (or at least no crash trace).

in lib.rs :

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

attached both Wasm files(in case you want to compare)

@RmStorm
Copy link
Author

RmStorm commented Nov 29, 2024

@mtb0x1 Thanks! That indeed seems to fix it! Do you know why this might be the case?

Edit:
no it didn't actual fix the issue I think. It just avoids the trap sometimes, sometimes it seem to run.. sometimes it crashes.. I changed index.js to:

async function run() {
  try {
    console.log(greet("no dice"))
    for (let i = 0; i < 10; i++) {
      const inputStr = "A".repeat(i+1).repeat(20000)
      console.log("iteration: ", i, "input length: ", inputStr.length)
      console.log("output length", greet(inputStr).length)
    }
  } catch (err) {
    console.error("Error calling wasm function:", err);
  }
}

And then the output is: ( I removed the page allocation log lines)

❯ node index.js 
Hello, no dice!
iteration:  0 input length:  10000
output length 10008
iteration:  1 input length:  20000
output length 10008
iteration:  2 input length:  30000
output length 10008
iteration:  3 input length:  40000
output length 10008
iteration:  4 input length:  50000
output length 10008
iteration:  5 input length:  60000
output length 10008
iteration:  6 input length:  70000
output length 10008
iteration:  7 input length:  80000
output length 10008
iteration:  8 input length:  90000
output length 10008
iteration:  9 input length:  100000
output length 10008

It's very clear that the output doesn't actually change so something is very wrong. If I make the input string twice as long like so const inputStr = "A".repeat(i+1).repeat(20000) I get:

❯ node index.js 
Hello, no dice!
iteration:  0 input length:  20000
Error calling wasm function: TypeError: The encoded data was not valid for encoding utf-8
    at TextDecoder.decode (node:internal/encoding:443:16)
    at getStringFromWasm0 (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/pkg/wasm2js_memory_problem_bg.js:90:30)
    at greet (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/pkg/wasm2js_memory_problem_bg.js:108:16)
    at run (file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/index.js:12:36)
    at file:///home/rmstorm/Documents/rust/wasm2js-memory-problem/index.js:19:1
    at ModuleJob.run (node:internal/modules/esm/module_job:268:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:543:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:116:5) {
  code: 'ERR_ENCODING_INVALID_ENCODED_DATA'
}

Which is just nonsense indicating memory corruption I think..

@mtb0x1
Copy link
Contributor

mtb0x1 commented Dec 4, 2024

sounds like memory limit/growth issue, if you test with smaller values it works.
I am not sure what was the design of wasm2js when it comes to memory.

as advised by kripken, can you bisect this would helps us pinpoint the issue.

@kripken
Copy link
Member

kripken commented Dec 4, 2024

If bisection doesn't work, another option is to debug this in depth using Binaryen's instrumentation, comparing wasm and wasm2js builds. Specifically, we can instrument the builds so that every single read and write to memory, locals, etc. are logged out. Assuming everything is deterministic, and that the wasm build works while wasm2js errors, then comparing the logs will find the first divergence, and pinpoint the bug.

To do this, the process is something like

  • Instrument the wasm using wasm-opt input.wasm -o output.wasm --instrument-locals --log-execution --instrument-memory (you may not need all three).
  • Add the new imports to the JS loading code, which you can get from here
  • Run the wasm, save the log.
  • Run wasm2js, then run that build and save the log.
  • Diff the logs.

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

No branches or pull requests

3 participants