Skip to content

Known gaps

A missing value in a recording has one of these causes, or is a loss counted in Recording health. Read the field’s capture status first.

  • Remote process memory. The memory of another process, such as the target of ptrace or process_vm_readv and process_vm_writev, is never read. Antinomie records its address and length only (status ADDRESS_ONLY).
  • Kernel threads. They are not traced, even by Capture all.
  • 32-bit and x32 calls. The schema covers the native x86-64 table only. i386 and x32 system calls are not decoded.
  • Outputs of failed calls. Output buffers are captured only on success, with a few documented exceptions.
  • Pages that are not loaded. The recorder never faults memory in. Since 0.7.1 (with a base built for 0.7.1 or later) an input that was not resident at the call’s entry is read again when the call returns, tagged at exit, so the largest gap of this kind is closed. Memory a call never reads stays NOT_RESIDENT.
  • Driver-specific URING_CMD. An io_uring command to a driver has a private layout. It is recorded as a gap with no decoder; its address is 0, never the kernel’s address. Socket URING_CMD operations for setsockopt and getsockopt are decoded.
  • Unknown selectors and options. A setsockopt, getsockopt, prctl, ptrace, keyctl or similar call with a selector the recorder does not know is recorded as raw bytes (for socket options up to 128 KiB) with a “no decoder” gap.
  • Some nested layouts. A few structures keep their top-level bytes only and name the nested pointer that was not followed, such as netfilter table replacements and multicast source lists.
  • Kernel-managed deferred pointers. Values the kernel reads or writes much later, such as a thread’s clear_child_tid at exit or the robust futex list traversal, are marked not observed asynchronously.
  • Asynchronous payload capture is limited to contexts where the program’s memory can be read safely; see Async observations.
  • More than 256 requests in flight, or more than 1,024 records waiting, are refused and counted as loss.
  • AIO reap observations depend on a 64-entry table per boot.
  • Vectored AIO reads that complete in an interrupt are captured when the program reaps them, up to 1,024 vector entries per request. A read whose reap entry was dropped keeps its gap and counts as loss.
  • Triage does not read io_uring requests that touch no file, or TEE. See Async observations.
  • The file identity of an io_uring rename, link or unlink is looked up just before the operation, so a name changed in between is identified as it was at the lookup.

These need a base built for Antinomie 0.7.0 or later; see Records beside the system calls.

  • Hashes have limits. A file over 16 MiB, on a filesystem other than ext4, tmpfs or ramfs, or queued when 16 jobs or 64 MiB are already waiting is not hashed; the record says why. A digest is the file’s bytes when the kernel read it, shortly after the call.
  • Written files. A file written through an io_uring worker, or closed by a process outside the run, is not hashed. A file opened for writing and not written is hashed at its last close all the same.
  • The launched program of a desktop run is the shell wrapper that starts it, so its hash is the wrapper’s.
  • User names. Triage names a user from the /etc/passwd of the base the VM’s disk came from, plus the account the app created. A user created in the guest after the base was built has no name and stays a number.
  • Shared file mappings (revision 18, a base built for 0.8.0). The pages of a MAP_SHARED mapping are seen when a call writes them back or drops them, each as it was at that moment, not every store before it. Not seen: a page the kernel’s background writeback cleaned while the mapping stayed and that was not stored to again, and mappings of the kernel’s internal files (shared anonymous memory, System V shared memory, memfd). Pages past the 4 MiB copy budget per call are listed as not captured, with the reason.
  • Earlier file positions. Without revision 18, a write at an O_APPEND position or through a descriptor another process shares is at an unknown offset, and in-kernel copies change a file without its bytes.
  • A run split across reconnects. Triage reads every recording segment of the run only while they are in the same recording folder as its newest one.
  • A process core stores at most its budget: 64 MiB to 1 GiB, and no more than the guest accepts (half its RAM less 128 MiB). A base made before 0.7.0 accepts 64 MiB only. Untouched memory is kept as holes and costs nothing. A core is not an atomic snapshot of the process; see Memory and core dumps.
  • A memory dump is not decoded by Antinomie itself; Volatility 3 or MemNixFS does that.

A missing call can come from loss, an unavailable capture or an unsupported construct. Absence alone does not prove that an action never happened.