add debug information to the crash page

This commit is contained in:
Corwin 2024-04-18 00:50:13 +01:00
parent 1d9a7d51a3
commit 121ebe312c
No known key found for this signature in database
3 changed files with 138 additions and 2 deletions

View file

@ -87,6 +87,15 @@ release +args: (_run-tool "release" args)
miri: miri:
(cd agb-hashmap && cargo miri test) (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: build-mgba-wasm:
rm -rf website/agb/src/app/mgba/vendor rm -rf website/agb/src/app/mgba/vendor
mkdir website/agb/src/app/mgba/vendor mkdir website/agb/src/app/mgba/vendor
@ -94,13 +103,19 @@ build-mgba-wasm:
build-combo-rom-site: build-combo-rom-site:
just _build-rom "examples/combo" "AGBGAMES" just _build-rom "examples/combo" "AGBGAMES"
build-site-app: build-mgba-wasm build-combo-rom-site
mkdir -p website/agb/public mkdir -p website/agb/public
gzip -9 -c examples/target/examples/combo.gba > website/agb/public/combo.gba.gz 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) (cd website/agb && npm install --no-save --prefer-offline --no-audit)
build-site-app: setup-app-build
(cd website/agb && npm run 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 build-site: build-site-app build-book
rm -rf website/build rm -rf website/build
cp website/agb/out website/build -r cp website/agb/out website/build -r

View file

@ -3,6 +3,9 @@
import { ContentBlock } from "../contentBlock"; import { ContentBlock } from "../contentBlock";
import { useClientValue } from "../useClientValue.hook"; import { useClientValue } from "../useClientValue.hook";
import { styled } from "styled-components"; 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() { export function BacktracePage() {
return ( return (
@ -38,10 +41,93 @@ export function BacktracePage() {
<li>Configure the backtrace page to not point to a site at all</li> <li>Configure the backtrace page to not point to a site at all</li>
<li>Not use the backtrace feature</li> <li>Not use the backtrace feature</li>
</ol> </ol>
<Backtrace />
</ContentBlock> </ContentBlock>
); );
} }
function Backtrace() {
const backtrace = useClientValue(getBacktrace) ?? "";
const backtraceAddresses = useBacktraceData(backtrace);
const [files, setFile] = useState<File[]>([]);
const backtraceLocations = useBacktraceLocations(
backtraceAddresses ?? [],
files ?? []
);
return (
<details>
<summary>Addresses in the backtrace</summary>
<input
type="file"
onChange={(evt) => {
const files = evt.target.files;
const filesArr = (files && Array.from(files)) ?? [];
setFile(filesArr);
}}
/>
<ol>
{backtraceAddresses &&
backtraceAddresses.map((x, idx) => (
<li key={x}>
<code>0x{x.toString(16).padStart(8, "0")}</code>
<BacktraceAddressInfo info={backtraceLocations[idx]} />
</li>
))}
</ol>
</details>
);
}
function BacktraceAddressInfo({ info }: { info: AddressInfo[] | undefined }) {
if (!info) return;
return (
<ol>
{info.map((x, idx) => (
<li key={idx}>
{x.is_inline && "(inlined into)"} {x.function_name}:{x.column}{" "}
{x.filename}:{x.line_number}
</li>
))}
</ol>
);
}
function useBacktraceLocations(addresses: number[], file: File[]) {
const debug = useAgbDebug();
const [debugInfo, setDebugInfo] = useState<AddressInfo[][]>([]);
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() { function BacktraceDisplay() {
const backtrace = useClientValue(getBacktrace) ?? ""; const backtrace = useClientValue(getBacktrace) ?? "";

View file

@ -0,0 +1,35 @@
import { useEffect, useState } from "react";
import debugInit, {
decode_backtrace,
DebugFile,
InitOutput,
} from "./vendor/agb_wasm/agb_wasm";
let agbDebug: Promise<InitOutput> | undefined;
interface AgbDebug {
decode_backtrace: (backtrace: string) => Uint32Array;
debug_file: (file: Uint8Array) => DebugFile;
}
export function useAgbDebug() {
const [debug, setDebug] = useState<AgbDebug>();
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;
}