forked from Manuel/meeting-assistant
prepare NoAVX recovery dispatch with memory admission and progress logs
This commit is contained in:
1 parent
7ea1c7f517
commit
fef676c810
3 files changed
+59
-5
No files matched your search
@@ -7,6 +7,7 @@ on:
|
|||||||
# Its manual workflow provides that evidence; the PR branch still runs all jobs.
|
# Its manual workflow provides that evidence; the PR branch still runs all jobs.
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- codex/macos-ci-kvm-compatibility
|
- codex/macos-ci-kvm-compatibility
|
||||||
|
- codex/macos-kvm-noavx-recovery
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ Baseline: bootstrap commit `4606de069678e8f95dfe3c7dad1bf5ce5384d30c`; separate
|
|||||||
|
|
||||||
The NoAVX continuation compares against KVM Recovery commit `720a431`. Its only guest change is adding `NoAVXFSCompressionTypeZlib-AVXpel.kext` to the existing OpenCore overlay and `Kernel.Add`. Existing Lilu/CryptexFixup, CPU passthrough, macOS 13 Recovery, disk, probes and deadlines are preserved. The hypothesis is that an AVX-dependent filesystem decompression path blocks native file loading on the Celeron; this has not been established as the cause of the disk-readiness hang. Application source is unchanged, and this candidate has only offline validation evidence.
|
The NoAVX continuation compares against KVM Recovery commit `720a431`. Its only guest change is adding `NoAVXFSCompressionTypeZlib-AVXpel.kext` to the existing OpenCore overlay and `Kernel.Add`. Existing Lilu/CryptexFixup, CPU passthrough, macOS 13 Recovery, disk, probes and deadlines are preserved. The hypothesis is that an AVX-dependent filesystem decompression path blocks native file loading on the Celeron; this has not been established as the cause of the disk-readiness hang. Application source is unchanged, and this candidate has only offline validation evidence.
|
||||||
|
|
||||||
|
The host-memory admission check is copied unchanged from RAW candidate `ec5508e`: the unchanged 4-GiB guest plus 512 MiB QEMU overhead requires 4.5 GiB available. Offline validation accepts the captured run-4204 value of 5,138,696 KiB and rejects 4 GiB, missing and invalid values. Guest RAM and the 6-GiB container cap are unchanged. This admission budget reserves no host memory against other workloads. The `codex/macos-kvm-noavx-recovery` branch skips only the PR/Push workflow's push trigger; pull requests and manual workflows retain their existing triggers.
|
||||||
|
|
||||||
|
The existing one-minute heartbeat prints at most the last two captured `[proof-start]`, `[proof-done]`, `[proof-native-wait]`, `[proof-result]` or `[native-version]` lines, each capped at 256 characters. `[proof-native-wait]` is this candidate's existing command-completion marker. It reads only the already retained `guest-proof.log`; no additional Docker/guest query or polling timer is added. Five offline fixture cases verify marker selection, missing/unrelated output and the bounds. The controlled 40-minute host deadline and existing cleanup are preserved.
|
||||||
|
|
||||||
## Reasons and remaining gaps
|
## Reasons and remaining gaps
|
||||||
|
|
||||||
The existing daemon's Intel Celeron 1037U lacks AVX/AVX2; a separate diagnostic proved KVM enabled/paused state and clean exit. `CPU_MODEL=host` preserves actual instruction availability rather than advertising AVX2 through emulated Skylake. This candidate refuses a TCG or CPU-model fallback.
|
The existing daemon's Intel Celeron 1037U lacks AVX/AVX2; a separate diagnostic proved KVM enabled/paused state and clean exit. `CPU_MODEL=host` preserves actual instruction availability rather than advertising AVX2 through emulated Skylake. This candidate refuses a TCG or CPU-model fallback.
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ static class NativeDiagnostic
|
|||||||
var output = Path.GetFullPath(Option(args, "--output") ?? "artifacts/native-macos");
|
var output = Path.GetFullPath(Option(args, "--output") ?? "artifacts/native-macos");
|
||||||
if (args.Contains("--validate"))
|
if (args.Contains("--validate"))
|
||||||
{
|
{
|
||||||
|
ValidateRunnerMemoryGate();
|
||||||
|
ValidateGuestProgress(output);
|
||||||
ValidateContracts();
|
ValidateContracts();
|
||||||
if (Option(args, "--source") is { } source)
|
if (Option(args, "--source") is { } source)
|
||||||
{
|
{
|
||||||
@@ -113,9 +115,8 @@ static class NativeDiagnostic
|
|||||||
throw new InvalidOperationException("Existing Docker resources cannot fit this bounded 2-CPU/6-GiB diagnostic; no infrastructure change was requested.");
|
throw new InvalidOperationException("Existing Docker resources cannot fit this bounded 2-CPU/6-GiB diagnostic; no infrastructure change was requested.");
|
||||||
}
|
}
|
||||||
await Command("sh", ["-c", "cat /proc/meminfo; printf '\n[cgroup]\n'; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true; printf '\n[workspace disk]\n'; df -Pk ."], output, "runner-resources", deadline.Token);
|
await Command("sh", ["-c", "cat /proc/meminfo; printf '\n[cgroup]\n'; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true; printf '\n[workspace disk]\n'; df -Pk ."], output, "runner-resources", deadline.Token);
|
||||||
var available = System.Text.RegularExpressions.Regex.Match(File.ReadAllText("/proc/meminfo"), @"(?m)^MemAvailable:\s+(\d+) kB$");
|
if (!HasAvailableGuestMemory(File.ReadAllText("/proc/meminfo")))
|
||||||
if (!available.Success || long.Parse(available.Groups[1].Value) < 5L * 1024 * 1024)
|
throw new InvalidOperationException("Existing runner memory cannot fit the 4-GiB guest plus its 512-MiB QEMU overhead budget; no infrastructure change was requested.");
|
||||||
throw new InvalidOperationException("Existing runner memory has less than the 5-GiB available diagnostic budget; no infrastructure change was requested.");
|
|
||||||
var source = Path.Combine(work, "dockur");
|
var source = Path.Combine(work, "dockur");
|
||||||
await Command("git", ["clone", "--no-checkout", "https://github.com/dockur/macos.git", source], output, "dockur-clone", deadline.Token);
|
await Command("git", ["clone", "--no-checkout", "https://github.com/dockur/macos.git", source], output, "dockur-clone", deadline.Token);
|
||||||
await Command("git", ["-C", source, "checkout", "--detach", DockurCommit], output, "dockur-checkout", deadline.Token);
|
await Command("git", ["-C", source, "checkout", "--detach", DockurCommit], output, "dockur-checkout", deadline.Token);
|
||||||
@@ -136,7 +137,7 @@ static class NativeDiagnostic
|
|||||||
AssertContainer(File.ReadAllText(Path.Combine(output, "container-created.stdout.log")), token);
|
AssertContainer(File.ReadAllText(Path.Combine(output, "container-created.stdout.log")), token);
|
||||||
await Command("docker", ["start", id], output, "docker-start", deadline.Token);
|
await Command("docker", ["start", id], output, "docker-start", deadline.Token);
|
||||||
await CapturePressure(id, output, "before", deadline.Token);
|
await CapturePressure(id, output, "before", deadline.Token);
|
||||||
Console.WriteLine("The owned restricted KVM/host-CPU macOS 13 compatibility guest is starting. Success requires native macOS 13+/x86_64 and a writable 64-GiB disk; no installer will run. This is not a single-variable causal test.");
|
Console.WriteLine("The owned restricted KVM/host-CPU macOS 13 compatibility guest is starting. Success requires native macOS 13+/x86_64 and a writable 64-GiB disk; no installer will run. The additional NoAVX boot kext is the only guest variable against KVM baseline 720a431.");
|
||||||
var recoveryStarted = Stopwatch.StartNew();
|
var recoveryStarted = Stopwatch.StartNew();
|
||||||
var heartbeat = Stopwatch.StartNew();
|
var heartbeat = Stopwatch.StartNew();
|
||||||
var diskPressureCaptured = false;
|
var diskPressureCaptured = false;
|
||||||
@@ -163,7 +164,8 @@ static class NativeDiagnostic
|
|||||||
if (running.Output.Trim() != "true") throw new InvalidOperationException("Guest container exited before a native readiness result.");
|
if (running.Output.Trim() != "true") throw new InvalidOperationException("Guest container exited before a native readiness result.");
|
||||||
if (heartbeat.Elapsed >= TimeSpan.FromSeconds(60))
|
if (heartbeat.Elapsed >= TimeSpan.FromSeconds(60))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[native-diagnostic] phase=recovery; elapsed={recoveryStarted.Elapsed.TotalMinutes:F1} minutes; container=running; readiness=pending");
|
Console.WriteLine($"[native-diagnostic] phase=recovery; elapsed={recoveryStarted.Elapsed.TotalMinutes:F1}/40 minutes; container=running; readiness=pending");
|
||||||
|
foreach (var progress in LastGuestProgress(proofPath)) Console.WriteLine("[native-diagnostic] guest-progress=" + progress);
|
||||||
heartbeat.Restart();
|
heartbeat.Restart();
|
||||||
}
|
}
|
||||||
await Task.Delay(TimeSpan.FromSeconds(20), deadline.Token);
|
await Task.Delay(TimeSpan.FromSeconds(20), deadline.Token);
|
||||||
@@ -430,6 +432,53 @@ static class NativeDiagnostic
|
|||||||
return (boot, document.ToString(), assets);
|
return (boot, document.ToString(), assets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HasAvailableGuestMemory(string meminfo)
|
||||||
|
{
|
||||||
|
var available = System.Text.RegularExpressions.Regex.Match(meminfo, @"(?m)^MemAvailable:\s+(\d+) kB$");
|
||||||
|
return available.Success && long.TryParse(available.Groups[1].Value, out var kib) && kib >= 4L * 1024 * 1024 + 512L * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string[] LastGuestProgress(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path)) return [];
|
||||||
|
var markers = new[] { "[proof-start]", "[proof-done]", "[proof-native-wait]", "[proof-result]", "[native-version]" };
|
||||||
|
return File.ReadLines(path).Where(line => markers.Any(marker => line.StartsWith(marker, StringComparison.Ordinal)))
|
||||||
|
.TakeLast(2).Select(line => line[..Math.Min(line.Length, 256)]).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ValidateGuestProgress(string output)
|
||||||
|
{
|
||||||
|
var fixture = Path.Combine(output, "validation-guest-progress");
|
||||||
|
Directory.CreateDirectory(fixture);
|
||||||
|
var path = Path.Combine(fixture, "guest-proof.log");
|
||||||
|
File.Delete(path);
|
||||||
|
if (LastGuestProgress(path).Length != 0) throw new InvalidOperationException("Missing guest progress was fabricated.");
|
||||||
|
File.WriteAllText(path, "[proof-start] platform child=12\nignored native output\n[proof-native-wait] platform exit=0\n[proof-start] uid child=13\n", new UTF8Encoding(false));
|
||||||
|
if (!LastGuestProgress(path).SequenceEqual(new[] { "[proof-native-wait] platform exit=0", "[proof-start] uid child=13" })) throw new InvalidOperationException("Heartbeat must show the last two captured native progress markers.");
|
||||||
|
File.WriteAllText(path, "[proof-done] uid\n[native-version] 13.6\n[proof-result] true: readiness\n", new UTF8Encoding(false));
|
||||||
|
if (!LastGuestProgress(path).SequenceEqual(new[] { "[native-version] 13.6", "[proof-result] true: readiness" })) throw new InvalidOperationException("Heartbeat lost native version/result progress.");
|
||||||
|
File.WriteAllText(path, "[proof-start] " + new string('x', 1024) + "\n", new UTF8Encoding(false));
|
||||||
|
if (LastGuestProgress(path).Single().Length != 256) throw new InvalidOperationException("Heartbeat progress line exceeded its output bound.");
|
||||||
|
File.WriteAllText(path, "unrelated output\n", new UTF8Encoding(false));
|
||||||
|
if (LastGuestProgress(path).Length != 0) throw new InvalidOperationException("Heartbeat selected unrelated guest output.");
|
||||||
|
Save(Path.Combine(fixture, "receipt.json"), new { success = true, fixtureCases = 5, maximumLines = 2, maximumLineCharacters = 256, existingProofOnly = true, dockerExecuted = false, guestExecuted = false });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ValidateRunnerMemoryGate()
|
||||||
|
{
|
||||||
|
// Run 4204: 4-GiB guest plus 512-MiB QEMU overhead fits its captured available memory.
|
||||||
|
var cases = new[]
|
||||||
|
{
|
||||||
|
("captured-run4204", "MemTotal: 16281732 kB\nMemAvailable: 5138696 kB\n", true),
|
||||||
|
("below-guest-plus-overhead", "MemAvailable: 4194304 kB\n", false),
|
||||||
|
("missing", "MemTotal: 16281732 kB\n", false),
|
||||||
|
("invalid", "MemAvailable: unavailable kB\n", false)
|
||||||
|
};
|
||||||
|
foreach (var (name, meminfo, expected) in cases)
|
||||||
|
if (HasAvailableGuestMemory(meminfo) != expected)
|
||||||
|
throw new InvalidOperationException("Existing runner memory admission failed: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
static Dictionary<string, byte[]> ReadNoAvxArchive(byte[] bytes)
|
static Dictionary<string, byte[]> ReadNoAvxArchive(byte[] bytes)
|
||||||
{
|
{
|
||||||
if (bytes.Length != 98356 || Hash(bytes) != NoAvxHash) throw new InvalidOperationException("Pinned OCLP NoAVX archive size/hash mismatch.");
|
if (bytes.Length != 98356 || Hash(bytes) != NoAvxHash) throw new InvalidOperationException("Pinned OCLP NoAVX archive size/hash mismatch.");
|
||||||
|
|||||||
Reference in new issue
Block a user