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) => (
+ -
+
0x{x.toString(16).padStart(8, "0")}
+
+
+ ))}
+
+
+ );
+}
+
+function BacktraceAddressInfo({ info }: { info: AddressInfo[] | undefined }) {
+ if (!info) return;
+
+ return (
+
+ {info.map((x, idx) => (
+ -
+ {x.is_inline && "(inlined into)"} {x.function_name}:{x.column}{" "}
+ {x.filename}:{x.line_number}
+
+ ))}
+
+ );
+}
+
+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;
+}