refactor(verify): give the failure-retaining Ghidra lanes' workspaces to VerifierFixture (#11097)

The m68k, MIPS, MIPS16 shared, MIPS relocatable, PowerPC, RISC-V and DOL lanes kept their mkdtemp workspace for diagnosis whenever verification or cleanup failed, and removed it with rm after success. VerifierFixture now allocates the workspace (a plain root: Ghidra children use it as TMPDIR). Every failure branch that names the retained workspace records that reason with fixture.retain, and success releases the fixture. The verifier-fixture-root baseline shrinks by 7.
This commit is contained in:
morluto authored and GitHub committed 2026-11-15 20:32:59 +08:00
1 parent 5b5ca68bb3
commit 0b95e55655
8 files changed
+139 -77

No files matched your search

+15 -5
View File
@@ -1,8 +1,7 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { readFile, mkdtemp, mkdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { readFile, mkdir } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -34,6 +33,7 @@ import {
assertVerifierCleanupComplete,
} from "./lib/verifier-run.mjs";
import { withVerifierCleanup } from "./lib/verifier-cleanup.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
// Optional cross-target lane. No tool acquisition and no target execution.
if (process.argv.length !== 2)
@@ -59,7 +59,17 @@ if (installation.status !== "available")
const execute = promisify(execFile);
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-dol-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-dol-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const baseEnv = {
@@ -309,11 +319,11 @@ try {
},
]);
} catch (cause) {
throw new Error(
throw retainWorkspace(
`GameCube DOL verification failed; retained workspace: ${workspace}`,
{ cause },
);
}
assert.ok(completed, "Verifier completion was not observed");
await rm(workspace, { recursive: true, force: true });
await workspaceFixture.release();
console.log(JSON.stringify({ ...completed, fixtures: reports }, null, 2));
+30 -18
View File
@@ -1,14 +1,7 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import {
mkdir,
mkdtemp,
readFile,
readdir,
rm,
writeFile,
} from "node:fs/promises";
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -21,6 +14,7 @@ import {
requireMcpOutput,
} from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
if (process.argv.length !== 2)
throw new Error("Usage: node scripts/verify-real-ghidra-m68k.mjs");
@@ -57,7 +51,18 @@ assert.equal(
"7679358409efee4c5c2465f9a47158c4d49e0415ba12b3170898440db7ea0bc9",
);
await mkdir(join(root, "build"), { recursive: true });
const workspace = await mkdtemp(join(root, "build", "rea-m68k-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-m68k-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
parent: join(root, "build"),
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const path = join(workspace, "image.bin");
@@ -257,6 +262,9 @@ try {
};
} catch (cause) {
if (stderr) process.stderr.write(stderr);
workspaceFixture.retain(
`m68k verification failed; retained workspace: ${workspace}`,
);
throw cause;
} finally {
try {
@@ -265,16 +273,20 @@ try {
await transport.close();
}
}
assert.deepEqual(
await readdir(runtime),
[],
`Runtime resources survived server shutdown; retained workspace: ${workspace}`,
);
const survivors = await readdir(runtime);
if (survivors.length > 0)
throw retainWorkspace(
`Runtime resources survived server shutdown (${survivors.join(", ")}); retained workspace: ${workspace}`,
);
const verifierRun = await completeVerifierRun(run);
assert.equal(verifierRun.process_lineage.status, "verified");
assert.deepEqual(verifierRun.process_lineage.descendants, []);
// Keep the workspace for diagnosis unless verification and cleanup both pass.
await rm(workspace, { recursive: true, force: true });
if (
verifierRun.process_lineage.status !== "verified" ||
verifierRun.process_lineage.descendants.length !== 0
)
throw retainWorkspace(
`Cleanup unverified; retained diagnostic workspace: ${workspace}`,
);
await workspaceFixture.release();
process.stdout.write(
JSON.stringify({ verifier_run: verifierRun, ...report }) + "\n",
);
@@ -2,8 +2,7 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, mkdir, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { mkdir, readFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -21,6 +20,7 @@ import {
} from "../tests/conformance/ghidra/mips-relocatable-checks.mjs";
import { requireMcpOperationResult } from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
// Optional cross-target lane for ET_REL objects. No tool acquisition, no
// linking and no target execution.
@@ -48,7 +48,17 @@ if (installation.status !== "available")
const execute = promisify(execFile);
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-mips-rel-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-mips-rel-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const env = {
@@ -320,14 +330,14 @@ if (
completed.process_lineage.status !== "verified" ||
completed.process_lineage.descendants.length !== 0
)
throw new Error(
throw retainWorkspace(
`Cleanup unverified; retained diagnostic workspace: ${workspace}`,
{ cause: primaryFailure },
);
if (primaryFailure !== undefined)
throw new Error(
throw retainWorkspace(
`MIPS relocatable verification failed; retained workspace: ${workspace}`,
{ cause: primaryFailure },
);
await rm(workspace, { recursive: true, force: true });
await workspaceFixture.release();
console.log(JSON.stringify({ ...completed, fixtures: reports }, null, 2));
+21 -8
View File
@@ -1,8 +1,7 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { promisify } from "node:util";
import { Client } from "@modelcontextprotocol/client";
@@ -16,6 +15,7 @@ import {
requireMcpToolError,
} from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
if (
process.env.GHIDRA_INSTALL_DIR === undefined ||
@@ -26,7 +26,17 @@ if (
);
const execute = promisify(execFile);
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-mips-shared-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-mips-shared-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const env = {
@@ -328,13 +338,16 @@ if (
completed.process_lineage.status !== "verified" ||
completed.process_lineage.descendants.length !== 0
)
throw new Error(`Cleanup unverified; retained workspace: ${workspace}`, {
cause: failure,
});
throw retainWorkspace(
`Cleanup unverified; retained workspace: ${workspace}`,
{
cause: failure,
},
);
if (failure !== undefined)
throw new Error(
throw retainWorkspace(
`MIPS16 shared verification failed; retained workspace: ${workspace}`,
{ cause: failure },
);
await rm(workspace, { recursive: true, force: true });
await workspaceFixture.release();
console.log(JSON.stringify({ ...completed, fixtures: reports }, null, 2));
+15 -7
View File
@@ -3,15 +3,12 @@ import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import {
mkdtemp,
mkdir,
readFile,
readdir,
realpath,
rm,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -48,6 +45,7 @@ import {
} from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { runOwnedCommand } from "../dist/process/OwnedCommand.js";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
// Optional cross-target lane. No tool acquisition and no target execution.
const mode = process.argv[2];
@@ -78,7 +76,17 @@ if (installation.status !== "available")
const execute = promisify(execFile);
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-mips-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-mips-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const env = {
@@ -892,16 +900,16 @@ if (
completed.process_lineage.status !== "verified" ||
completed.process_lineage.descendants.length !== 0
)
throw new Error(
throw retainWorkspace(
`Cleanup unverified; retained diagnostic workspace: ${workspace}`,
{ cause: primaryFailure },
);
if (primaryFailure !== undefined)
throw new Error(
throw retainWorkspace(
`MIPS verification failed; retained workspace: ${workspace}`,
{
cause: primaryFailure,
},
);
await rm(workspace, { recursive: true, force: true });
await workspaceFixture.release();
console.log(JSON.stringify({ ...completed, fixtures: reports }, null, 2));
+16 -6
View File
@@ -2,8 +2,7 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, mkdir, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { mkdir, readFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -23,6 +22,7 @@ import { inspectVerifierGhidraInstallation } from "./lib/ghidra-installation-ins
import { buildPowerPcFixture } from "../tests/conformance/ghidra/ppc-fixture.mjs";
import { requireMcpOperationResult } from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
// Optional cross-target lane. No tool acquisition and no target execution.
if (process.argv.length !== 2)
@@ -47,7 +47,17 @@ if (installation.status !== "available")
const execute = promisify(execFile);
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-ppc-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-ppc-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const env = {
@@ -303,16 +313,16 @@ if (
completed.process_lineage.status !== "verified" ||
completed.process_lineage.descendants.length !== 0
)
throw new Error(
throw retainWorkspace(
`Cleanup unverified; retained diagnostic workspace: ${workspace}`,
{ cause: primaryFailure },
);
if (primaryFailure !== undefined)
throw new Error(
throw retainWorkspace(
`PowerPC verification failed; retained workspace: ${workspace}`,
{
cause: primaryFailure,
},
);
await rm(workspace, { recursive: true, force: true });
await workspaceFixture.release();
console.log(JSON.stringify({ ...completed, fixtures: reports }, null, 2));
+26 -20
View File
@@ -3,15 +3,7 @@ import { schemaVerifierEnvironment } from "./lib/mcp-verification-environment.mj
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import {
mkdtemp,
mkdir,
readFile,
readdir,
rm,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
@@ -42,6 +34,7 @@ import {
requireMcpToolError,
} from "./lib/mcp-verifier-results.mjs";
import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs";
import { VerifierFixture } from "./lib/verifier-fixture.mjs";
if (process.argv.length !== 2)
throw new Error("Usage: node scripts/verify-real-ghidra-riscv.mjs");
@@ -67,7 +60,17 @@ assert.equal(installation.providerVersion, "12.1.4");
const execute = promisify(execFile);
const entrypoint = fileURLToPath(new URL("./rea.mjs", import.meta.url));
const run = createVerifierRun();
const workspace = await mkdtemp(join(tmpdir(), "rea-riscv-proof-"));
const workspaceFixture = await VerifierFixture.open({
prefix: "rea-riscv-proof-",
// Ghidra children use the workspace as TMPDIR; a plain root admits them on every host.
private: false,
});
const workspace = workspaceFixture.path;
// A failed run keeps its workspace for diagnosis; the fixture records why.
const retainWorkspace = (message, options) => {
workspaceFixture.retain(message);
return new Error(message, options);
};
const runtime = join(workspace, "runtime");
await mkdir(runtime);
const env = {
@@ -563,20 +566,23 @@ if (
completed.process_lineage.status !== "verified" ||
completed.process_lineage.descendants.length !== 0
)
throw new Error(`Cleanup unverified; retained workspace: ${workspace}`, {
cause: failure,
});
throw retainWorkspace(
`Cleanup unverified; retained workspace: ${workspace}`,
{
cause: failure,
},
);
if (failure !== undefined)
throw new Error(
throw retainWorkspace(
`RISC-V verification failed; retained workspace: ${workspace}; MCP stderr: ${stderr}`,
{ cause: failure },
);
assert.deepEqual(
await readdir(runtime),
[],
`Runtime resources survived server shutdown; retained workspace: ${workspace}`,
);
await rm(workspace, { recursive: true, force: true });
const survivors = await readdir(runtime);
if (survivors.length > 0)
throw retainWorkspace(
`Runtime resources survived server shutdown (${survivors.join(", ")}); retained workspace: ${workspace}`,
);
await workspaceFixture.release();
console.log(
JSON.stringify(
{
@@ -423,15 +423,8 @@
"scripts/verify-real-cutter.mjs": 1,
"scripts/verify-real-ghidra-address-context.mjs": 1,
"scripts/verify-real-ghidra-checkpoints.mjs": 1,
"scripts/verify-real-ghidra-dol.mjs": 1,
"scripts/verify-real-ghidra-function-bundle.mjs": 1,
"scripts/verify-real-ghidra-m68k.mjs": 1,
"scripts/verify-real-ghidra-mips-relocatable.mjs": 1,
"scripts/verify-real-ghidra-mips-shared.mjs": 1,
"scripts/verify-real-ghidra-mips.mjs": 1,
"scripts/verify-real-ghidra-ppc.mjs": 1,
"scripts/verify-real-ghidra-project-coverage.mjs": 1,
"scripts/verify-real-ghidra-riscv.mjs": 1,
"scripts/verify-real-ghidra-swift.mjs": 1,
"scripts/verify-real-ghidra-switch.mjs": 1,
"scripts/verify-real-ghidra.mjs": 1,