feat(watchdog): dedicated-isolate heartbeat + resource sampler (T-435)
A main-isolate Timer would freeze WITH the main isolate and tell us nothing, so the watchdog runs in its own isolate: it fsyncs a heartbeat every ~500ms (so the last on-disk heartbeat bounds a freeze to ~500ms) and every ~2s samples this process's thread / handle-or-fd / child-host / RSS counts. A monotonically climbing child or thread count is the leak signature the soak couldn't reproduce on CI but a real freeze would show. Output is JSON-lines in clide-watchdog.log, bounded by the same truncate-on-cap scheme as the crumb files. - watchdog.dart (Flutter-free, tested): ResourceSample, ResourceSampler (forPlatform), PosixResourceSampler (/proc/self: Threads, fd count, task children, ProcessInfo.currentRss), WatchdogFile (bounded fsynced JSON-lines), runWatchdog (the loop, bounded by maxTicks for tests), watchdogEntry (the sendable Isolate.spawn entry). - watchdog_windows.dart (coverage:ignore — Win32 FFI, validated only at runtime on Windows): one CreateToolhelp32Snapshot for thread + conhost/OpenConsole child count, GetProcessHandleCount, ProcessInfo.currentRss. Exhaustively defensive: any failure yields a -1 field, snapshot handle always closed, never throws. - main.dart: spawn the watchdog at boot (desktop only), non-fatal. Per-line fsync means the OS reaping the isolate at exit loses nothing. Tests: ResourceSample.toJson, Posix sampler against real /proc, WatchdogFile (JSON shape, cap, disabled), runWatchdog (immediate baseline tick). Coverage gate 95.08%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:clide/app.dart';
|
||||
import 'package:clide/test_app.dart';
|
||||
@@ -110,6 +111,14 @@ Future<void> main() async {
|
||||
settingValue: bootSettings.get<String>('app.log.level'),
|
||||
);
|
||||
bootLogSinks = [FileLogSink(dir: Directory(logDirectory())).call];
|
||||
// Crash-diagnostic watchdog in its own isolate (T-435): heartbeats +
|
||||
// resource samples that survive a frozen main isolate. Non-fatal — a
|
||||
// leak-detector that breaks startup is worse than a missing one. The OS
|
||||
// reaps the isolate on exit; every line is fsynced, so abrupt death loses
|
||||
// nothing.
|
||||
try {
|
||||
await Isolate.spawn(watchdogEntry, ('${logDirectory()}/clide-watchdog.log', 500, 2000));
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Resolve toolchain + boot daemon inline — same as Linux.
|
||||
|
||||
Reference in New Issue
Block a user