Repository navigation
Port the time tools to Rust - #550
Merged
Pedro Henrique Penna (ppenna) merged 4 commits intoOct 16, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The large stacked change includes privileged, concurrency-sensitive timekeeping code and still lacks the deferred KVM, MSHV, and WHP host validation.
0 open findings
What changed in this PR
Ports nvx-time and nvx-time-probe to Rust while preserving the existing C ABI and behavior; the stacked PR also contains earlier guest-tool ports.
Changes:
- Implements time repair, monitoring, diagnostics, latency, and warp tooling in Rust.
- Adds extensive parity and guest-binary tests.
- Builds, lints, and validates static musl executables in CI.
| File | Description |
|---|---|
.github/actions/check-nvx-rs/action.yml |
Adds musl guest validation. |
doc/ci.md |
Documents guest CI checks. |
doc/contribute.md |
Updates contributor guidance. |
doc/project-structure.md |
Documents Rust guest modules. |
doc/usage.md |
Updates guest-tool usage. |
nvx-rs/Cargo.toml |
Registers guest binaries and profile. |
nvx-rs/README.md |
Documents guest tooling. |
nvx-rs/src/bin/nvx-console-pending.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-device-io.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-exit.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-ingress.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-managed-agent.rs |
Adds agent entry point. |
nvx-rs/src/bin/nvx-mmio-write.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-port-io.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-reseed.rs |
Adds binary entry point. |
nvx-rs/src/bin/nvx-time-probe.rs |
Adds probe entry point. |
nvx-rs/src/bin/nvx-time.rs |
Adds time-tool entry point. |
nvx-rs/src/guest/agent/cgroup.rs |
Implements cgroup containment. |
nvx-rs/src/guest/agent/diagnostics.rs |
Implements agent diagnostics. |
nvx-rs/src/guest/agent/dispatch.rs |
Implements request dispatch. |
nvx-rs/src/guest/agent/exec.rs |
Implements workload execution. |
nvx-rs/src/guest/agent/exec_config.rs |
Handles execution configuration. |
nvx-rs/src/guest/agent/helper.rs |
Implements launch helper. |
nvx-rs/src/guest/agent/launch.rs |
Implements process launching. |
nvx-rs/src/guest/agent/loopback.rs |
Adds loopback integration coverage. |
nvx-rs/src/guest/agent/mappings.rs |
Implements host mappings. |
nvx-rs/src/guest/agent/mod.rs |
Wires agent modules. |
nvx-rs/src/guest/agent/seam.rs |
Adds privileged-operation seam. |
nvx-rs/src/guest/agent/session.rs |
Implements control sessions. |
nvx-rs/src/guest/agent/tty.rs |
Implements control TTY handling. |
nvx-rs/src/guest/console_pending.rs |
Ports console query tool. |
nvx-rs/src/guest/freestanding/args.rs |
Parses freestanding arguments. |
nvx-rs/src/guest/freestanding/device_io.rs |
Ports device benchmark. |
nvx-rs/src/guest/freestanding/exit.rs |
Implements guest exit. |
nvx-rs/src/guest/freestanding/hosted.rs |
Supports hosted testing. |
nvx-rs/src/guest/freestanding/mem.rs |
Provides memory primitives. |
nvx-rs/src/guest/freestanding/mod.rs |
Wires freestanding modules. |
nvx-rs/src/guest/freestanding/start.rs |
Implements process startup. |
nvx-rs/src/guest/freestanding/syscall.rs |
Provides raw syscalls. |
nvx-rs/src/guest/ingress/address.rs |
Parses ingress addresses. |
nvx-rs/src/guest/ingress/collect.rs |
Collects listening sockets. |
nvx-rs/src/guest/ingress/diag.rs |
Parses socket diagnostics. |
nvx-rs/src/guest/ingress/encode.rs |
Encodes announcements. |
nvx-rs/src/guest/ingress/mod.rs |
Wires ingress behavior. |
nvx-rs/src/guest/ingress/sender.rs |
Implements live announcements. |
nvx-rs/src/guest/ingress/table.rs |
Parses socket tables. |
nvx-rs/src/guest/ingress/test_mode.rs |
Adds ingress test mode. |
nvx-rs/src/guest/mmio_write.rs |
Ports MMIO writer. |
nvx-rs/src/guest/mod.rs |
Exposes guest modules. |
nvx-rs/src/guest/parse.rs |
Adds byte-oriented parsing. |
nvx-rs/src/guest/port_io.rs |
Ports port-I/O tool. |
nvx-rs/src/guest/reseed.rs |
Ports reseeding tool. |
nvx-rs/src/guest/sys.rs |
Provides guest system wrappers. |
nvx-rs/src/guest/time/checks.rs |
Implements time checks. |
nvx-rs/src/guest/time/commands.rs |
Implements time commands. |
nvx-rs/src/guest/time/daemon.rs |
Implements time daemon behavior. |
nvx-rs/src/guest/time/decoders.rs |
Decodes ABI records. |
nvx-rs/src/guest/time/events.rs |
Formats violation events. |
nvx-rs/src/guest/time/mod.rs |
Wires the time tool. |
nvx-rs/src/guest/time/state.rs |
Persists time state. |
nvx-rs/src/guest/time/system.rs |
Implements privileged operations. |
nvx-rs/src/guest/time/test_mode.rs |
Adds parity test hooks. |
nvx-rs/src/guest/time_probe/clock.rs |
Implements clock diagnostics. |
nvx-rs/src/guest/time_probe/format.rs |
Preserves C-style formatting. |
nvx-rs/src/guest/time_probe/latency.rs |
Implements latency probing. |
nvx-rs/src/guest/time_probe/mod.rs |
Dispatches probe commands. |
nvx-rs/src/guest/time_probe/support.rs |
Provides probe utilities. |
nvx-rs/src/guest/time_probe/warp.rs |
Implements warp detection. |
nvx-rs/src/guest/x86.rs |
Adds x86 guest primitives. |
nvx-rs/src/lib.rs |
Exposes guest functionality. |
nvx-rs/src/protocol/boot.rs |
Extends boot parsing. |
nvx-rs/src/protocol/exec.rs |
Adds guest EXEC decoding. |
nvx-rs/src/protocol/features.rs |
Adds guest feature masks. |
nvx-rs/src/protocol/maps.rs |
Adds mapping protocol support. |
nvx-rs/src/protocol/mod.rs |
Exposes protocol additions. |
nvx-rs/src/protocol/outer.rs |
Extends outer records. |
nvx-rs/src/protocol/tests.rs |
Adds protocol coverage. |
nvx-rs/tests/guest_tools.rs |
Exercises real guest binaries. |
scripts/nvx.py |
Exposes ELF validation command. |
scripts/nvx_tools/elf.py |
Validates static ELF binaries. |
scripts/test_nvx_tools.py |
Tests CLI and ELF validation. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 14, 2026 23:23
ea74389 to
ce99b8d
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 14, 2026 23:37
ce99b8d to
cfc0dd6
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 15, 2026 02:30
cfc0dd6 to
0066c97
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 15, 2026 05:41
0066c97 to
385635c
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 15, 2026 05:53
385635c to
b24e8ae
Compare
This was referenced Oct 15, 2026
Pedro Henrique Penna (ppenna)
marked this pull request as draft
October 15, 2026 18:17
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 16, 2026 16:33
b24e8ae to
98a35f1
Compare
This was referenced Oct 16, 2026
Port nvx-time, the guest side of NVX time ABI v1, as a static Rust guest binary that keeps the C program's command lines, exit statuses 0, 1, and 2, power-off statuses 193, 194, and 195, markers, output, state and run files, scalar port transactions, deferred work, and timing boundaries. Split the program by concern: pure decoders of the restore packet, samples, the kernel log, timer list, CPU lists, command line, clock corrections, and frequencies; event records; the time state; the conformance checks; the commands; the background daemon; the system boundary; and the C program's test hooks. Follow the C program's rename of restore step 13 to the deferred restore, repair step 7, in names and in the deferred-restore-cpu test hook. Follow the guest programs' conventions. The program fails with the shared CommandError and exits with the shared statuses, and its diagnostics describe errno values with nix's text. The capture request is plain decimal and the exhaustive-leaf hook's CPUID values plain decimal or 0x-prefixed hexadecimal, while the other test hooks keep the C program's strtoll and strtoull reading, which their compatibility tests compare. Every port, path, file mode, limit, time constant, status bit, CPUID leaf and bit, MSR, and Hyper-V identity is named once. Extend nvx::guest::sys with the safe operations that the program needs, through nix wherever it provides them: timerfd and signalfd owners, flock, absolute monotonic sleeps, renameat and unlinkat, polling, CPU affinity, fork, setsid and dup2 for detaching, private mount namespaces, and tmpfs mounts. Reach libc through nix's re-export only for the calls that nix does not wrap, ioperm, adjtimex, klogctl, sigqueue, and _exit, for sched_setscheduler, which musl does not implement for one thread, and for the child waits that keep real-time signals, as the managed agent's do. CPUID runs through its core::arch intrinsic, and the port instructions, in sys::port only, as inline assembly. Give every resource and every temporary change an owner that releases or restores it when dropped: port permissions, MSR files, file locks, created files, which disappear unless kept or renamed, scratch directories, tmpfs mounts, blocked signal masks, CPU affinity, the idle scheduling policy, which only the thread that entered it leaves, the forced rcu_normal setting, and the thread that promotes idle threads. sys::fork refuses a process that has other threads, and the program forks only while it has one. Port the C test hooks with golden unit coverage, and test the real binary through NVX_GUEST_BINARIES. Part of #358. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port nvx-time-probe's latency, pairwise warp, and clock diagnostics as a static Rust guest binary that keeps the C program's command lines, version, tables, markers, and exit statuses: 0 on success, 1 when the warp check fails, 2 for usage errors, and 3 when a diagnostic cannot prepare or run its measurement. Split the program into option and kernel-input support, the three diagnostics, and number formatting. The TSC rate still comes from --tsc-hz, sysfs, the kernel log, cpuinfo, or a measurement against CLOCK_MONOTONIC_RAW, in that order. Run each warp pair and ping-pong round on scoped threads pinned to their CPUs, with 64-byte-aligned atomics and the C program's acquire, release, and sequentially consistent orderings. Format measured values with Rust's exact fixed-precision formatting and C's spellings of NaN, and render UTC times with the proleptic Gregorian civil-date conversion rather than gmtime. Follow the guest programs' conventions. Integer options are plain decimal or 0x-prefixed hexadecimal, as C's base-0 strtoull read them, rates are finite plain decimals with an optional exponent, which a new shared parse_f64 reads, and CPU lists are strict, so that the signed, octal, padded, infinite, hexadecimal-float, and duplicate forms that C accepted are usage errors. The kernel's own numbers, such as its TSC rate and clock parameters, are read only in the forms that the kernel writes. Extend nvx::guest::sys with the probe's operations: the time-stamp counter reads that the latency table compares, RDTSC, RDTSCP, and LFENCE;RDTSC, and the fenced read that brackets a timed region, as core::arch intrinsics; PAUSE through std::hint::spin_loop; the clock_gettime and getpid system calls without the vDSO's or libc's shortcuts; getauxval for the vDSO; sched_yield; the calling thread's affinity mask and CPU; the raw and coarse monotonic clocks; a one-shot sleep; the TSC frequency and lockup-detector paths; and the CPUID leaves, bits, display signature, and MSRs that the clock report prints. The optional MSR dump reads /dev/cpu/0/msr through the shared MsrFile owner and reports a faulting read as a #GP. Cover the formats, the strict option and kernel-number parsers, CPU lists, percentile ranks, rounding, the shared statuses, and the aligned atomics, and test the real binary's usage, version, option errors, and small diagnostics. Part of #358. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add nvx-time and nvx-time-probe to the Rust guest-binary inventory. The existing nvx.py build-guest-binaries and test-guest-binaries commands now build their static musl executables in the same Cargo build as the other guest programs, check their ELF form, include their digests in the manifest, and run their prebuilt integration tests. A build adds both programs, as it adds nvx-managed-agent and nvx-ingress, to an earlier output whose manifest does not record them. Test that the immediate predecessor's output, which lacks only the two time tools, upgrades in place. Document both command lines, and count nine binaries in the command documentation. The guest-image build remains unchanged until the final switch. Part of #358. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 16, 2026 19:35
98a35f1 to
ac858d8
Compare
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 16, 2026 19:40
ac858d8 to
05bbf16
Compare
Pedro Henrique Penna (ppenna)
marked this pull request as ready for review
October 16, 2026 19:55
The ports translated the C programs nearly line by line: byte strings and sentinel values for state, C's fixed buffers, the leniency of strtoll and scanf, and wrapping arithmetic. Model them with Rust's types, nix, and the standard library instead, keeping the output, exit statuses, state file, and port transactions byte-exact for valid input: - Share the kernel's text formats between both tools: /dev/kmsg and syslog records, CPU lists, and /proc/timer_list. This also fixes the probe's timer-list summary, which printed handler=none hres_active=-1 nohz=-1 on real kernels. - Type check IDs, violation codes, event sources, state tokens, CPU sets, restore packets, time samples, and their errors, and replace zero and -1 sentinels with Option. - Return the checks' failures as values that ? propagates. - Use TimeSpec, Instant, Duration, std::fs, channels, black_box, split_first_chunk, and div_euclid instead of hand-written versions. - Specialize the latency probe's timing loop per method, as C hoisted its switch out of the loop. - Fail C1 for every CPU that the parallel test hook's jobs cannot run on, after the foreground's failure, as C's check_cpus_parallel did, rather than for the first only. - Test the warp probe's CPU-pair protocol on two CPUs, under a timeout, and its summary's accounting of stalled and inconsistent pairs. The tools now read the kernel's text, their own records, and their test hooks' arguments only in the form in which they are written, UTF-8 text and plain decimal numbers, and no longer reproduce what only C's buffers did to oversized input, such as the 127-byte limit on --new-cpus or the 638-byte cut of an asynchronous console line. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/fix-issue-358-time-tools
branch
from
October 16, 2026 20:05
05bbf16 to
2ac2ce1
Compare
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Broad privileged timing and process changes need human review and post-rewrite in-VM validation, alongside resolution of the output compatibility findings.
2 open findings
1 resolved since last review
🧠 Review effort: Balanced
Comment on lines
+455
to
+458
| let _ = writeln!( | ||
| output, | ||
| "msr {index:#010x} {name:<34} = {value:#x} ({value})" | ||
| ); |
Contributor
Author
Comment on lines
+648
to
+663
| let _ = writeln!( | ||
| output, | ||
| "NVX-TIME-PROBE clock clocksource={} available={available} tsc_hz={} tsc_hz_source={} \ | ||
| hv_vendor={} hv_max_leaf={:#x} hv_features_eax={:#x} hv_features_edx={:#x} \ | ||
| flags={present} missing_flags={absent} tsc_unstable_msgs={} unchecked_msr_msgs={} {}", | ||
| String::from_utf8_lossy(&clocksource), | ||
| f0(rate.hz), | ||
| rate.source, | ||
| sanitize_token(&ids.vendor), | ||
| ids.max_leaf, | ||
| ids.features_eax, | ||
| ids.features_edx, | ||
| time.tsc_unstable, | ||
| time.unchecked_msr, | ||
| timer_summary | ||
| ); |
Contributor
Author
This was referenced Oct 16, 2026
Pedro Henrique Penna (ppenna)
deleted the
agents/fix-issue-358-time-tools
branch
October 16, 2026 20:37
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
nvx-time, the guest side of NVX time ABI v1, to safe Rust;nvx-time-probelatency, warp, and clock diagnostics to safe Rust;port transactions, deferred work, and timing boundaries;
nvx.py;Result, and the standardlibrary in place of the C translation's byte strings, sentinels, and buffers.
Part of #358 (PR 4 of 5). Rebased onto
dev(11c7a0f42), which merged #533,so the four commits on top are this PR's.
Guest conventions
The tools are reworked onto the
nvx::guestconventions that #531 and #533established:
Every system call goes through
nix, or through the standard library, whichreads and writes whole files and the MSR devices. libc is reached through
nix::libconly for calls thatnixdoes not wrap (ioperm,adjtimex,klogctl,getauxval,sigqueue, and_exit), forsched_setscheduler,which musl does not implement for one thread, for the probe's raw
clock_gettimeandgetpidsystem calls, and for child waits that keepreal-time signals.
guest::sysdocuments each.Errors are
nix'sErrnowith its descriptions. Both programs fail with theshared
CommandErrorand exit with the sharedExitStatus.System-specific values are named once.
sys::kernelholds the kernel paths,sys::PortOpenVMM's ports,sys::x86the CPUID leaves, bits, and MSRs, andsys::ClockAdjustmenttheadjtimexmodes and status bits. The programsname their own limits, file modes, and time constants.
Owners release or restore every resource and temporary change when dropped:
flocklocks;rcu_normalsetting;sys::forkrefuses a process that has other threads, and every fork point issingle-threaded.
test promoteandtest deferred-restore-cpudrive theproduction promoter and deferred-restore fork.
CPUID and the TSC reads use
core::archintrinsics. Onlysys::portusesinline assembly, for its scalar
inb,inl, andoutb. The probe formatswith Rust's exact fixed-precision formatting and C's NaN spellings, rather
than libc
snprintf.Input is strict:
0xhexadecimal where C read base 0, with a-sign where a test hook's number may be negative;names, codes, and sources are read only in the form in which they are
written.
The tools do not reproduce what only C's buffers and lenient parsers did to
oversized or malformed input, such as the 127-byte limit on
--new-cpus.guest/mod.rsdocuments the rule, and C's parity fixtures pass unchanged.The port follows
dev's rename of restore step 13 to the deferred restore(repair step 7), including the
deferred-restore-cputest hook.Idiomatic Rust
The ports first translated the C programs nearly line by line. The fourth
commit rewrites them in Rust's terms, keeping the output, exit statuses, state
file, and port transactions byte-exact for valid input:
guest::kernel_textparses the kernel's text formats for both tools:/dev/kmsgandsyslog(2)records, CPU lists, and/proc/timer_list. Thisfixes the probe's timer-list summary, which printed
handler=none hres_active=-1 nohz=-1on real kernels.packets, time samples, and their errors are types with
Display, andOptionreplaces the zero and -1 sentinels.?propagates.TimeSpec,Instant,Duration,std::fs, channels,black_box,split_first_chunk, anddiv_euclidreplace hand-written versions.method's dispatch out of the measured loop, as in C.
Follow-ups
The latest review found two places where
nvx-time-probe clockprints zero as0x0where C's%#xprinted0, as the port has since its first revision:nvx-time-probe clock --msras C's%#xdoes #589: the MSR values ofclock --msr;nvx-time-probe clockas C's%#xdoes #590: the hypervisor's CPUID fields.Validation
This revision (
2ac2ce1a2)In WSL Linux, on the rebased head:
check-nvx-rsstep:check-guest-binaries, with 254 library and 33 guest-tool tests,including a two-CPU warp run;
build-guest-binariesandtest-guest-binaries.nvx-timeis 623,184bytes (
df4fca77...), andnvx-time-probeis 561,808 bytes(
4f0d22e5...).check-aci-edge-sandboxesstep, includingtest-agent-bundling.test_guest_time.py's C fixtures against the musl debug andguest-profile
nvx-time, and its new two-unusable-CPUparallelcaseagainst the C program.
and
dev's Markdown link check.nvx.py verify, which needs an OpenVMM checkout, the Windowschecks, and every in-VM run. The results below predate the rewrite.
Previous revision (
98a35f1d5, before the rewrite)Windows and WSL
check-nvx-rscommand on Windows and WSL Linux:check-guest-binaries, with 234 library and 32 guest-tool tests.check-aci-edge-sandboxescommand on Windows, includingtest-agent-bundling.nvx.py verify, the Windowsvalidate-nvxlist (1,080 unit tests),Ruff check and format, compileall, Linux and Windows Pyright, WSL
test_guest_binaries.pyandtest_guest_time.py, anddev's markdown linkcheck on the merge with
dev.nvx-rscommits also passed the full WSL check (Clippy, musl, tests, and rustdoc)
before the rebase onto Port nvx-ingress to Rust #533's final head, which changed only
ingress/table.rs.build-guest-binariesruns were byte-identical and passed all 32prebuilt tests.
nvx-timeis 610,896 bytes (2344e4b3...), andnvx-time-probeis 541,328 bytes (2d6bbed9...).In-VM on WHP
The runs used a local Windows/WHP host with an Intel Core i9-12900H, and
OpenVMM
c6d97c529with guest images fromv0.1.0-dev.f429da852efa. Both Rusttime tools were overlaid in copies of the guest images.
test-microvmscenario, plusmanaged-exec-configand
sandbox-lifecycle. This covers:exhaustive=ok/8/0, 29 warpruns with a maximum offset of 23 ns and no backward steps, and every boot
and restore within its CPU budget.
nvx-time status, probe latency on two CPUs, a two-CPU warp(
PASS), the clock report, and the optional MSR dump.Not repeated for that revision
The bare-metal KVM and MSHV in-VM runs, the generated C/old/new differential
comparisons, and the boot, capture, and restore timing table were not repeated.