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

Question: Does it support compiling to wasm? #833

Open
axetroy opened this issue Apr 29, 2023 · 4 comments
Open

Question: Does it support compiling to wasm? #833

axetroy opened this issue Apr 29, 2023 · 4 comments

Comments

@axetroy
Copy link

axetroy commented Apr 29, 2023

I'm starting a similar project, parsing dumps on the web side

Before I start I want to make sure that with this library, it is possible to compile to wasm, not WASI.

Thanks

@luser
Copy link
Collaborator

luser commented May 1, 2023

I'm not aware that anyone has tried it. (unless maybe @Gankra did and I forgot?) The vast majority of what these crates do is just parsing the input minidump from bytes and making some decisions based on the contents. The only things I think you are likely to need to deal with are:

  1. Leaving out the convenience method Minidump::from_path, which uses mmap. This should be easy.
  2. If you want unwinding + symbolication to work, tweaking or entirely reworking how HttpSymbolSupplier works, as it currently uses reqwest to fetch symbols from a symbol server over HTTP. (The simplest solution might be to shim it somehow to call out to the browser's fetch implementation.)

@axetroy
Copy link
Author

axetroy commented May 12, 2023

We can create a crate names minidump-wasm to do this.

This crate use wasm compatible crate and receive Blob or Uint8Array as params and output to json or text.

https://github.com/rustwasm/wasm-pack will make it works

@TrueBrain
Copy link
Contributor

I recently have been toying with analyzing a crashdump in a browser (via Rust -> wasm_bindgen). It took surprisingly little effort, and most things are in fact already done. There are a few things worth of note however.

The problem that took me the longest to figure out, is that minidump-processor has its default feature set to disasm_amd64. However, this doesn't compile for the WASM target, and honestly, I don't even want it to be used. But disabling a default feature in Rust is kinda annoying (you need to set default-features = false). Having this fixed, you can drag in breakpad-symbols, minidump, minidump-common, minidump-processor and minidump-unwind, all without features selected, just fine (note: do not select the http feature, it will not build).

The next ofc is fetching symbols. Without saying, the file, cache, and http backends won't work. But writing your own was surprisingly easy. Just implement SymbolSupplier for your supplier, define a locate_symbols that runs a Javascript fetch on a symbol server that contains your symbols. There were two struggles here:

  1. The locate_symbols is an async with the Send trait, but wasm_bindgen doesn't have the Send trait (rightfully) on any of its objects. So it is tricky to await for the fetch result. I ended up creating a oneshot channel, and await on that. Code snipper below.
  2. For example Mozilla's Symbol Server doesn't set CORS headers. As such, you cannot use fetch from your browser to fetch the symbol files. What you end up doing is setting up a proxy that adds those CORS headers for what-ever symbol server you are using. The point of CORS is sometimes a bit lost on me :)

As for minidump itself, it just worked. I will leave below some snippets of my tool, in case anyone wants to try it out themselves. It is rough code, but you get the idea.

// Given a Vec<u8> named "array" which contains the minidump

let supplier = YourSymbolSupplier::new();
let symbolizer = Symbolizer::new(supplier);

let dump = minidump::Minidump::read(&array[..])?;
let state = minidump_processor::process_minidump(&dump, &symbolizer).await?;

// You can now use the state object to do what-ever; for example, "state.print_json(...)".

As for the symbol supplier, I went with something like:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = window)]
    pub fn fetch_symbol_file(s: &str) -> js_sys::Promise;
}

#[async_trait]
impl SymbolSupplier for YourSymbolSupplier {
    async fn locate_symbols(
        &self,
        module: &(dyn Module + Sync),
    ) -> Result<SymbolFile, SymbolError> {
        if let Some(lookup) = lookup(module, FileKind::BreakpadSym) {
            let (tx, rx) = oneshot::channel::<Vec<u8>>();

            {
                let url = format!("https://<your symbol server here>/{}", lookup.server_rel);
                let fetch = fetch_symbol_file(&url);

                let callback = Closure::<dyn FnMut(JsValue)>::new({
                    let mut tx = Some(tx);

                    move |result: JsValue| {
                        let array = js_sys::Uint8Array::new(&result);
                        let array = array.to_vec();

                        tx.take().expect("multiple fires on same channel").send(array).expect("Failed to send result");
                    }
                });

                let _ = fetch.then(&callback);
                callback.forget();
            }

            let result = rx.await.expect("Failed to receive result");
            if result.is_empty() {
                return Err(SymbolError::NotFound);
            }

            return SymbolFile::from_bytes(&result[..]);
        }

        Err(SymbolError::NotFound)
    }

    async fn locate_file(
        &self,
        _module: &(dyn Module + Sync),
        _file_kind: FileKind,
    ) -> Result<PathBuf, FileError> {
        Err(FileError::NotFound)
    }
}

And the javascript that belongs with it:

async function fetch_symbol_file(url) {
    try {
        const response = await fetch(url);
        if (response.status !== 200) {
            return "";
        }

        return await response.arrayBuffer();
    } catch (e) {
        return "";
    }
}
window.fetch_symbol_file = fetch_symbol_file;

@Gankra
Copy link
Collaborator

Gankra commented Sep 18, 2023

The async refactor of the codebase I did ~2 years ago was ostensibly gesturing towards enabling wasm support, cool to hear that it seems to have worked!

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

4 participants