tiny.machine.checkpoint.hot
Defined in checkpoint.
Recording a running machine often costs too much when every capture copies and hashes all 67,108,864 bytes, because an execution changes only a small part of that data between two captures.
API (18)
Actions
Public operations.
Capacity.derive: The function derives the exact byte counts forpagesslots.Snapshot.aliases: The function answers whether a byte range touches the snapshot value, the parent handle, the storage and memory behind that parent, or the captured index and page arrays.Snapshot.dirtyPageCount: The function reports how many ordered changed pages the snapshot holds.Snapshot.evidenceRam: The function returns the durable parent's complete normalized memory.Snapshot.identity: The function revalidates the parent, the changed pages, the memory digest, and the stored identity.Snapshot.materializeForRestore: The function overlays the changed pages on authenticated parent memory, then verifies the child memory digest.Snapshot.prepareForRestore: The function verifiesexpectedand requires the destination to already hold the parent memory image.Snapshot.root: The function returns the fully verified child checkpoint root.Storage.init: The function joins an index slice and a page slice that carry the same number of slots.capture: The function captures ordered changed pages from live memory against a durable parent.
Types and contracts
Public types and contracts.
Capacity: The capacity fixes how much storage a caller supplies for a capped number of changed pages.Error: The error set combines the failures of a durable checkpoint with destination aliasing for a snapshot.Materialized: The record holds the restore result from a snapshot.Snapshot: The snapshot holds a changed-page checkpoint borrowing a durable parent and the captured storage.Storage: The storage holds the caller-owned output arrays for one changed-page capture.
Values and defaults
Public values and defaults.
Source
Source: lib/machine/src/checkpoint/hot/owner.zig:18
/// The capacity fixes how much storage a caller supplies for a capped number of/// changed pages. The byte counts cover one two-byte index and one aligned/// 4096-byte page for each slot.pub const Capacity = struct { pages: u16, index_storage_bytes: u32, page_storage_bytes: u32, /// The function derives the exact byte counts for `pages` slots. A count /// above 16,384 returns `DeltaCapacityExceeded`. pub fn derive(pages: usize) error{DeltaCapacityExceeded}!Capacity { if (pages > page_count) return error.DeltaCapacityExceeded; const index_storage_bytes = std.math.mul( usize, pages, @sizeOf(u16), ) catch return error.DeltaCapacityExceeded; const page_storage_bytes = std.math.mul( usize, pages, page_bytes, ) catch return error.DeltaCapacityExceeded; return .{ .pages = @intCast(pages), .index_storage_bytes = @intCast(index_storage_bytes), .page_storage_bytes = @intCast(page_storage_bytes), }; }};Source: lib/machine/src/checkpoint/hot/owner.zig:78
/// The record holds the restore result from a snapshot. The `copied_pages`/// field counts the changed pages written into the caller-owned destination/// memory.pub const Materialized = struct { material: checkpoint.Material, copied_pages: u16,};Source: lib/machine/src/checkpoint/hot/owner.zig:91
/// The snapshot holds a changed-page checkpoint borrowing a durable parent and/// the captured storage. The caller preserves the parent, the indices, and the/// page bytes for the snapshot's lifetime.pub const Snapshot = struct { parent: *const checkpoint.Checkpoint, identity_value: checkpoint.Identity, memory: checkpoint.MemoryDigest, material: checkpoint.Material, indices: []const u16, pages: []align(page_bytes) const u8, /// The function reports how many ordered changed pages the snapshot holds. /// A caller uses this count to size a report or a later publication. pub fn dirtyPageCount(self: *const @This()) u16 { std.debug.assert(self.indices.len <= page_count); return @intCast(self.indices.len); } /// The function revalidates the parent, the changed pages, the memory /// digest, and the stored identity. A recomputed value differing from the /// stored one returns `CheckpointCorrupt`. pub fn identity(self: *const @This()) checkpoint.DurableError!checkpoint.Identity { return (try inspect(self)).identity; } fn inspect(self: *const @This()) checkpoint.DurableError!Inspection { const parent_contents = try checkpoint.inspect(self.parent); try checkpoint.validateParent(self.material, parent_contents.material); try checkpoint.validateDeltaImage( self.indices, self.material.immutable_image, ); const memory = try checkpoint.deltaDigest( self.parent.ram, self.indices, self.pages, ); if (!std.meta.eql(memory, self.memory)) { return error.CheckpointCorrupt; } const actual = try checkpoint.identify(self.material, memory); if (!std.meta.eql(actual, self.identity_value)) { return error.CheckpointCorrupt; } return .{ .parent = parent_contents, .identity = actual }; } /// The function returns the fully verified child checkpoint root. pub fn root(self: *const @This()) checkpoint.DurableError!checkpoint.Root { return (try self.identity()).root; } /// The function verifies `expected` and requires the destination to already /// hold the parent memory image. The snapshot storage and the destination /// memory must be disjoint. The call returns the verified identity. pub fn prepareForRestore( self: *const @This(), expected: checkpoint.Root, destination: []align(page_bytes) const u8, ) Error!checkpoint.Identity { const inspection = try inspect(self); if (!std.meta.eql(inspection.identity.root, expected)) { return error.CheckpointRootMismatch; } try validateDestinationAgainst(self, destination, inspection.parent); return inspection.identity; } /// The function overlays the changed pages on authenticated parent memory, /// then verifies the child memory digest. The call returns the material and /// the number of copied pages. Pages already copied stay in the destination /// when the closing digest check fails. pub fn materializeForRestore( self: *const @This(), expected: checkpoint.Root, destination: []align(page_bytes) u8, ) Error!Materialized { const inspection = try inspect(self); if (!std.meta.eql(inspection.identity.root, expected)) { return error.CheckpointRootMismatch; } try validateDestinationAgainst(self, destination, inspection.parent); for (self.indices, 0..) |page_index, index| { const start = @as(usize, page_index) * page_bytes; const source = self.pages[index * page_bytes ..][0..page_bytes]; @memcpy(destination[start..][0..page_bytes], source); } const restored = try checkpoint.validatedMemoryDigest( destination, self.material.immutable_image, ); if (!std.meta.eql(restored, self.memory)) { return error.MemoryDigestMismatch; } return .{ .material = self.material, .copied_pages = self.dirtyPageCount(), }; } /// The function returns the durable parent's complete normalized memory. pub fn evidenceRam( self: *const @This(), ) []align(page_bytes) const u8 { return self.parent.ram; } /// The function answers whether a byte range touches the snapshot value, /// the parent handle, the storage and memory behind that parent, or the /// captured index and page arrays. pub fn aliases(self: *const @This(), bytes: []const u8) bool { return buffersOverlap(bytes, std.mem.asBytes(self)) or buffersOverlap(bytes, std.mem.asBytes(self.parent)) or buffersOverlap(bytes, &self.parent.storage.bytes) or buffersOverlap(bytes, self.parent.ram) or buffersOverlap(bytes, std.mem.sliceAsBytes(self.indices)) or buffersOverlap(bytes, self.pages); }};Source: lib/machine/src/checkpoint/hot/owner.zig:50
/// The storage holds the caller-owned output arrays for one changed-page/// capture. The value borrows both slices. The caller keeps their addresses and/// captured prefixes unchanged for as long as a snapshot can be used.pub const Storage = struct { capacity: Capacity, indices: []u16, pages: []align(page_bytes) u8, /// The function joins an index slice and a page slice that carry the same /// number of slots. Every index slot has one aligned page behind it in the /// page storage. More than 16,384 indices returns `DeltaCapacityExceeded`. /// Any other page-storage length returns `DeltaStorageMismatch`. pub fn init( indices: []u16, pages: []align(page_bytes) u8, ) Error!Storage { const capacity = try Capacity.derive(indices.len); if (pages.len != capacity.page_storage_bytes) { return error.DeltaStorageMismatch; } return .{ .capacity = capacity, .indices = indices, .pages = pages, }; }};Source: lib/machine/src/checkpoint/canon/digest.zig:13
pub const page_count: usize = core.layout.ram_bytes / page_bytes;Source: lib/machine/src/checkpoint/hot/owner.zig:13
/// The error set combines the failures of a durable checkpoint with destination/// aliasing for a snapshot.pub const Error = checkpoint.DurableError || HotError;Source: lib/machine/src/checkpoint/hot/owner.zig:233
/// The function captures ordered changed pages from live memory against a/// durable parent. The material must share the parent's profile fingerprint,/// immutable image descriptor, and execution fingerprint. The supplied storage/// bounds the captured page count, and exceeding it returns/// `DeltaCapacityExceeded`. Failures before the identity is built leave both/// arrays unchanged, and rejection of the identity can leave both arrays/// changed. On success the returned snapshot holds borrowed references to the/// parent and to both output arrays.pub fn capture( parent: *const checkpoint.Checkpoint, material: checkpoint.Material, current: []align(page_bytes) const u8, storage: Storage,) Error!Snapshot { const parent_contents = try checkpoint.inspect(parent); try checkpoint.validateParent(material, parent_contents.material); try validateStorage(storage); try validateStorageAliases(parent, storage); const delta = try checkpoint.captureDelta( parent.ram, current, material.immutable_image, storage.indices, storage.pages, ); const count: usize = delta.page_count; const identity_value = try checkpoint.identify(material, delta.memory); return .{ .parent = parent, .identity_value = identity_value, .memory = delta.memory, .material = material, .indices = storage.indices[0..count], .pages = storage.pages[0 .. count * page_bytes], };}Source: lib/machine/src/checkpoint/hot/owner.zig:45
pub const maximum_capacity = Capacity.derive(page_count) catch unreachable;Source: lib/machine/src/checkpoint/hot/root.zig
//! Recording a running machine often costs too much when every capture copies//! and hashes all 67,108,864 bytes, because an execution changes only a small//! part of that data between two captures. The machine's fixed 67,108,864//! bytes, addressed from zero, holding 16,384 pages, are its *memory*. A memory//! image whose page tables have been rewritten to their canonical form and//! whose boot frame, request ring, event ring, and kernel stack are zeroed is//! *normalized memory*. A checkpoint holding a complete normalized memory image//! in two caller-owned regions is a *durable checkpoint*. A block of 4096//! contiguous bytes of memory, named by its zero-based index, is a *page*. The//! calls here record only the pages that differ from a durable checkpoint//! already written down. A page whose bytes differ from the parent image's page//! at the same index is a *changed page*. Changed pages recorded as strictly//! ascending indices with one matching 4096-byte page record per index are//! *ordered changed pages*. A checkpoint holding ordered changed pages against//! a durable parent is a *hot snapshot*.//!//! A partial record is worth having only when it still yields the name the full//! image would carry. This code takes from no outside source: it takes its//! digests from the sibling digest schema inside this package. The complete//! binary tree over the 16,384 page digests, 14 levels deep, is the *page//! tree*. The SHA-256 identity of a normalized memory image, binding the page//! tree's root to the fixed memory geometry, is the *memory digest*. Folding//! the changed pages into the same page tree produces the same memory digest as//! the full image.//!//! A snapshot borrows its parent handle and two caller-owned arrays, one for//! ordered page indices and one for the matching page bytes. The caller's array//! length fixes how many changed pages fit, and a capture that would exceed it//! leaves both arrays unchanged.//!//! The memory image of a running machine, before normalization, is its *live//! memory*. Capture checks the parent relationship and the live memory before//! it writes anything into those arrays.//!//! A page table, boot frame, request ring, event ring, or stack page, which a//! capture takes from the parent, is a *retained page*. Page tables, the boot//! frame, the transport rings, and the stack keep their parent bytes, so//! per-run state never enters a snapshot as a changed page.//!//! Restore starts from the parent's memory already present in the destination,//! and the snapshot requires that memory to hash to the parent's memory digest//! before it writes anything.//!//! Restore then lays each changed page over that memory and hashes the result,//! comparing it with the memory digest the snapshot recorded.const owner = @import("owner.zig");pub const Error = owner.Error;pub const Capacity = owner.Capacity;pub const Materialized = owner.Materialized;pub const Snapshot = owner.Snapshot;pub const Storage = owner.Storage;pub const capture = owner.capture;pub const maximum_capacity = owner.maximum_capacity;pub const page_bytes = owner.page_bytes;pub const page_count = owner.page_count;Source: lib/machine/src/checkpoint/root.zig:90
pub const hot = @import("hot/root.zig");Audit
| Definitions | 18 |
|---|---|
| Public names | 20 |
| Members | 14 |
| Version | 26.7.0 |
| Revision | daab053ee433 |