From 04d368edf0f2aedf62101a257d6e48e4f0791026 Mon Sep 17 00:00:00 2001
From: Corwin
Date: Fri, 19 Apr 2024 21:49:16 +0100
Subject: [PATCH] move out backtrace decoding
---
website/agb/src/app/crash/backtrace.tsx | 170 ++--------------------
website/agb/src/app/crash/debug.tsx | 184 ++++++++++++++++++++++++
website/agb/src/app/crash/page.tsx | 1 -
website/agb/src/app/useAgbDebug.hook.ts | 9 +-
4 files changed, 199 insertions(+), 165 deletions(-)
create mode 100644 website/agb/src/app/crash/debug.tsx
diff --git a/website/agb/src/app/crash/backtrace.tsx b/website/agb/src/app/crash/backtrace.tsx
index 2178673b..4c393688 100644
--- a/website/agb/src/app/crash/backtrace.tsx
+++ b/website/agb/src/app/crash/backtrace.tsx
@@ -3,11 +3,11 @@
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";
+import { Debug } from "./debug";
export function BacktracePage() {
+ const backtrace = useClientValue(getBacktrace);
+
return (
agbrs crash backtrace viewer
@@ -22,7 +22,9 @@ export function BacktracePage() {
time.{" "}
Send these to the creator of the game you are playing.
-
+ {(backtrace && ) || (
+ No backtrace data in Url
+ )}
The owners of this website are not necessarily the creators of the
@@ -41,167 +43,13 @@ export function BacktracePage() {
additional debug files it can present you with the corresponding
function names, file names, line numbers, and column numbers.
-
+ Backtrace
+ {backtrace && }
);
}
-const BacktraceListWrapper = styled.div`
- font-size: 1rem;
- position: relative;
- width: calc(100vw - 20px);
- margin-left: calc(-1 * (100vw - 20px) / 2);
- left: 50%;
-`;
-
-const BacktraceList = styled.ol`
- overflow-x: scroll;
- white-space: nowrap;
-`;
-
-function Backtrace() {
- const backtrace = useClientValue(getBacktrace) ?? "";
- const backtraceAddresses = useBacktraceData(backtrace);
-
- const [files, setFile] = useState([]);
- const backtraceAddressesList =
- typeof backtraceAddresses === "object" ? backtraceAddresses : [];
- const backtraceError =
- typeof backtraceAddresses === "string" ? backtraceAddresses : undefined;
-
- const backtraceLocations = useBacktraceLocations(
- backtraceAddressesList,
- files ?? []
- );
-
- return (
-
- Addresses in the backtrace
-
- Elf file or GBA file with debug information:
- {
- const files = evt.target.files;
- const filesArr = (files && Array.from(files)) ?? [];
- setFile(filesArr);
- }}
- />
-
-
-
- {backtraceError}
- {backtraceAddressesList.map((x, idx) => (
-
- {backtraceLocations[idx] ? (
-
- ) : (
- 0x{x.toString(16).padStart(8, "0")}
- )}
-
- ))}
-
-
-
- );
-}
-
-function makeNicePath(path: string) {
- const srcIndex = path.lastIndexOf("/src/");
- if (srcIndex < 0) return path;
-
- const crateNameStartIndex = path.slice(0, srcIndex).lastIndexOf("/");
- const crateName =
- crateNameStartIndex < 0
- ? ""
- : path.slice(crateNameStartIndex + 1, srcIndex);
-
- return `<${crateName}>/${path.slice(srcIndex + 5)}`;
-}
-
-const GreenSpan = styled.span`
- color: green;
-`;
-
-const BacktraceAddressLine = styled.ul`
- list-style-type: none;
- padding-left: 20px;
-`;
-
-function BacktraceAddressInfo({ info }: { info: AddressInfo[] | undefined }) {
- if (!info) return;
-
- function FunctionName({
- interesting,
- functionName,
- }: {
- interesting: boolean;
- functionName: string;
- }) {
- if (interesting) {
- return {functionName} ;
- }
- return functionName;
- }
-
- return (
-
- {info.map((x, idx) => (
-
-
- {x.is_inline && "(inlined into)"}{" "}
- {" "}
-
- {makeNicePath(x.filename)}:{x.line_number}:{x.column}
-
-
-
- ))}
-
- );
-}
-
-function useBacktraceLocations(addresses: number[], file: File[]) {
- const debug = useAgbDebug();
- const [debugInfo, setDebugInfo] = useState([]);
-
- useEffect(() => {
- (async () => {
- const f = file[0];
- if (!f) return;
- if (!debug) return;
- 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));
- return debugInfo;
- })().then((x) => setDebugInfo(x ?? []));
- }, [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 (e: unknown) {
- return `${e}`;
- }
- }, [debug, trace]);
-}
-
-function BacktraceDisplay() {
- const backtrace = useClientValue(getBacktrace) ?? "";
-
+function BacktraceCopyDisplay({ backtrace }: { backtrace: string }) {
return (
{backtrace}
diff --git a/website/agb/src/app/crash/debug.tsx b/website/agb/src/app/crash/debug.tsx
new file mode 100644
index 00000000..a403f431
--- /dev/null
+++ b/website/agb/src/app/crash/debug.tsx
@@ -0,0 +1,184 @@
+import { styled } from "styled-components";
+import { AddressInfo, AgbDebug, useAgbDebug } from "../useAgbDebug.hook";
+import { useMemo, useState } from "react";
+
+const BacktraceListWrapper = styled.div`
+ font-size: 1rem;
+ position: relative;
+ width: calc(100vw - 20px);
+ margin-left: calc(-1 * (100vw - 20px) / 2);
+ left: 50%;
+`;
+
+const BacktraceList = styled.ol`
+ overflow-x: scroll;
+ white-space: nowrap;
+`;
+
+interface DebugProps {
+ encodedBacktrace: string;
+}
+
+export function Debug(props: DebugProps) {
+ const debug = useAgbDebug();
+ if (debug) {
+ return ;
+ } else {
+ return Loading debug viewer...
;
+ }
+}
+
+interface DebugBacktraceDecodeProps extends DebugProps {
+ debug: AgbDebug;
+}
+
+const NonWrapCode = styled.code`
+ white-space: nowrap;
+`;
+
+function DebugBacktraceDecode({
+ encodedBacktrace,
+ debug,
+}: DebugBacktraceDecodeProps) {
+ const backtraceAddresses = useBacktraceData(debug, encodedBacktrace);
+ const [backtraceLocations, setBacktraceLocations] = useState(
+ []
+ );
+
+ if (typeof backtraceAddresses === "string") {
+ return ;
+ }
+
+ return (
+ <>
+
+ If you add the elf file used to make the GBA file, or the GBA file
+ itself if it was made with agb-gbafix --debug
+ , you can see: function names, file names, line numbers, and column
+ numbers.
+
+
+ Elf file or GBA file with debug information:{" "}
+ {
+ const files = evt.target.files;
+ if (!files) return;
+ const file = files[0];
+ if (!file) return;
+ loadLocations(debug, backtraceAddresses, file).then((data) =>
+ setBacktraceLocations(data)
+ );
+ }}
+ />
+
+
+
+ {backtraceAddresses.map((x, idx) => (
+
+
+
+ ))}
+
+
+ >
+ );
+}
+
+function DebugError({ error }: { error: string }) {
+ return (
+ <>
+ Something went wrong decoding the backtrace
+ {error}
+ >
+ );
+}
+
+function makeNicePath(path: string) {
+ const srcIndex = path.lastIndexOf("/src/");
+ if (srcIndex < 0) return path;
+
+ const crateNameStartIndex = path.slice(0, srcIndex).lastIndexOf("/");
+ const crateName =
+ crateNameStartIndex < 0
+ ? ""
+ : path.slice(crateNameStartIndex + 1, srcIndex);
+
+ return `<${crateName}>/${path.slice(srcIndex + 5)}`;
+}
+
+const GreenSpan = styled.span`
+ color: green;
+`;
+
+const BacktraceAddressLine = styled.ul`
+ list-style-type: none;
+ padding-left: 20px;
+`;
+
+function BacktraceAddressInfo({
+ address,
+ info,
+}: {
+ address: number;
+ info: AddressInfo[] | undefined;
+}) {
+ if (!info) {
+ return 0x{address.toString(16).padStart(8, "0")}
;
+ }
+
+ function FunctionName({
+ interesting,
+ functionName,
+ }: {
+ interesting: boolean;
+ functionName: string;
+ }) {
+ if (interesting) {
+ return {functionName} ;
+ }
+ return functionName;
+ }
+
+ return (
+
+ {info.map((x, idx) => (
+
+
+ {x.is_inline && "(inlined into)"}{" "}
+ {" "}
+
+ {makeNicePath(x.filename)}:{x.line_number}:{x.column}
+
+
+
+ ))}
+
+ );
+}
+
+async function loadLocations(debug: AgbDebug, addresses: number[], file: File) {
+ const buf = await file.arrayBuffer();
+ const view = new Uint8Array(buf);
+
+ const agbDebugFile = debug.debug_file(view);
+ const debugInfo = addresses.map((x) => agbDebugFile.address_info(x));
+ return debugInfo;
+}
+
+function useBacktraceData(debug: AgbDebug, trace: string) {
+ return useMemo(() => {
+ try {
+ const addresses = debug?.decode_backtrace(trace);
+ return addresses && Array.from(addresses);
+ } catch (e: unknown) {
+ return `${e}`;
+ }
+ }, [debug, trace]);
+}
diff --git a/website/agb/src/app/crash/page.tsx b/website/agb/src/app/crash/page.tsx
index 05283ac3..e9d4c70d 100644
--- a/website/agb/src/app/crash/page.tsx
+++ b/website/agb/src/app/crash/page.tsx
@@ -1,6 +1,5 @@
import { Metadata } from "next";
import { BacktracePage } from "./backtrace";
-import { ContentBlock } from "../contentBlock";
export const metadata: Metadata = {
title: "agbrs crash backtrace",
diff --git a/website/agb/src/app/useAgbDebug.hook.ts b/website/agb/src/app/useAgbDebug.hook.ts
index 3a8eba1a..4ed74e4e 100644
--- a/website/agb/src/app/useAgbDebug.hook.ts
+++ b/website/agb/src/app/useAgbDebug.hook.ts
@@ -3,16 +3,19 @@ import debugInit, {
decode_backtrace,
DebugFile,
InitOutput,
-} from "./vendor/agb_wasm/agb_wasm";
+ AddressInfo,
+} from "./vendor/backtrace/backtrace";
let agbDebug: Promise | undefined;
-interface AgbDebug {
+export { AddressInfo };
+
+export interface AgbDebug {
decode_backtrace: (backtrace: string) => Uint32Array;
debug_file: (file: Uint8Array) => DebugFile;
}
-export function useAgbDebug() {
+export function useAgbDebug(): AgbDebug | undefined {
const [debug, setDebug] = useState();
useEffect(() => {