Das tracev3-Dateiformat erklärt
Ein Blick in macOS-tracev3-Dateien: Header, Katalog und LZ4-Chunksets, Firehose-, Oversize-, Statedump- und Simpledump-Chunks, uuidtext, dsc und timesync.
Kurz gesagt. Eine .tracev3-Datei ist ein Strom von Chunks. Ein Header-Chunk beschreibt Maschine und Bootvorgang, Katalog-Chunks listen Prozesse und Subsysteme auf, und auf jeden Katalog folgen Chunksets – LZ4-komprimierte Blöcke mit Firehose-Einträgen (den Protokollmeldungen), Oversize-Strings sowie Statedump- und Simpledump-Datensätzen. Der Text stammt aus uuidtext- und dsc-Dateien, die Zeit aus timesync. Genau diese Struktur dekodiert der Unified Log Parser mit dem Open-Source-Parser von Mandiant, kompiliert zu WebAssembly.
Der folgende Aufbau stützt sich auf die öffentliche Reverse-Engineering-Arbeit von Mandiant (macos-UnifiedLogs) und Joachim Metz (libyal dtformats). Apple dokumentiert das Format nicht, und Felder, die in diesen Quellen als unbekannt gelten, sind es auch hier.
Chunks und Präambeln
Jeder Chunk beginnt mit einer 16-Byte-Präambel: einem 32-Bit-Tag, einem 32-Bit-Sub-Tag und einer 64-Bit-Datengröße. Chunks werden auf 8 Byte aufgefüllt. Die relevanten Tags:
| Tag | Chunk |
|---|---|
0x1000 | Header |
0x600b | Katalog |
0x600d | Chunkset (komprimiert) |
0x6001 | Firehose (innerhalb eines Chunksets) |
0x6002 | Oversize (innerhalb eines Chunksets) |
0x6003 | Statedump (innerhalb eines Chunksets) |
0x6004 | Simpledump (innerhalb eines Chunksets) |
Eine Datei beginnt deshalb immer mit den Bytes 00 10 00 00 – daran erkennt der Parser sie, egal wie sie heißt.
Header
Der Header hält die Mach-Timebase (Zähler und Nenner) fest, die Continuous Time beim Anlegen der Datei, die Zeitzonenverschiebung und das Sommerzeit-Flag sowie Sub-Chunks mit der Build-Version (zum Beispiel 25G83), dem Hardwaremodell (Mac14,14), der Boot-UUID, der PID von logd und dem Pfad der Zeitzonendatei. Die Boot-UUID verknüpft jeden Eintrag mit einer Boot-Sitzung und mit den passenden timesync-Datensätzen. Der Tab Quellen des Parsers zeigt diese Werte an.
Katalog
Ein Katalog beschreibt die Einträge der nachfolgenden Chunksets:
- ein Array von UUIDs – der protokollierenden Binarys und des Shared Cache;
- eine Tabelle mit Subsystem- und Kategorie-Strings;
- Prozesseinträge: PID, effektive User-ID, der Index der UUID des Hauptprogramms und der UUID des Shared Cache, die zusätzlichen UUIDs (geladene Bibliotheken) mit ihren Ladeadressen sowie die vom Prozess genutzten Subsysteme;
- Subchunk-Deskriptoren: Zeitspanne, unkomprimierte Größe und Kompressionsalgorithmus jedes Chunksets.
Firehose-Einträge verweisen über ein Paar von Kennungen, die in dieser Tabelle aufgelöst werden, auf ihren Prozess und über einen kleinen Index auf ihr Subsystem.
Chunksets
Ein Chunkset ist ein komprimierter Block. Die meisten nutzen LZ4 mit Apples Block-Header bv41 (bv41- kennzeichnet einen unkomprimierten Block); neuere Versionen können auch LZBITMAP verwenden, das der Parser unterstützt. Dekomprimiert ist ein Chunkset wiederum eine Folge von Chunks.
Firehose
Ein Firehose-Chunk hat eine Präambel mit einer Basis-Continuous-Time, danach folgen die Einträge. Jeder Eintrag hat einen Aktivitätstyp (Log, Activity, Trace, Signpost, Loss), einen Log-Typ (Default, Info, Debug, Error, Fault oder eine Signpost-Art), Flags, eine Thread-ID, ein Zeitdelta und einen Datenbereich. Die Flags geben an, wo der Formatstring liegt: in der eigenen uuidtext-Datei des Prozesses (Hauptprogramm), im Shared Cache (dsc), an einem absoluten Offset oder relativ zu einer anderen UUID. Der Datenbereich enthält die Argumente – Zahlen, Strings oder Verweise auf private Daten und Oversize-Strings.
Oversize
Argumente, die für den Eintrag zu groß sind, werden in Oversize-Chunks gespeichert, adressiert über eine Datenreferenz. Sie können in einer späteren Datei liegen als der Eintrag, der auf sie verweist. Deshalb muss ein Parser Oversize-Daten über Dateigrenzen hinweg mitführen und unaufgelöste Einträge am Ende erneut versuchen.
Statedump und Simpledump
Statedumps sind Schnappschüsse, die ein Prozess bei Bedarf schreibt – eine plist, ein Protobuf oder ein benutzerdefiniertes Objekt mit Titel (der „TCC Authorization Cache“ ist ein Beispiel). Simpledumps sind kurze Meldungen ohne Formatstring, die launchd und einige andere protokollieren.
uuidtext und dsc: der fehlende Text
Eine uuidtext-Datei (/private/var/db/uuidtext/XX/…, wobei XX das erste Byte der UUID ist und der Dateiname aus den restlichen 30 Hex-Ziffern besteht) beginnt mit der Signatur 0x66778899, listet Bereiche des Adressraums des Binarys auf, enthält die zugehörigen Formatstrings und endet mit dem Pfad des Binarys. Eine dsc-Datei (uuidtext/dsc/<32 hex>, Signatur hcsd) leistet dasselbe für den gesamten dyld Shared Cache und benennt jedes darin enthaltene Framework.
Um eine Meldung zu rendern, ermittelt der Parser anhand der Katalog-UUID und der Flags des Eintrags die richtige Datei, liest den Formatstring am gespeicherten Offset und setzt die Argumente im printf-Stil ein: %d, %s, %@, %{public}s, %{private}@ sowie Typ-Decoder wie %{errno}d, %{uuid_t}.16P oder %{bool}d. Private Argumente, die nicht erfasst wurden, erscheinen als <private>.
timesync
timesync-Dateien enthalten pro Boot-UUID einen Header (Signatur 0xbbb0, Timebase, Bootzeit) und Datensätze, die eine Continuous Time einer Uhrzeit zuordnen. Die Uhrzeit eines Eintrags ergibt sich aus der Uhrzeit des nächstgelegenen vorangehenden Datensatzes plus der verstrichenen Continuous Time, multipliziert mit der Timebase – 125/3 auf Apple Silicon, 1/1 auf Intel.
Was das in der Praxis bedeutet
- Sichern Sie
uuidtext,dscundtimesynczusammen mit den tracev3-Dateien (Leitfaden zur Sicherung). - Rechnen Sie selbst dann mit einigen „unaufgelösten“ Einträgen: Ein String kann in den von Apple aufbewahrten Dateien fehlen.
- Vergleichen Sie wichtige Einträge mit
log show. An einem echten Archiv validiert, renderte der Parser rund 99 % der Log-, Activity- und State-Meldungen identisch; Signposts unterscheiden sich in der Darstellung.
Die einzelnen Begriffe finden Sie im Glossar, oder öffnen Sie Ihre eigenen Dateien im Parser.