Observe owned macOS guest resource use and loaded recovery kexts

This commit is contained in:
dh committed 2026-10-06 08:02:17 +02:00
1 parent 96755c704e
commit b15670cc62
3 files changed
+180 -4

No files matched your search

+2
View File
@@ -20,6 +20,8 @@ Memory admission requires the unchanged 4-GiB guest plus 512 MiB QEMU overhead.
## Reasons and remaining gaps
The runtime-observation candidate compares against `96755c704e63884e9c45e8ebbb18b7b12e4bdd83` without changing VM parameters or native gates. After the existing KVM staging marker, the host samples only the owned container's QEMU `stat`, `status`, `io`, `wchan` and cgroup CPU/memory/I/O counters and pressure, at most once per 60 seconds and 173 attempts within the unchanged 172-minute outer budget. Ownership inspection and reads share a ten-second deadline; the exec also has a nine-second internal timeout. Missing files are optional and each successful UTC-stamped snapshot is limited to 16 KiB and appended to `runtime-resources.log`. Completion prints the complete history, bounded to 173 × 16 KiB, plus the existing resource limits bounded to 16 KiB, allowing counter deltas even when artifact retrieval fails. Command lines and environments are never read. Failure diagnostics additionally invoke `kmutil showloaded --list-only` under the existing 45-second watchdog; absent, empty or failed output does not establish whether the kexts loaded. The original gate failure is preserved. These observations provide performance evidence; they prove no bottleneck or fix by themselves. Local `--validate` covers marker retention, scheduling, PID/executable rejection, byte budgets, UTC retention and a real ten-second timeout without Docker or macOS.
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.
All four Swift helpers target `x86_64-apple-macos13.0`; the macOS 14 EventKit call has an existing macOS 13 fallback. Inspected native Mach-O files in pinned .NET SDK 10.0.401 x64 declare `minos 12.0`. These source/binary minima are not runtime qualification or vendor support: macOS 13 is outside [Microsoft's current .NET 10 supported-OS policy](https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md). This probe does not install that SDK, compile helpers or test calendar/audio permissions.
+177 -4
View File
@@ -24,6 +24,10 @@ static class NativeDiagnostic
const long GuestDiskBytes = 64L * 1024 * 1024 * 1024;
const long ContainerMemoryBytes = 6L * 1024 * 1024 * 1024;
const int MaximumCapturedCharacters = 8 * 1024 * 1024;
const int MaximumRuntimeSnapshotBytes = 16 * 1024;
const int MaximumRuntimeSnapshots = 173;
static readonly TimeSpan RuntimeSnapshotInterval = TimeSpan.FromSeconds(60);
static readonly TimeSpan RuntimeSnapshotTimeout = TimeSpan.FromSeconds(10);
const string SdkVersion = "10.0.401";
const string SdkSha512 = "33401b4a2da8554e3306db6072ea8569d9fcc608509c271e0aa4b39e7cc432da3631f14e7e1e2445d67d72550d18ce44a8bbd2382a756867ad2edab6b1c963c0";
const string FullState = "/storage/13/ci-state";
@@ -71,6 +75,7 @@ static class NativeDiagnostic
if (recoveryFormat is not ("dmg" or "raw")) throw new ArgumentException("Recovery format must be dmg or raw.");
if (args.Contains("--validate"))
{
await ValidateRuntimeObservation(output);
ValidateRunnerMemoryGate();
ValidateGuestProgress(output);
ValidateContracts();
@@ -106,6 +111,8 @@ static class NativeDiagnostic
// Full execution leaves eight minutes within the existing 180-minute job for evidence/cleanup.
var deadlineMinutes = full ? 172 : 40;
using var deadline = new CancellationTokenSource(TimeSpan.FromMinutes(deadlineMinutes));
var runtimeElapsed = Stopwatch.StartNew();
var runtimeObservation = new RuntimeObservation();
using var signal = OperatingSystem.IsLinux() ? PosixSignalRegistration.Create(PosixSignal.SIGTERM, context => { context.Cancel = true; deadline.Cancel(); }) : null;
ConsoleCancelEventHandler cancelHandler = (_, context) => { context.Cancel = true; deadline.Cancel(); };
Console.CancelKeyPress += cancelHandler;
@@ -117,7 +124,7 @@ static class NativeDiagnostic
throw new InvalidOperationException("This diagnostic runs on the existing Linux/x64 runner only.");
ValidateContracts();
var sourceCommit = (await Command("git", ["rev-parse", "HEAD"], output, "candidate-commit", deadline.Token)).Output.Trim();
Save(Path.Combine(output, "run-metadata.json"), new { token, startedUtc = DateTimeOffset.UtcNow, sourceCommit, dockurCommit = DockurCommit, profile = "kvm-host-ventura-cryptex-noavx", recoveryFormat, causalSingleVariableTest = false, comparisonBaselineCommit = "d1594e90b3fa9c6f3bb52f12b754ae933105b8c8", changedGuestVariable = recoveryFormat == "raw" ? "none" : "recovery-disk-backend", changedHostVariable = "recovery-hash-capture-frequency", kvm = true, cpuModel = "host", recoveryMajor = 13, cryptexVersion = "1.0.5", liluVersion = "1.7.1", noAvxBaseVersion = "12.6", noAvxSha256 = NoAvxHash, runId = Environment.GetEnvironmentVariable("GITHUB_RUN_ID"), server = Environment.GetEnvironmentVariable("GITHUB_SERVER_URL"), architecture = RuntimeInformation.ProcessArchitecture.ToString(), deadlineMinutes, mode = full ? "full" : "readiness" });
Save(Path.Combine(output, "run-metadata.json"), new { token, startedUtc = DateTimeOffset.UtcNow, sourceCommit, dockurCommit = DockurCommit, profile = "kvm-host-ventura-cryptex-noavx", recoveryFormat, causalSingleVariableTest = false, comparisonBaselineCommit = "96755c704e63884e9c45e8ebbb18b7b12e4bdd83", changedGuestVariable = "loaded-kexts-diagnostic", changedHostVariable = "runtime-resource-observation", kvm = true, cpuModel = "host", recoveryMajor = 13, cryptexVersion = "1.0.5", liluVersion = "1.7.1", noAvxBaseVersion = "12.6", noAvxSha256 = NoAvxHash, runId = Environment.GetEnvironmentVariable("GITHUB_RUN_ID"), server = Environment.GetEnvironmentVariable("GITHUB_SERVER_URL"), architecture = RuntimeInformation.ProcessArchitecture.ToString(), deadlineMinutes, mode = full ? "full" : "readiness" });
var info = await Command("docker", ["info", "--format", "{{json .}}"], output, "docker-info", deadline.Token);
using (var document = JsonDocument.Parse(info.Output))
{
@@ -163,6 +170,7 @@ static class NativeDiagnostic
{
deadline.Token.ThrowIfCancellationRequested();
await CaptureGuest(id, output, deadline.Token, full);
await CaptureRuntimeResources(state, output, runtimeObservation, runtimeElapsed.Elapsed, deadline.Token);
if (full && phaseStarted.Elapsed > phaseBudget)
throw new InvalidOperationException("The bounded native " + phase + " phase exceeded " + phaseBudget.TotalMinutes + " minutes.");
var resultPath = Path.Combine(output, "guest-result.json");
@@ -229,6 +237,7 @@ static class NativeDiagnostic
using var captureDeadline = new CancellationTokenSource(TimeSpan.FromSeconds(45));
try { await CaptureGuest(state.ContainerId ?? state.ContainerName, output, captureDeadline.Token, full, true, state.Token); } catch (Exception exception) { Console.Error.WriteLine("Final evidence capture: " + exception.Message); }
try { PrintGuestProof(output, state.Token); } catch (Exception exception) { Console.Error.WriteLine("Native proof output: " + exception.Message); }
try { PrintRuntimeResources(output); } catch (Exception exception) { Console.Error.WriteLine("Runtime resource output: " + exception.GetType().Name); }
if (full) try { PrintFullProof(output, state.Token); } catch (Exception exception) { Console.Error.WriteLine("Full native proof output: " + exception.Message); }
var clean = await Cleanup(output);
if (!clean) { outcome = "failed"; error = (error ?? "") + " Owned-resource cleanup failed; inspect cleanup evidence."; }
@@ -247,8 +256,9 @@ static class NativeDiagnostic
{
var readiness = File.ReadAllText(Path.Combine("tools", "ci", "macos-native-readiness.sh"));
var baseline = ReplaceOnce(readiness, "(( ${os_version%%.*} >= 13 ))", "(( ${os_version%%.*} >= 14 ))");
baseline = ReplaceOnce(baseline, " run_command processes /bin/ps -axo pid,ppid,comm\n run_command loaded_kexts /usr/bin/kmutil showloaded --list-only\n", " run_command processes /bin/ps -axo pid,ppid,comm\n");
if (Hash(Encoding.UTF8.GetBytes(baseline)) != "4d428f594dac14eff64ed87b172c81ecf85ac91da8c5460cd6ec4b1d310800c3")
throw new InvalidOperationException("Compatibility readiness may change only the baseline's macOS minimum to 13; identity, services, disk and limits must remain identical.");
throw new InvalidOperationException("Readiness may change only the macOS minimum and optional failure-time loaded-kext diagnostic; gates and watchdogs must remain identical.");
if (Hash(File.ReadAllBytes(Path.Combine("tools", "ci", "macos-native-bootstrap.sh"))) != "94f069e116fdc7685a4d233cab6fa50df9f39274386bb82157674061e74fadb5")
throw new InvalidOperationException("Compatibility profile must preserve the baseline Apple recoveryosd wrapper.");
if (Hash(Encoding.UTF8.GetBytes(OriginalDaemon13)) != "af9d7f6c1948079bd4384d27b6882678d6fb4e338fcf6a8be8f84fceef174ad6") throw new InvalidOperationException("macOS 13 allowlist bytes differ from the independently read comparison plist.");
@@ -1281,6 +1291,93 @@ static class NativeDiagnostic
await Command("docker", ["exec", id, "sh", "-c", "printf '[qemu]\n'; qemu-system-x86_64 --version | head -n 1; printf '[Recovery hash]\n'; test -f /storage/13/setup.dmg && sha256sum /storage/13/setup.dmg || exit 1; if test -f /storage/13/setup.dmg.raw.json; then printf '[RAW Recovery equality receipt]\n'; cat /storage/13/setup.dmg.raw.json; fi; printf '[resources]\n'; df -Pk /storage; cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max 2>/dev/null || true"], output, "guest-container-resources", cancellation, requireSuccess: false, retainSuccessful: true);
}
// Runtime arguments are fixed container paths; fixtures substitute only their own temporary tree.
const string RuntimeResourceScript = """
{
proc=$1; cgroup=$2; pid_file=$3
qemu_pid=$(head -c 32 "$pid_file" 2>/dev/null || true)
valid_pid=false
case "$qemu_pid" in ''|*[!0-9]*|0|1) ;; *)
if [ "${#qemu_pid}" -le 10 ] && [ "$(readlink "$proc/$qemu_pid/exe" 2>/dev/null)" = /usr/bin/qemu-system-x86_64 ]; then valid_pid=true; fi ;;
esac
if [ "$valid_pid" = true ]; then
printf '[owned QEMU pid=%s]\n' "$qemu_pid"
for name in stat status io wchan; do
printf '\n[qemu/%s]\n' "$name"
if [ -r "$proc/$qemu_pid/$name" ]; then head -c 2048 "$proc/$qemu_pid/$name" 2>/dev/null || true; else printf 'unavailable\n'; fi
done
else
printf '[owned QEMU unavailable; no process counters read]\n'
fi
for name in cpu.stat memory.current memory.events cpu.pressure memory.pressure io.pressure io.stat; do
printf '\n[cgroup/%s]\n' "$name"
if [ -r "$cgroup/$name" ]; then head -c 1024 "$cgroup/$name" 2>/dev/null || true; else printf 'unavailable\n'; fi
done
} | head -c 16384
""";
sealed class RuntimeObservation
{
public bool Staged { get; set; }
public int Attempts { get; set; }
public TimeSpan? LastAttempt { get; set; }
}
static bool StartRuntimeObservation(RuntimeObservation observation, bool staged, TimeSpan elapsed)
{
observation.Staged |= staged;
if (!observation.Staged || observation.Attempts >= MaximumRuntimeSnapshots || elapsed >= TimeSpan.FromMinutes(172)
|| observation.LastAttempt is { } previous && elapsed - previous < RuntimeSnapshotInterval) return false;
observation.LastAttempt = elapsed;
observation.Attempts++;
return true;
}
static async Task CaptureRuntimeResources(OwnedResources state, string output, RuntimeObservation observation, TimeSpan elapsed, CancellationToken cancellation)
{
var log = Path.Combine(output, "container.stdout.log");
var staged = File.Exists(log) && File.ReadLines(log).Any(line => line.Contains("[compatibility-profile] accelerator=kvm", StringComparison.Ordinal));
if (!StartRuntimeObservation(observation, staged, elapsed)) return;
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
deadline.CancelAfter(RuntimeSnapshotTimeout);
try
{
if (state.ContainerId is not { } id || !System.Text.RegularExpressions.Regex.IsMatch(id, "^[0-9a-f]{64}$"))
throw new InvalidOperationException("Missing owned container ID.");
var owner = await Command("docker", ["inspect", "--format", "{{index .Config.Labels \"" + OwnerLabel + "\"}}", id], output, "capture-runtime-owner", deadline.Token, maximumCapturedBytes: MaximumRuntimeSnapshotBytes);
if (owner.Output.Trim() != state.Token) throw new InvalidOperationException("Runtime observation refuses a foreign container.");
// The internal timeout also bounds the exec process if the Docker client is interrupted.
var result = await Command("docker", ["exec", id, "timeout", "9", "sh", "-c", RuntimeResourceScript, "runtime-resources", "/proc", "/sys/fs/cgroup", "/dev/shm/qemu.pid"], output, "capture-runtime-resources", deadline.Token, requireSuccess: false, maximumCapturedBytes: MaximumRuntimeSnapshotBytes);
if (result.ExitCode == 0 && !string.IsNullOrWhiteSpace(result.Output)) AppendRuntimeSnapshot(output, result.Output, DateTimeOffset.UtcNow);
}
catch (Exception exception)
{
// This optional probe must neither qualify nor fail a native gate or prevent cleanup.
Console.Error.WriteLine("[native-diagnostic] Runtime resource observation unavailable: " + exception.GetType().Name);
cancellation.ThrowIfCancellationRequested();
}
}
static void AppendRuntimeSnapshot(string output, string snapshot, DateTimeOffset capturedUtc)
{
var record = "[runtime-resources UTC " + capturedUtc.ToUniversalTime().ToString("O") + "]\n" + snapshot + "\n";
if (Encoding.UTF8.GetByteCount(record) > MaximumRuntimeSnapshotBytes) throw new InvalidOperationException("Runtime snapshot exceeds its byte budget.");
File.AppendAllText(Path.Combine(output, "runtime-resources.log"), record, new UTF8Encoding(false));
File.WriteAllText(Path.Combine(output, "runtime-resources.last-success.log"), record, new UTF8Encoding(false));
}
static void PrintRuntimeResources(string output)
{
foreach (var (name, budget) in new[] { ("runtime-resources.log", MaximumRuntimeSnapshots * MaximumRuntimeSnapshotBytes), ("guest-container-resources.last-success.stdout.log", MaximumRuntimeSnapshotBytes) })
{
var path = Path.Combine(output, name);
if (!File.Exists(path)) continue;
if (new FileInfo(path).Length > budget) throw new InvalidOperationException("Runtime console evidence exceeds its byte budget.");
Console.WriteLine("[native-diagnostic] Final resource evidence: " + name);
Console.WriteLine(File.ReadAllText(path));
}
}
static async Task CaptureMonitor(string id, string output, string token, CancellationToken cancellation)
{
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
@@ -1498,6 +1595,80 @@ static class NativeDiagnostic
if (Crc32(image.AsSpan(0, length)) != BinaryPrimitives.ReadUInt32BigEndian(image.AsSpan(trailer + 88)) || Crc32(decoded) != BinaryPrimitives.ReadUInt32BigEndian(mish.AsSpan(72)) || Crc32(mish.AsSpan(72, 4)) != BinaryPrimitives.ReadUInt32BigEndian(image.AsSpan(trailer + 360))) throw new InvalidOperationException("Independent C# fixture CRC32 readback failed.");
}
static async Task ValidateRuntimeObservation(string output)
{
var fixture = Path.Combine(output, "validation-runtime-observation");
Directory.CreateDirectory(fixture);
var observation = new RuntimeObservation();
if (StartRuntimeObservation(observation, false, TimeSpan.Zero)
|| !StartRuntimeObservation(observation, true, TimeSpan.Zero)
|| StartRuntimeObservation(observation, false, TimeSpan.FromSeconds(59))
|| !StartRuntimeObservation(observation, false, TimeSpan.FromSeconds(60))
|| !StartRuntimeObservation(observation, false, TimeSpan.FromSeconds(130))
|| observation.Attempts != 3) throw new InvalidOperationException("Runtime marker retention or monotonic interval failed.");
observation.Attempts = MaximumRuntimeSnapshots - 1;
if (!StartRuntimeObservation(observation, false, TimeSpan.FromSeconds(190))
|| StartRuntimeObservation(observation, false, TimeSpan.FromSeconds(250))
|| StartRuntimeObservation(new RuntimeObservation(), true, TimeSpan.FromMinutes(172)))
throw new InvalidOperationException("Runtime attempt/deadline bounds failed.");
var proc = Path.Combine(fixture, "proc");
var cgroup = Path.Combine(fixture, "cgroup");
var pidFile = Path.Combine(fixture, "qemu.pid");
Directory.CreateDirectory(Path.Combine(proc, "123"));
Directory.CreateDirectory(cgroup);
var exe = Path.Combine(proc, "123", "exe");
File.Delete(exe);
File.CreateSymbolicLink(exe, "/usr/bin/qemu-system-x86_64");
File.WriteAllText(pidFile, "123\n");
File.WriteAllText(Path.Combine(proc, "123", "stat"), "123 (qemu-system-x86) S 1 2 3\n");
File.WriteAllText(Path.Combine(proc, "123", "io"), "read_bytes: 4096\n");
File.WriteAllText(Path.Combine(cgroup, "cpu.stat"), "usage_usec 100\nnr_throttled 2\n");
var result = await Command("sh", ["-c", RuntimeResourceScript, "runtime-fixture", proc, cgroup, pidFile], fixture, "runtime-valid", CancellationToken.None, maximumCapturedBytes: MaximumRuntimeSnapshotBytes);
if (!result.Output.Contains("[owned QEMU pid=123]") || !result.Output.Contains("read_bytes: 4096")
|| !result.Output.Contains("nr_throttled 2") || !result.Output.Contains("unavailable") || result.Error.Length != 0)
throw new InvalidOperationException("Owned process/cgroup fixture or optional-file handling failed.");
File.WriteAllText(pidFile, "../123\n");
var invalid = await Command("sh", ["-c", RuntimeResourceScript, "runtime-fixture", proc, cgroup, pidFile], fixture, "runtime-invalid-pid", CancellationToken.None, maximumCapturedBytes: MaximumRuntimeSnapshotBytes);
if (!invalid.Output.Contains("no process counters read") || invalid.Output.Contains("read_bytes: 4096")) throw new InvalidOperationException("Invalid QEMU PID reached process counters.");
File.WriteAllText(pidFile, "123\n");
File.Delete(exe);
File.CreateSymbolicLink(exe, "/usr/bin/other-process");
var foreign = await Command("sh", ["-c", RuntimeResourceScript, "runtime-fixture", proc, cgroup, pidFile], fixture, "runtime-foreign-executable", CancellationToken.None, maximumCapturedBytes: MaximumRuntimeSnapshotBytes);
if (foreign.Output.Contains("read_bytes: 4096")) throw new InvalidOperationException("Foreign executable reached process counters.");
foreach (var (label, script) in new[] { ("runtime-ascii-budget", "head -c 20000 /dev/zero | tr '\\000' x"), ("runtime-utf8-budget", "i=0; while [ \"$i\" -lt 5000 ]; do printf 'éé'; i=$((i+1)); done") })
{
var rejected = false;
try { await Command("sh", ["-c", script], fixture, label, CancellationToken.None, maximumCapturedBytes: MaximumRuntimeSnapshotBytes); }
catch (Exception exception) when (exception is InvalidOperationException or OperationCanceledException) { rejected = true; }
var loggedBytes = new FileInfo(Path.Combine(fixture, label + ".stdout.log")).Length + new FileInfo(Path.Combine(fixture, label + ".stderr.log")).Length;
if (!rejected || loggedBytes > MaximumRuntimeSnapshotBytes) throw new InvalidOperationException("Runtime output exceeded its shared UTF-8 byte budget.");
}
var captured = DateTimeOffset.Parse("2026-10-06T00:00:00Z");
File.Delete(Path.Combine(fixture, "runtime-resources.log"));
AppendRuntimeSnapshot(fixture, result.Output, captured);
AppendRuntimeSnapshot(fixture, "last successful snapshot", captured.AddMinutes(1));
var retained = File.ReadAllText(Path.Combine(fixture, "runtime-resources.last-success.log"));
if (!retained.Contains("2026-10-06T00:01:00.0000000+00:00") || !retained.Contains("last successful snapshot")
|| !File.ReadAllText(Path.Combine(fixture, "runtime-resources.log")).Contains(result.Output))
throw new InvalidOperationException("Runtime UTC append or last-success retention failed.");
var oversizedRejected = false;
try { AppendRuntimeSnapshot(fixture, new string('é', MaximumRuntimeSnapshotBytes), captured); }
catch (InvalidOperationException) { oversizedRejected = true; }
if (!oversizedRejected || File.ReadAllText(Path.Combine(fixture, "runtime-resources.last-success.log")) != retained)
throw new InvalidOperationException("Oversized snapshot replaced last successful evidence.");
using var deadline = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken.None);
deadline.CancelAfter(RuntimeSnapshotTimeout);
var elapsed = Stopwatch.StartNew();
var timedOut = false;
try { await Command("sh", ["-c", "sleep 30"], fixture, "runtime-timeout", deadline.Token, maximumCapturedBytes: MaximumRuntimeSnapshotBytes); }
catch (OperationCanceledException) { timedOut = true; }
if (!timedOut || elapsed.Elapsed > RuntimeSnapshotTimeout + TimeSpan.FromSeconds(5)) throw new InvalidOperationException("Runtime command escaped its ten-second linked deadline.");
Save(Path.Combine(fixture, "validation.json"), new { success = true, intervalSeconds = RuntimeSnapshotInterval.TotalSeconds, maximumAttempts = MaximumRuntimeSnapshots, maximumBytes = MaximumRuntimeSnapshotBytes, timeoutSeconds = RuntimeSnapshotTimeout.TotalSeconds, observedTimeoutSeconds = elapsed.Elapsed.TotalSeconds, dockerExecuted = false, guestExecuted = false });
}
static async Task ValidateResourceRetention(string output)
{
var fixture = Path.Combine(output, "validation-resource-retention");
@@ -1513,7 +1684,7 @@ static class NativeDiagnostic
if (receipt.RootElement.GetProperty("stdoutSha256").GetString() != Hash(Encoding.UTF8.GetBytes(successful)) || receipt.RootElement.GetProperty("exitCode").GetInt32() != 0) throw new InvalidOperationException("Last successful snapshot receipt does not identify the retained bytes.");
}
static async Task<CommandResult> Command(string executable, string[] arguments, string output, string label, CancellationToken cancellation, bool requireSuccess = true, bool echo = false, bool retainSuccessful = false)
static async Task<CommandResult> Command(string executable, string[] arguments, string output, string label, CancellationToken cancellation, bool requireSuccess = true, bool echo = false, bool retainSuccessful = false, int? maximumCapturedBytes = null)
{
if (!label.StartsWith("capture-", StringComparison.Ordinal) && label is not "container" and not "container-running" and not "guest-container-resources")
Console.WriteLine("[native-diagnostic] " + label);
@@ -1523,6 +1694,7 @@ static class NativeDiagnostic
foreach (var argument in arguments) start.ArgumentList.Add(argument);
start.Environment["GIT_TERMINAL_PROMPT"] = "0";
using var process = Process.Start(start) ?? throw new InvalidOperationException("Cannot start " + executable);
var capturedBytes = 0;
async Task<string> Read(StreamReader reader, string stream)
{
var captured = new StringBuilder();
@@ -1532,7 +1704,8 @@ static class NativeDiagnostic
{
var count = await reader.ReadAsync(buffer.AsMemory(), commandToken);
if (count == 0) break;
if (captured.Length + count > MaximumCapturedCharacters)
if (captured.Length + count > MaximumCapturedCharacters
|| maximumCapturedBytes is { } byteBudget && Interlocked.Add(ref capturedBytes, Encoding.UTF8.GetByteCount(buffer.AsSpan(0, count))) > byteBudget)
{
commandCancellation.Cancel();
throw new InvalidOperationException(label + " exceeded its bounded diagnostic log size.");
+1
View File
@@ -157,6 +157,7 @@ diagnose_failure() {
run_command context /usr/sbin/sysctl kern.bootargs machdep.cpu.brand_string machdep.cpu.features machdep.cpu.leaf7_features
run_command parent /bin/ps -p "$$" -p "$PPID" -o pid=,ppid=,comm=
run_command processes /bin/ps -axo pid,ppid,comm
run_command loaded_kexts /usr/bin/kmutil showloaded --list-only
}
fail_probe() {