make backtrace view fancy

This commit is contained in:
Corwin 2024-04-18 19:10:53 +01:00
parent 55394640b0
commit 0d4005f163
No known key found for this signature in database

View file

@ -46,52 +46,121 @@ export function BacktracePage() {
); );
} }
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() { function Backtrace() {
const backtrace = useClientValue(getBacktrace) ?? ""; const backtrace = useClientValue(getBacktrace) ?? "";
const backtraceAddresses = useBacktraceData(backtrace); const backtraceAddresses = useBacktraceData(backtrace);
const [files, setFile] = useState<File[]>([]); const [files, setFile] = useState<File[]>([]);
const backtraceAddressesList =
typeof backtraceAddresses === "object" ? backtraceAddresses : [];
const backtraceError =
typeof backtraceAddresses === "string" ? backtraceAddresses : undefined;
const backtraceLocations = useBacktraceLocations( const backtraceLocations = useBacktraceLocations(
backtraceAddresses ?? [], backtraceAddressesList,
files ?? [] files ?? []
); );
return ( return (
<details> <details>
<summary>Addresses in the backtrace</summary> <summary>Addresses in the backtrace</summary>
<input <label>
type="file" Elf file or GBA file with debug information:
onChange={(evt) => { <input
const files = evt.target.files; type="file"
const filesArr = (files && Array.from(files)) ?? []; onChange={(evt) => {
setFile(filesArr); const files = evt.target.files;
}} const filesArr = (files && Array.from(files)) ?? [];
/> setFile(filesArr);
<ol> }}
{backtraceAddresses && />
backtraceAddresses.map((x, idx) => ( </label>
<BacktraceListWrapper>
<BacktraceList>
{backtraceError}
{backtraceAddressesList.map((x, idx) => (
<li key={x}> <li key={x}>
<code>0x{x.toString(16).padStart(8, "0")}</code> {backtraceLocations[idx] ? (
<BacktraceAddressInfo info={backtraceLocations[idx]} /> <BacktraceAddressInfo info={backtraceLocations[idx]} />
) : (
<code>0x{x.toString(16).padStart(8, "0")}</code>
)}
</li> </li>
))} ))}
</ol> </BacktraceList>
</BacktraceListWrapper>
</details> </details>
); );
} }
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
? "<crate>"
: 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 }) { function BacktraceAddressInfo({ info }: { info: AddressInfo[] | undefined }) {
if (!info) return; if (!info) return;
function FunctionName({
interesting,
functionName,
}: {
interesting: boolean;
functionName: string;
}) {
if (interesting) {
return <strong>{functionName}</strong>;
}
return functionName;
}
return ( return (
<ol> <BacktraceAddressLine>
{info.map((x, idx) => ( {info.map((x, idx) => (
<li key={idx}> <li key={idx}>
{x.is_inline && "(inlined into)"} {x.function_name}:{x.column}{" "} <code>
{x.filename}:{x.line_number} {x.is_inline && "(inlined into)"}{" "}
<FunctionName
interesting={x.is_interesting}
functionName={x.function_name}
/>{" "}
<GreenSpan>
{makeNicePath(x.filename)}:{x.line_number}:{x.column}
</GreenSpan>
</code>
</li> </li>
))} ))}
</ol> </BacktraceAddressLine>
); );
} }
@ -100,17 +169,17 @@ function useBacktraceLocations(addresses: number[], file: File[]) {
const [debugInfo, setDebugInfo] = useState<AddressInfo[][]>([]); const [debugInfo, setDebugInfo] = useState<AddressInfo[][]>([]);
useEffect(() => { useEffect(() => {
const f = file[0];
if (!f) return;
if (!debug) return;
(async () => { (async () => {
const f = file[0];
if (!f) return;
if (!debug) return;
const buf = await f.arrayBuffer(); const buf = await f.arrayBuffer();
const view = new Uint8Array(buf); const view = new Uint8Array(buf);
const agbDebugFile = debug.debug_file(view); const agbDebugFile = debug.debug_file(view);
const debugInfo = addresses.map((x) => agbDebugFile.address_info(x)); const debugInfo = addresses.map((x) => agbDebugFile.address_info(x));
setDebugInfo(debugInfo); return debugInfo;
})(); })().then((x) => setDebugInfo(x ?? []));
}, [addresses, debug, file]); }, [addresses, debug, file]);
return debugInfo; return debugInfo;
@ -124,7 +193,9 @@ function useBacktraceData(trace?: string) {
if (!trace) return; if (!trace) return;
const addresses = debug?.decode_backtrace(trace); const addresses = debug?.decode_backtrace(trace);
return addresses && Array.from(addresses); return addresses && Array.from(addresses);
} catch {} } catch (e: unknown) {
return `${e}`;
}
}, [debug, trace]); }, [debug, trace]);
} }