Skip to content

fix(trace): read each guest trace batch once - #1991

Open
ludfjig wants to merge 1 commit into
hyperlight-dev:mainfrom
ludfjig:fix-dup-guest-traces-v2
Open

ludfjig wants to merge 1 commit into
hyperlight-dev:mainfrom
ludfjig:fix-dup-guest-traces-v2

Conversation

@ludfjig

@ludfjig ludfjig commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

The guest reports a trace batch by setting r8 to the TraceBatch marker, with the buffer pointer in r9 and its length in r10. Nothing cleared r8, and the host checked it on every exit. The Halt after each flush re-read the same buffer, so every span in it was exported twice (#1230).

On main this is hidden by accident. #1794 and #1891 added code between the last flush and the halt that happens to overwrite r8. Any change there can bring the duplicates back.

This PR makes the guest clear r8 after each OUT that carries a batch, and makes the host check for a batch only on OUT exits. Either half prevents the duplicates alone. Checked by counting exported spans before #1891 and on v0.12.0. No automated test, because CI does not build trace-enabled guests.

Replaces #1619. Closes #1230

@ludfjig ludfjig added the kind/bugfix For PRs that fix bugs label Oct 28, 2026
Copilot AI balanced review requested due to automatic review settings October 28, 2026 19:22
@ludfjig ludfjig added the kind/bugfix For PRs that fix bugs label Oct 28, 2026
The host checked r8 for the TraceBatch marker on every exit, and
nothing cleared it. The Halt after each flush re-read the same buffer,
so every span in it was exported twice.

Clear r8 in the guest after each OUT that carries a batch, and check
for a batch on the host only on OUT exits.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
@ludfjig
ludfjig force-pushed the fix-dup-guest-traces-v2 branch from 76d0f03 to 0c48193 Compare October 28, 2026 19:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The user-visible trace_guest duplicate-span fix is missing a release note in the prerelease Fixed section of CHANGELOG.md.

1 open issue
What changed in this PR

This PR prevents duplicate guest spans by ensuring each trace batch is consumed once across Hyperlight’s guest-to-host tracing boundary.

Changes:

  • Clear the trace marker after both guest trace-delivery paths.
  • Restrict host trace-batch checks to OUT exits.
  • Document the register protocol and exit handling.
File Description
src/​hyperlight_host/​src/​hypervisor/​hyperlight_vm/​mod.rs Checks for trace batches only on OUT exits.
src/​hyperlight_guest/​src/​arch/​amd64/​exit.rs Clears r8 after sending piggybacked trace data.
src/​hyperlight_guest_tracing/​src/​state.rs Clears r8 after dedicated trace-batch sends.
docs/​hyperlight-metrics-logs-and-traces.md Describes OUT-only delivery and marker clearing.

Review effort: Balanced

});
// The guest reports trace batches only on OUT exits. Other exits
// can hold stale registers and must not be checked.
if let Ok(VmExit::IoOut(..)) = &result {
@hyperlight-gh-bot

Copy link
Copy Markdown

Benchmark Results

Measured commit: 0c481935f6b7
Baseline commit: 17ff5c027947

kvm / amd (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
373.16 µs (➖ 1.00x faster)
byte_chunks 755.40 ns (➖ 1.05x faster)
vec_bytes 578.81 ns (➖ 1.00x faster)

payload_allocation

slot_pool_segmented
65536 142.32 ns (➖ 1.01x slower)
262144 522.92 ns (➖ 1.03x faster)

sandboxes

create_initialized_and_drop
medium 82.57 ms (➖ 1.02x slower)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
7.68 ns (➖ 1.09x faster) 7.68 ns (➖ 1.00x faster) 7.68 ns (➖ 1.04x faster)

snapshot_files

load_snapshot_unverified
small 93.42 µs (➖ 1.00x faster)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 1.94 µs (➖ 1.07x faster) 2.04 µs (➖ 1.07x slower)
262144 9.45 µs (➖ 1.35x slower) 8.58 µs (➖ 1.21x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 1.03 µs (➖ 1.03x faster) 1.00 µs (➖ 1.00x slower)
65536 6.24 µs (➖ 1.01x faster) 7.10 µs (➖ 1.07x slower)
262144 28.47 µs (➖ 1.04x slower)
kvm / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
712.05 µs (➖ 1.05x faster)
byte_chunks 719.53 ns (➖ 1.06x faster)
vec_bytes 550.74 ns (➖ 1.06x faster)

payload_allocation

slot_pool_segmented
65536 195.71 ns (➖ 1.09x slower)
262144 564.25 ns (➖ 1.07x slower)

sandboxes

create_initialized_and_drop
medium 79.68 ms (➖ 1.02x faster)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
7.72 ns (➖ 1.11x slower) 7.72 ns (➖ 1.11x slower) 7.74 ns (➖ 1.11x slower)

snapshot_files

load_snapshot_unverified
small 46.99 µs (➖ 1.06x faster)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 2.42 µs (➖ 1.12x slower) 2.43 µs (➖ 1.08x slower)
262144 8.62 µs (➖ 1.09x slower) 8.63 µs (➖ 1.09x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 834.48 ns (➖ 1.10x slower) 837.84 ns (➖ 1.07x slower)
65536 8.01 µs (➖ 1.04x slower) 8.02 µs (➖ 1.05x slower)
262144 34.16 µs (➖ 1.09x slower)
mshv3 / amd (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
364.37 µs (➖ 1.14x slower)
byte_chunks 927.88 ns (➖ 1.01x slower)
vec_bytes 704.30 ns (➖ 1.00x slower)

payload_allocation

slot_pool_segmented
65536 182.60 ns (➖ 1.00x slower)
262144 672.29 ns (➖ 1.00x slower)

sandboxes

create_initialized_and_drop
medium 64.68 ms (➖ 1.10x slower)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
9.88 ns (➖ 1.02x faster) 9.91 ns (➖ 1.00x slower) 9.89 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 87.77 µs (➖ 1.02x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 2.47 µs (➖ 1.01x faster) 2.71 µs (➖ 1.02x slower)
262144 9.08 µs (➖ 1.03x faster) 9.31 µs (➖ 1.03x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 1.26 µs (➖ 1.00x faster) 1.27 µs (➖ 1.03x faster)
65536 7.91 µs (➖ 1.01x faster) 7.98 µs (➖ 1.01x faster)
262144 34.52 µs (➖ 1.05x faster)
mshv3 / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
661.32 µs (➖ 1.07x faster)
byte_chunks 895.59 ns (➖ 1.00x slower)
vec_bytes 645.82 ns (➖ 1.01x faster)

payload_allocation

slot_pool_segmented
65536 219.13 ns (➖ 1.16x faster)
262144 670.27 ns (➖ 1.01x slower)

sandboxes

create_initialized_and_drop
medium 64.80 ms (➖ 1.04x faster)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
8.69 ns (➖ 1.00x faster) 8.70 ns (➖ 1.00x faster) 9.95 ns (➖ 1.14x slower)

snapshot_files

load_snapshot_unverified
small 44.09 µs (➖ 1.05x faster)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 2.29 µs (➖ 1.03x faster) 2.27 µs (➖ 1.01x faster)
262144 7.93 µs (➖ 1.01x faster) 7.98 µs (➖ 1.00x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 906.41 ns (➖ 1.02x faster) 920.87 ns (➖ 1.01x faster)
65536 7.37 µs (➖ 1.02x faster) 7.56 µs (➖ 1.00x faster)
262144 41.56 µs (➖ 1.11x slower)
hyperv-ws2025 / amd (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
1.85 ms (➖ 1.15x faster)
byte_chunks 1.13 µs (➖ 1.03x faster)
vec_bytes 764.07 ns (➖ 1.05x faster)

payload_allocation

slot_pool_segmented
65536 234.32 ns (➖ 1.01x faster)
262144 809.32 ns (➖ 1.02x slower)

sandboxes

create_initialized_and_drop
medium 79.67 ms (➖ 1.03x faster)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
10.63 ns (➖ 1.00x faster) 10.66 ns (➖ 1.03x faster) 10.61 ns (➖ 1.01x faster)

snapshot_files

load_snapshot_unverified
small 604.02 µs (➖ 1.18x faster)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 2.51 µs (➖ 1.10x faster) 2.53 µs (➖ 1.01x faster)
262144 8.53 µs (➖ 1.10x faster) 8.59 µs (➖ 1.02x faster)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 1.36 µs (➖ 1.03x faster) 1.37 µs (➖ 1.01x slower)
65536 8.96 µs (➖ 1.02x faster) 8.86 µs (➖ 1.06x faster)
262144 38.55 µs (➖ 1.20x faster)
hyperv-ws2025 / intel (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

decode_vec_bytes_copy encode_control
2.85 ms (➖ 1.02x faster)
byte_chunks 1.19 µs (➖ 1.01x slower)
vec_bytes 770.86 ns (➖ 1.01x faster)

payload_allocation

slot_pool_segmented
65536 212.93 ns (➖ 1.05x faster)
262144 740.69 ns (➖ 1.06x faster)

sandboxes

create_initialized_and_drop
medium 97.77 ms (➖ 1.07x faster)

slot_pool

alloc_dealloc_4096 alloc_dealloc_128 alloc_dealloc_1500
10.31 ns (➖ 1.03x faster) 9.69 ns (➖ 1.04x faster) 9.63 ns (➖ 1.08x faster)

snapshot_files

load_snapshot_unverified
small 613.48 µs (➖ 1.01x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
65536 2.44 µs (➖ 1.04x slower) 2.30 µs (➖ 1.02x faster)
262144 7.46 µs (➖ 1.03x faster) 7.64 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
8192 1.09 µs (➖ 1.03x slower) 1.19 µs (➖ 1.10x slower)
65536 8.37 µs (➖ 1.01x faster) 8.32 µs (➖ 1.01x faster)
262144 41.31 µs (➖ 1.22x faster)

Reported by cargo ci bench-report --candidate run:40754091203 --config-file bench_report.toml.

@ludfjig

ludfjig commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

@dblnz maybe you could take a look at this one since you reviewed the old pr this replaces?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bugfix For PRs that fix bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix duplicate guest traces

2 participants