diff --git a/justfile b/justfile index a2b42bdf..eab6cfba 100644 --- a/justfile +++ b/justfile @@ -87,6 +87,15 @@ release +args: (_run-tool "release" args) miri: (cd agb-hashmap && cargo miri test) +setup-cargo-wasm: + cargo install wasm-pack + +build-agb-wasm: + (cd agb-wasm && wasm-pack build --target web) + rm -rf website/agb/src/app/vendor/agb_wasm + mkdir website/agb/src/app/vendor + cp agb-wasm/pkg website/agb/src/app/vendor/agb_wasm -r + build-mgba-wasm: rm -rf website/agb/src/app/mgba/vendor mkdir website/agb/src/app/mgba/vendor @@ -94,13 +103,19 @@ build-mgba-wasm: build-combo-rom-site: just _build-rom "examples/combo" "AGBGAMES" - -build-site-app: build-mgba-wasm build-combo-rom-site mkdir -p website/agb/public gzip -9 -c examples/target/examples/combo.gba > website/agb/public/combo.gba.gz + + +setup-app-build: build-mgba-wasm build-combo-rom-site build-agb-wasm (cd website/agb && npm install --no-save --prefer-offline --no-audit) + +build-site-app: setup-app-build (cd website/agb && npm run build) +serve-site-dev: setup-app-build + (cd website/agb && npm run dev) + build-site: build-site-app build-book rm -rf website/build cp website/agb/out website/build -r diff --git a/website/agb/src/app/crash/backtrace.tsx b/website/agb/src/app/crash/backtrace.tsx index 28185f76..80430ec7 100644 --- a/website/agb/src/app/crash/backtrace.tsx +++ b/website/agb/src/app/crash/backtrace.tsx @@ -3,6 +3,9 @@ import { ContentBlock } from "../contentBlock"; import { useClientValue } from "../useClientValue.hook"; import { styled } from "styled-components"; +import { useEffect, useMemo, useState } from "react"; +import { useAgbDebug } from "../useAgbDebug.hook"; +import { AddressInfo } from "../vendor/agb_wasm/agb_wasm"; export function BacktracePage() { return ( @@ -38,10 +41,93 @@ export function BacktracePage() {
  • Configure the backtrace page to not point to a site at all
  • Not use the backtrace feature
  • + ); } +function Backtrace() { + const backtrace = useClientValue(getBacktrace) ?? ""; + const backtraceAddresses = useBacktraceData(backtrace); + + const [files, setFile] = useState([]); + const backtraceLocations = useBacktraceLocations( + backtraceAddresses ?? [], + files ?? [] + ); + + return ( +
    + Addresses in the backtrace + { + const files = evt.target.files; + const filesArr = (files && Array.from(files)) ?? []; + setFile(filesArr); + }} + /> +
      + {backtraceAddresses && + backtraceAddresses.map((x, idx) => ( +
    1. + 0x{x.toString(16).padStart(8, "0")} + +
    2. + ))} +
    +
    + ); +} + +function BacktraceAddressInfo({ info }: { info: AddressInfo[] | undefined }) { + if (!info) return; + + return ( +
      + {info.map((x, idx) => ( +
    1. + {x.is_inline && "(inlined into)"} {x.function_name}:{x.column}{" "} + {x.filename}:{x.line_number} +
    2. + ))} +
    + ); +} + +function useBacktraceLocations(addresses: number[], file: File[]) { + const debug = useAgbDebug(); + const [debugInfo, setDebugInfo] = useState([]); + + useEffect(() => { + const f = file[0]; + if (!f) return; + if (!debug) return; + (async () => { + const buf = await f.arrayBuffer(); + const view = new Uint8Array(buf); + + const agbDebugFile = debug.debug_file(view); + const debugInfo = addresses.map((x) => agbDebugFile.address_info(x)); + setDebugInfo(debugInfo); + })(); + }, [addresses, debug, file]); + + return debugInfo; +} + +function useBacktraceData(trace?: string) { + const debug = useAgbDebug(); + + return useMemo(() => { + try { + if (!trace) return; + const addresses = debug?.decode_backtrace(trace); + return addresses && Array.from(addresses); + } catch {} + }, [debug, trace]); +} + function BacktraceDisplay() { const backtrace = useClientValue(getBacktrace) ?? ""; diff --git a/website/agb/src/app/useAgbDebug.hook.ts b/website/agb/src/app/useAgbDebug.hook.ts new file mode 100644 index 00000000..6e6217e8 --- /dev/null +++ b/website/agb/src/app/useAgbDebug.hook.ts @@ -0,0 +1,35 @@ +import { useEffect, useState } from "react"; +import debugInit, { + decode_backtrace, + DebugFile, + InitOutput, +} from "./vendor/agb_wasm/agb_wasm"; + +let agbDebug: Promise | undefined; + +interface AgbDebug { + decode_backtrace: (backtrace: string) => Uint32Array; + debug_file: (file: Uint8Array) => DebugFile; +} + +export function useAgbDebug() { + const [debug, setDebug] = useState(); + + useEffect(() => { + (async () => { + if (agbDebug === undefined) { + agbDebug = debugInit(); + } + + await agbDebug; + console.log("Loaded agb debug"); + + setDebug({ + decode_backtrace, + debug_file: (file: Uint8Array) => new DebugFile(file), + }); + })(); + }, []); + + return debug; +}