Woher wissen Sie, dass es gewirkt hat?
Bei jedem anderen Werkzeug dieser Art wissen Sie es nicht. Die Anwendung startet, zeigt irgendein Datum, und Sie nehmen an, dass es stimmt. Sie kann die Uhr aber über einen Weg lesen, den nichts abgefangen hat - und dann bedeutet „der Test ist bestanden“ gar nichts.
Der Fehler, der kein Symptom hat
Ein Zeitverschiebungs-Werkzeug, das einen Kanal verpasst, stürzt nicht ab und warnt nicht. Die Anwendung liest das echte Datum einfach über die eine Funktion, die niemand abgefangen hat, und verhält sich genau so, wie sie sich an jedem gewöhnlichen Nachmittag verhalten hätte. Der Test wird grün. Der Fehler wird ausgeliefert.
RunAsDate ist dazu in seiner eigenen Dokumentation ehrlich - es sagt, dass es „Anwendungen nicht beeinflusst, die das aktuelle Datum bzw. die aktuelle Uhrzeit aus einer anderen Quelle beziehen“ - und überlässt Ihnen dann herauszufinden, ob Ihre eine davon ist. Dieses Werkzeug behandelt genau diesen Satz als etwas, das man misst.
Was eine Sitzung tatsächlich meldet
Echte Ausgabe, aus einem Lauf des Werkzeugs gegen einen der Test-Prozesse dieses Projekts - einen, der absichtlich eine Netzwerkverbindung öffnet:
$ chrono run "...\pnet-x64.exe" --at 2038-01-19T03:14:07 --report session.txt Chrono Mock - session report target: C:\ChronoMock\tools\probes\bin\pnet-x64.exe verdict: WORKS - time substitution took effect (processes: 1) every process that read time saw the session clock session: fake clock reached 2038-01-19T03:14:10 real elapsed 3.4s, fake elapsed 3.4s exited: the target closed itself with code 0 covered channels (substituted, with call counts, 4 calls in all): - pid 9152: GetSystemTimeAsFileTime (1 call) - pid 9152: NtQuerySystemTime (3 calls) ... every other channel, including the ones never called observed channels (hooked but left real): - pid 9152: NtCreateUserProcess (0 calls) - pid 9152: connect (1 call) warnings: - the target opened a network connection - it may read time from a server, which no local hook can cover (source.network_at_start) requested: 2038-01-19T03:14:07 (zone +02:00 (host default), mode flow)
Die letzten beiden Blöcke sind der Punkt. Nichts ist hier fehlgeschlagen - das Urteil ist funktioniert - und der Bericht sagt trotzdem ungefragt, dass diese Anwendung mit dem Netzwerk gesprochen hat, bevor sie fertig war, sodass ein lokaler Hook womöglich nicht die ganze Geschichte ist. Dieser Satz ist der Unterschied zwischen einem Test, dem Sie vertrauen können, und einem, dem nicht.
Vier Urteile, und zu jedem ein Exit-Code
Das Urteil wird im laufenden Prozess gefällt, bevor Sie mit der Arbeit beginnen, und es ist der Exit-Code des Werkzeugs selbst, sodass eine Pipeline danach verzweigen kann.
| Urteil | Exit | Bedeutung |
|---|---|---|
| funktioniert | 0 | jeder Prozess, der die Zeit las, sah die Sitzungsuhr |
| funktioniert teilweise | 10 | einige Kanäle abgedeckt, andere nicht - die nicht abgedeckten werden aufgelistet |
| funktioniert nicht | 11 | die Ersetzung hat die wichtigen Kanäle nicht erreicht |
| unbestimmt | 4 | die Abdeckung ließ sich nicht feststellen - ein ehrliches „Ich weiß es nicht“ |
Lautet das Urteil „funktioniert nicht“, wird der Prozess sofort angehalten, bevor Sie mit ihm arbeiten können. Das Urteil kann dem Start nicht vorausgehen, denn die einzige Quelle der Wahrheit ist eine Messung aus dem laufenden Prozess heraus. Garantiert ist, dass Sie die Wahrheit erfahren, bevor Sie mit der Arbeit beginnen - nicht, dass ein aussichtsloser Start nie stattfindet. Sie können das übergehen und weitermachen, und die Sitzung wird dann überall, wo sie erscheint, als unzuverlässig gekennzeichnet, auch in jedem Nachweis, den Sie exportieren.
Andere Codes sagen, dass der Lauf selbst scheiterte und nicht die Ersetzung - ein Bedienfehler, ein Ziel, das nicht starten konnte, ein abgebrochener Lauf, eine bereits laufende andere Sitzung - und keiner davon ist ein Urteil. Die vollständige Tabelle steht in der Kommandozeilen-Referenz.
Abgedeckt, beobachtet, nicht abgedeckt
Kanäle fallen in drei Behälter, und der mittlere ist der interessante.
- Abgedeckt - abgefangen und ersetzt. Die Anwendung hat gefragt und die Sitzungsuhr bekommen. Die Aufrufzahl sagt, wie oft.
- Beobachtet - abgefangen, gezählt und bewusst bei der Wahrheit gelassen. Hier stehen Wartevorgänge auf Objekte, weil ein verkürztes Zeitlimit eine Zeitüberschreitung an einem echten Handle vortäuschen könnte, und der Multimedia-Timer, weil seine Skalierung Audio- und MIDI-Timing verschieben würde - beide beobachtet, wenn die Sitzung die Achse der verstrichenen Zeit skaliert. Netzwerkverbindungen und ein Weg der Prozesserzeugung stehen in jeder Sitzung hier, weil sie Indizien sind, keine Uhren. Der hochauflösende Zähler gehört nicht in diese Gruppe: Er bleibt unberührt, bis Sie ihn mit
--scale-qpcanfordern, und ist dann abgedeckt. - Nicht abgedeckt - ein Kanal, den die Anwendung gelesen hat und den die Ersetzung nicht erreichte. Das macht aus einem Urteil funktioniert teilweise, und er wird namentlich aufgelistet statt weggefasst.
Auch eine Null ist eine Information: Der Kanal war abgefangen, und die Anwendung hat nie gefragt. Deshalb listet der Bericht sie auf, statt sie zu verbergen.
Was die Prüfung nicht sehen kann
Der Bericht ist ehrlich zur Abdeckung, und das heißt, ehrlich zu seinen eigenen Grenzen zu sein. Dies wird ohne jeden Funktionsaufruf gelesen, den man abfangen könnte, oder ganz außerhalb des Prozesses:
- direkte Lesezugriffe auf die gemeinsame Datenseite im Benutzermodus - der Wert steht im Speicher, es gibt also nichts abzufangen. Selten in gewöhnlichen Anwendungen, häufig in Lizenz- und Manipulationsschutz-Code
- direkte Systemaufrufe, die die Standard-Exporte umgehen - dasselbe Muster
- Zeit, die außerhalb des Prozesses bezogen wird, über WMI, einen COM-Server außerhalb des Prozesses oder RPC
- Zeit aus dem Netzwerk - ein HTTP-
Date-Header, NTP, eine API-Antwort oder die eigene Uhr eines Datenbankservers - Zeit von Kerneltreibern
Den Netzwerkfall kann die Prüfung wenigstens bemerken, weshalb die Warnung im Bericht oben existiert. Für den Rest bedeutet ein Urteil funktioniert, dass die Kanäle, die dieses Werkzeug sehen kann, abgedeckt waren - nicht, dass es keinen anderen Weg gibt.
In einer Pipeline
--json gibt dieselben Fakten in Maschinenform aus, --report schreibt die
Fassung für Menschen in eine Datei, und eine Sitzung, deren Urteil etwas anderes als
funktioniert ist, stellt dieser Datei einen Hinweis auf unzuverlässige Nachweise voran,
statt ihn unten zu vergraben.
chrono run "C:\apps\test.exe" --at 2027-12-31T23:59:59 --json --report evidence.txt