Files
reverse-engineering-anything/src/gpu/GpuCodeObjectsProvider.ts
T
morluto a891bb80ae refactor(filesystem): preserve admitted file facts
Return the validated descriptor and its original metadata from regular-file
admission. Propagate that contract through owned reads, hashes, snapshots,
artifact inventory and provider file workflows instead of replacing initial
facts with another stat. Keep arbitrary output-handle operations separate.

Preserve final descriptor/path identity proofs, borrowed-source verification,
BigInt precision, cancellation provenance and failed-close ownership. Remove
the obsolete source and owned-ZIP baseline stages and update their consumers
and regressions. Tie read completion faults to actual reads instead of the
retired duplicate stat; instrument real handles without fake handle wrappers.

Validation: fresh runtime/catalog build; focused source, lifecycle, filesystem,
CLI/MCP and authored provider-fixture tests; full static checks; docs checks;
test discovery; duplication check. Platform-specific cases remain skipped on
macOS. No installed provider or device behavior was verified.
2026-10-25 04:09:11 +08:00

340 lines
12 KiB
TypeScript

import { lstat, type FileHandle } from "node:fs/promises";
import type { GpuCodeObjectsPort } from "../application/gpu/GpuCodeObjectsPort.js";
import {
createGpuCodeObjectsEvidence,
createGpuCodeObjectsPartialEvidence,
} from "../domain/gpu/gpuCodeObjectsEvidence.js";
import type { ExecutionOptions } from "../application/AnalysisProvider.js";
import type {
GpuCodeObjects,
GpuCodeObjectsInput,
} from "../domain/gpu/gpuCodeObjects.js";
import {
gpuCodeObjectsSchema,
gpuCodeObjectsPartialSchema,
} from "../domain/gpu/gpuCodeObjects.js";
import { AnalysisError } from "../domain/analysisErrorBase.js";
import {
AnalysisArtifactChangedError,
AnalysisAccessDeniedError,
AnalysisCancelledError,
AnalysisResourceConstraintError,
AnalysisUnsupportedTargetError,
} from "../domain/analysisErrorCore.js";
import { BinaryTargetError } from "../domain/configurationErrors.js";
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
import { err, ok, type Result } from "../domain/result.js";
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
import {
withArtifactRegularFile,
bindArtifactFileCleanup,
artifactFilePrimaryFacts,
artifactFileCancellationCause,
artifactFileCompletion,
} from "../artifacts/ArtifactFileOperation.js";
import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js";
import {
BinaryFormatFailure,
BinaryResourceFailure,
} from "../native/metadata/BinaryContainer.js";
import {
GpuFileSource,
gpuCount,
GPU_OBJECT_BYTES,
GPU_REPORT_BYTES,
} from "./GpuSource.js";
import { GpuInventory } from "./GpuInventory.js";
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
import {
isSelectedRegularFileError,
sameRegularFileState,
} from "../filesystem/RegularFile.js";
export const GPU_CODE_OBJECTS_PROVIDER_IDENTITY = {
id: "gpu-code-objects",
name: "REA static GPU code-object reader",
version: "1",
} as const;
const OPERATION = "inspect_gpu_code_objects";
/** One admitted descriptor and bounded report at a time; retained cleanup blocks new work. */
export class GpuCodeObjectsProvider implements GpuCodeObjectsPort {
readonly identity = GPU_CODE_OBJECTS_PROVIDER_IDENTITY;
readonly #resources = new ArtifactResourceScope();
#busy = false;
async close(): Promise<void> {
try {
await this.#resources.close();
} catch (cause) {
const error = new ProviderCleanupError(
this.identity.id,
this.#resources.cleanupResources,
{},
{ cause, operation: OPERATION },
);
bindArtifactFileCleanup(cause, error);
throw error;
}
}
async inspect(
input: GpuCodeObjectsInput,
options?: ExecutionOptions,
): Promise<Result<GpuCodeObjects, AnalysisError>> {
if (this.#busy)
return err(
new AnalysisResourceConstraintError(
OPERATION,
"capacity",
"One GPU inspection is already active",
{ concurrent_inspections: 1 },
{ retryable: true },
),
);
this.#busy = true;
let progress: { inventory: GpuInventory; size: number } | undefined;
try {
return ok(
await this.#resources.run(() =>
withArtifactRegularFile(
input.path,
this.#resources,
options?.signal,
({ handle: file }) =>
this.#read(file, input, options?.signal, (value) => {
progress = value;
}),
),
),
);
} catch (cause) {
const { cause: primary, origin } = artifactFilePrimaryFacts(cause);
let error: AnalysisError;
if (origin === "owned-or-classified" && primary instanceof AnalysisError)
error = primary;
else if (primary instanceof BinaryResourceFailure)
error = new AnalysisResourceConstraintError(
OPERATION,
"memory",
primary.message,
{
maximum_object_bytes: GPU_OBJECT_BYTES,
maximum_report_bytes: GPU_REPORT_BYTES,
},
{
cause,
retryable: false,
remediationAction: `${primary.message} This release has no runtime override for that GPU byte allowance. The object allowance is ${String(GPU_OBJECT_BYTES)} bytes and the report allowance is ${String(GPU_REPORT_BYTES)} bytes. Additional host memory does not raise them. Repeating the unchanged request fails the same way.`,
},
);
else if (primary instanceof BinaryFormatFailure) {
error =
primary.kind === "unsupported"
? new AnalysisUnsupportedTargetError(
OPERATION,
input.path,
primary.message,
{ cause },
)
: new BinaryTargetError(input.path, primary.message, {
cause,
constraint: "file_format_rejected",
});
} else if (
origin === "owned-or-classified" &&
primary instanceof ArtifactReaderFailure &&
primary.reason === "cancelled"
)
error = new AnalysisCancelledError(OPERATION, {
cause: artifactFileCancellationCause(primary),
});
else if (
origin === "owned-or-classified" &&
primary instanceof ArtifactReaderFailure &&
primary.reason === "integrity"
)
error = new AnalysisArtifactChangedError(
OPERATION,
input.path,
primary.message,
{ cause },
);
else if (
primary instanceof Error &&
"code" in primary &&
(primary.code === "EACCES" || primary.code === "EPERM")
)
error = new AnalysisAccessDeniedError(
OPERATION,
input.path,
primary.code,
{ cause: primary },
);
else if (isSelectedRegularFileError(primary))
error = new BinaryTargetError(input.path, primary.message, { cause });
else if (
origin === "owned-or-classified" &&
primary instanceof ArtifactReaderFailure &&
primary.reason === "limit"
)
error = new AnalysisResourceConstraintError(
OPERATION,
"memory",
primary.message,
{ maximum_object_bytes: GPU_OBJECT_BYTES },
{
cause: primary,
retryable: false,
remediationAction: `${primary.message} The GPU object allowance is ${String(GPU_OBJECT_BYTES)} bytes. This release has no runtime override. Additional host memory does not raise it. Repeating the unchanged request fails the same way.`,
},
);
// Other coded I/O failures remain host failures (for example, EMFILE).
else
error = new ProviderAdapterError(this.identity.id, OPERATION, {
cause,
diagnostics: {
path: input.path,
reason:
primary instanceof Error ? primary.message : String(primary),
},
});
if (
cause instanceof ArtifactReaderFailure &&
cause.cleanup !== undefined
) {
const cleanup = new ProviderCleanupError(
this.identity.id,
cause.cleanup.resources,
{ reason: cause.cleanup.reason },
{ cause, operation: OPERATION },
);
error = analysisErrorWithCleanupFailure(error, cleanup, OPERATION);
}
const completed = artifactFileCompletion(cause);
if (completed !== undefined) {
// The descriptor owner retains observations even when its final close fails.
const report = gpuCodeObjectsSchema.safeParse(completed.value);
if (report.success)
error.retainPartialObservation(
createGpuCodeObjectsEvidence(report.data, this.identity, input),
);
}
if (
error.partialObservation === undefined &&
progress !== undefined &&
progress.inventory.objects.length > 0
) {
const partial = gpuCodeObjectsPartialSchema.parse({
kind: "gpu-code-objects-partial",
artifact: {
path: input.path,
observed_file_size: progress.size,
sha256: null,
file_identity: "unverified",
},
analysis: input.analysis,
objects: progress.inventory.objects,
kernels: progress.inventory.kernels,
ptx_artifacts: progress.inventory.ptx_artifacts,
coverage: { discovery: "partial", payloads: "partial" },
limitations: [
...progress.inventory.limitations,
"Only completed declarations observed before interruption are retained. Root file identity and completeness were not verified; positional IDs are scoped to this interrupted observation.",
],
});
error.retainPartialObservation(
createGpuCodeObjectsPartialEvidence(partial, this.identity, input),
);
}
bindArtifactFileCleanup(cause, error);
return err(error);
} finally {
this.#busy = false;
}
}
async #read(
file: FileHandle,
input: GpuCodeObjectsInput,
signal?: AbortSignal,
retainProgress?: (progress: {
inventory: GpuInventory;
size: number;
}) => void,
): Promise<GpuCodeObjects> {
const initial = await file.stat({ bigint: true });
const size = gpuCount(initial.size);
const source = new GpuFileSource(file, size, signal);
const sha256 = await source.digest();
if (input.expected_sha256 !== undefined && sha256 !== input.expected_sha256)
throw new AnalysisArtifactChangedError(
OPERATION,
input.path,
"GPU source does not match the inventory's selected SHA-256",
);
const inventory = new GpuInventory(input, signal);
retainProgress?.({ inventory, size });
const format = await inventory.inspect(source);
const after = await file.stat({ bigint: true });
const named = await lstat(input.path, { bigint: true }).catch(
(cause: unknown) => {
if (
cause instanceof Error &&
"code" in cause &&
["ENOENT", "ENOTDIR", "ELOOP"].includes(String(cause.code))
)
throw new ArtifactReaderFailure(
"integrity",
"Selected GPU path changed during inspection",
{ cause },
);
throw cause;
},
);
if ([after, named].some((state) => !sameRegularFileState(initial, state)))
throw new ArtifactReaderFailure(
"integrity",
"GPU source changed during inspection",
);
const leaves = inventory.objects.filter(
(row) =>
row.kind !== "cuda-fatbin" &&
row.kind !== "hip-bundle" &&
row.kind !== "native-section" &&
row.kind !== "host",
);
return gpuCodeObjectsSchema.parse({
artifact: {
path: input.path,
file_size: size,
sha256,
bytes_read: source.bytesRead,
},
format,
analysis: input.analysis,
objects: inventory.objects,
kernels: inventory.kernels,
ptx_artifacts: inventory.ptx_artifacts,
coverage: {
discovery_scope: "supported-section-names-and-standalone-formats",
discovery: inventory.discovery,
payloads:
input.analysis === "inventory"
? "not-requested"
: leaves.every((row) => row.inspection === "complete") &&
inventory.discovery === "complete"
? "complete"
: "partial",
examined_sections: inventory.examined_sections,
unknown_section_names: inventory.unknown_section_names,
},
limitations: [
...inventory.limitations,
"Full-file SHA-256 streams every file byte; section and selected payload reads add to artifact.bytes_read. Retained Evidence is historical and never reopens the target.",
],
});
}
}