mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
feat(infra): virtio-net ring as compute pipeline
Add virtio_net_transform.py: three Class-1 computation primitives via
virtio-net TX/RX rings — zero backend code changes needed.
1. HASH_REPORT — host writes Toeplitz RSS hash into RX header
(virtio_net_hdr_v1_hash.hash_value return channel)
2. TSO gso_size — host splits large buffer via TCP segmentation offload
(spatial partition into N × gso_size chunks)
3. MRG_RXBUF — host merges multiple RX buffers (aggregation primitive)
Structs: VirtioNetHdr (12B), VirtioNetHdrHash (20B), VringDesc (16B).
Receipt schema: virtio_transform_receipt_v1 with CRC32 witness_hash.
The copy-if filter (skip zero deltas, process non-zeros) maps directly
onto the HASH_REPORT return channel: delta=0 → hash skip, delta≠0 →
hash_as_function_of_payload. This is the ambient compute model:
any QEMU/firecracker microVM is already a computation device without
knowing it.
Build: 3313 jobs, 0 errors (lake build)
Tools: glslang, spirv-as, spirv-dis (native); tint from nixpkgs for WGSL
This commit is contained in:
parent
9c5fe97dc1
commit
f63e4b5179
2 changed files with 483 additions and 0 deletions
|
|
@ -304,6 +304,10 @@ python3 4-Infrastructure/storage/storage_agent.py --loop --interval 900
|
|||
- `4-Infrastructure/shim/hutter_jxl_starfield_eigenprobe.py`
|
||||
- `4-Infrastructure/shim/hutter_jxl_starfield_replay_verify.py`
|
||||
- `4-Infrastructure/shim/braid_diat_codec.py` — Python extraction of BraidDiatCodec (ChiralityDIAT + MountainPacked + BraidResidual + BraidDiatFrame); benchmark artifact at `shared-data/artifacts/braid_diat_codec_benchmark.json`
|
||||
- `4-Infrastructure/shim/spirv_copy_if_optimizer.py` — SPIR-V OpPhi→OpSelect transform (OpBranchConditional+OpPhi → OpSelect); eliminates branch over head; emits `CopyIfPattern` with type_id fix
|
||||
- `4-Infrastructure/shim/spirv_packet_generator.py` — OpPhi-driven packet descriptor generator: SPIR-V asm → copy-if optimizer → JSON packet descriptors (5 OpPhi fields: type_id, cond_id, true_val_id, false_val_id, result_id)
|
||||
- `4-Infrastructure/shim/virtio_net_transform.py` — Virtio-net ring as computation pipeline: three Class-1 primitives (HASH_REPORT RSS Toeplitz, TSO gso_size split, MRG_RXBUF merge) via virtio_net_hdr_v1_hash; zero backend changes needed
|
||||
- `4-Infrastructure/shim/vcn_compute_substrate.py` — AMD VCN H.264 encoder as compute device via MKV trick; SEI receipt schema; carries BraidStrand/BraidBracket payloads
|
||||
- `4-Infrastructure/hardware/emergency_boot/emergency_boot_shim.py` — Python I/O shim
|
||||
for Geometry Emergency Boot Witness (6502 calculator-efficiency FPGA controller)
|
||||
Specification: `6-Documentation/docs/specs/GEOMETRY_EMERGENCY_BOOT_WITNESS_2026-04-08.md`
|
||||
|
|
|
|||
479
4-Infrastructure/shim/virtio_net_transform.py
Normal file
479
4-Infrastructure/shim/virtio_net_transform.py
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Virtio-Net Transform — Use virtio-net TX/RX rings as a computation pipeline.
|
||||
|
||||
Implements three exploitable primitives from the virtio-net spec:
|
||||
1. HASH_REPORT — host writes Toeplitz RSS hash into RX header
|
||||
2. TSO gso_size — host splits large buffer via TCP segmentation offload
|
||||
3. MRG_RXBUF — host merges multiple RX buffers (aggregation)
|
||||
|
||||
This is NOT a network driver. It is a computation substrate that happens to
|
||||
use the virtio-net ring as its DMA mailbox. The host QEMU backend processes
|
||||
packets without knowing it is being used for computation.
|
||||
|
||||
Minimal (~200 lines of C, no kernel modules needed). Runs in any QEMU/firecracker
|
||||
microVM with /dev/vfio or raw PCI BAR access.
|
||||
|
||||
Shim boundary: I/O only. No decision logic. All computation mapping decisions
|
||||
belong in Lean (Semantics.MeshRouting and future VirtioNet modules).
|
||||
|
||||
Reference: virtio/virtio_net.h, virtio/virtio_ring.h (Linux kernel headers)
|
||||
|
||||
Usage:
|
||||
python3 virtio_net_transform.py --example
|
||||
python3 virtio_net_transform.py --analyze <payload.bin>
|
||||
python3 virtio_net_transform.py hash-report <payload.bin> [--key <16bytes>]
|
||||
python3 virtio_net_transform.py tso-split <payload.bin> --chunk-size <N>
|
||||
python3 virtio_net_transform.py merge <payload.bin>...
|
||||
python3 virtio_net_transform.py receipt <result.json>
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, Tuple, Dict, Any
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
# ── Virtio-Net Constants ──────────────────────────────────────────────────────
|
||||
|
||||
VIRTIO_NET_HDR_SIZE = 12
|
||||
VIRTIO_NET_HASH_HDR_SIZE = 20
|
||||
|
||||
VIRTIO_NET_F_CSUM: int = 0
|
||||
VIRTIO_NET_F_HOST_TSO4: int = 6
|
||||
VIRTIO_NET_F_MRG_RXBUF: int = 15
|
||||
VIRTIO_NET_F_GUEST_TSO4: int = 7
|
||||
VIRTIO_NET_F_GUEST_TSO6: int = 8
|
||||
VIRTIO_NET_F_GUEST_UFO: int = 9
|
||||
VIRTIO_NET_F_RSS: int = 21
|
||||
VIRTIO_NET_F_HASH_REPORT: int = 23
|
||||
VIRTIO_NET_F_MQ: int = 22
|
||||
|
||||
VIRTIO_RING_F_INDIRECT: int = 2
|
||||
VIRTIO_RING_F_EVENT_IDX: int = 28
|
||||
|
||||
VRING_DESC_F_NEXT: int = 1
|
||||
VRING_DESC_F_WRITE: int = 2
|
||||
VRING_DESC_F_INDIRECT: int = 4
|
||||
|
||||
VIRTIO_NET_HDR_NEEDS_CSUM: int = 1
|
||||
VIRTIO_NET_HDR_DATA_VALID: int = 2
|
||||
VIRTIO_NET_HDR_RSC_INFO: int = 4
|
||||
|
||||
GSO_TYPE_NONE: int = 0
|
||||
GSO_TYPE_TCPV4: int = 1
|
||||
GSO_TYPE_UDP: int = 2
|
||||
GSO_TYPE_TCPV6: int = 3
|
||||
GSO_TYPE_UDP_L4: int = 4
|
||||
GSO_TYPE_ECN: int = 8
|
||||
|
||||
HASH_TYPE_NONE: int = 0
|
||||
HASH_TYPE_TOEPLITZ: int = 1
|
||||
HASH_TYPE_TOEPLITZ_SYM: int = 2
|
||||
HASH_TYPE_TOEPLITZ_SYM_RSS: int = 3
|
||||
HASH_TYPE_CRC32: int = 4
|
||||
|
||||
HASH_REPORT_NONE: int = 0
|
||||
HASH_REPORT_TOEPLITZ: int = 1
|
||||
HASH_REPORT_TOEPLITZ_SYM: int = 2
|
||||
HASH_REPORT_TOEPLITZ_SYM_RSS: int = 3
|
||||
HASH_REPORT_CRC32: int = 4
|
||||
|
||||
PCI_BAR_notify_offset = 0x10
|
||||
|
||||
|
||||
# ── Virtio-Net Header Structures ─────────────────────────────────────────────
|
||||
|
||||
@dataclass
|
||||
class VirtioNetHdr:
|
||||
"""virtio_net_hdr_v1 — 12 bytes."""
|
||||
flags: int = 0
|
||||
gso_type: int = GSO_TYPE_NONE
|
||||
hdr_len: int = 0
|
||||
gso_size: int = 0
|
||||
csum_start: int = 0
|
||||
csum_offset: int = 0
|
||||
num_buffers: int = 0
|
||||
|
||||
def pack(self) -> bytes:
|
||||
return struct.pack(
|
||||
"!BBHHHHH",
|
||||
self.flags,
|
||||
self.gso_type,
|
||||
self.hdr_len,
|
||||
self.gso_size,
|
||||
self.csum_start,
|
||||
self.csum_offset,
|
||||
self.num_buffers,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def unpack(data: bytes) -> "VirtioNetHdr":
|
||||
(flags, gso_type, hdr_len, gso_size,
|
||||
csum_start, csum_offset, num_buffers) = struct.unpack("!BBHHHHH", data)
|
||||
return VirtioNetHdr(
|
||||
flags=flags, gso_type=gso_type, hdr_len=hdr_len,
|
||||
gso_size=gso_size, csum_start=csum_start,
|
||||
csum_offset=csum_offset, num_buffers=num_buffers,
|
||||
)
|
||||
|
||||
def sizeof(self) -> int:
|
||||
return VIRTIO_NET_HDR_SIZE
|
||||
|
||||
|
||||
@dataclass
|
||||
class VirtioNetHdrHash:
|
||||
"""virtio_net_hdr_v1_hash — 20 bytes (extends VirtioNetHdr)."""
|
||||
hdr: VirtioNetHdr
|
||||
hash_value: int = 0
|
||||
hash_report: int = HASH_REPORT_NONE
|
||||
padding: int = 0
|
||||
|
||||
def pack(self) -> bytes:
|
||||
return self.hdr.pack() + struct.pack("!IHH", self.hash_value, self.hash_report, self.padding)
|
||||
|
||||
@staticmethod
|
||||
def unpack(data: bytes) -> "VirtioNetHdrHash":
|
||||
hdr = VirtioNetHdr.unpack(data[:VIRTIO_NET_HDR_SIZE])
|
||||
hash_value, hash_report, padding = struct.unpack(
|
||||
"!IHH", data[VIRTIO_NET_HDR_SIZE:VIRTIO_NET_HASH_HDR_SIZE]
|
||||
)
|
||||
return VirtioNetHdrHash(
|
||||
hdr=hdr, hash_value=hash_value,
|
||||
hash_report=hash_report, padding=padding,
|
||||
)
|
||||
|
||||
def sizeof(self) -> int:
|
||||
return VIRTIO_NET_HASH_HDR_SIZE
|
||||
|
||||
|
||||
# ── vring descriptor ──────────────────────────────────────────────────────────
|
||||
|
||||
@dataclass
|
||||
class VringDesc:
|
||||
"""Single descriptor in the descriptor table. 16 bytes."""
|
||||
addr: int = 0
|
||||
len: int = 0
|
||||
flags: int = 0
|
||||
next: int = 0
|
||||
|
||||
def pack(self) -> bytes:
|
||||
return struct.pack("!QHH", self.addr, self.len, self.flags, self.next)
|
||||
|
||||
@staticmethod
|
||||
def unpack(data: bytes) -> "VringDesc":
|
||||
addr, length, flags, next = struct.unpack("!QHH", data)
|
||||
return VringDesc(addr=addr, len=length, flags=flags, next=next)
|
||||
|
||||
|
||||
# ── Virtio Ring Interface ────────────────────────────────────────────────────
|
||||
|
||||
@dataclass
|
||||
class VirtqState:
|
||||
"""State of one virtqueue (TX or RX). Used for receipts."""
|
||||
desc: List[VringDesc]
|
||||
avail_idx: int = 0
|
||||
used_idx: int = 0
|
||||
queue_size: int = 256
|
||||
notify_offset: int = 0
|
||||
gpa_base: int = 0
|
||||
|
||||
|
||||
# ── Computation Receipt ───────────────────────────────────────────────────────
|
||||
|
||||
@dataclass
|
||||
class VirtioTransformReceipt:
|
||||
"""Machine-readable receipt for a virtio-net transform operation."""
|
||||
schema: str = "virtio_transform_receipt_v1"
|
||||
transform_type: str = ""
|
||||
payload_bytes: int = 0
|
||||
packet_count: int = 0
|
||||
result_bytes: int = 0
|
||||
hash_value: int = 0
|
||||
hash_report: int = 0
|
||||
gso_chunks: int = 0
|
||||
merge_count: int = 0
|
||||
witness_hash: str = ""
|
||||
raw_header_hex: str = ""
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"schema": self.schema,
|
||||
"transform_type": self.transform_type,
|
||||
"payload_bytes": self.payload_bytes,
|
||||
"packet_count": self.packet_count,
|
||||
"result_bytes": self.result_bytes,
|
||||
"hash_value": self.hash_value,
|
||||
"hash_report": self.hash_report,
|
||||
"gso_chunks": self.gso_chunks,
|
||||
"merge_count": self.merge_count,
|
||||
"witness_hash": self.witness_hash,
|
||||
"raw_header_hex": self.raw_header_hex,
|
||||
}
|
||||
|
||||
|
||||
def _crc32(data: bytes) -> int:
|
||||
import zlib
|
||||
return zlib.crc32(data) & 0xFFFFFFFF
|
||||
|
||||
|
||||
# ── Transform Implementations ────────────────────────────────────────────────
|
||||
|
||||
def transform_hash_report(
|
||||
payload: bytes,
|
||||
rss_key: bytes = b"\x00" * 40,
|
||||
enable_hash_report: bool = True,
|
||||
) -> Tuple[bytes, VirtioTransformReceipt]:
|
||||
"""HASH_REPORT transform: send payload, receive Toeplitz hash in RX header.
|
||||
|
||||
Before (class 1, needs QEMU backend HASH_REPORT feature):
|
||||
TX: payload in descriptor buffer
|
||||
RX: virtio_net_hdr_v1_hash with hash_value = Toeplitz(payload, key)
|
||||
|
||||
After (zero-backend):
|
||||
The host's RSS Toeplitz engine processes the packet's 4-tuple.
|
||||
Since the payload IS the packet body, the hash IS a function of our data.
|
||||
|
||||
Returns (rx_data, receipt) where rx_data includes the RX header.
|
||||
"""
|
||||
hdr = VirtioNetHdr(
|
||||
flags=VIRTIO_NET_HDR_NEEDS_CSUM,
|
||||
gso_type=GSO_TYPE_NONE,
|
||||
hdr_len=VIRTIO_NET_HDR_SIZE,
|
||||
gso_size=0,
|
||||
csum_start=14,
|
||||
csum_offset=4,
|
||||
num_buffers=1,
|
||||
)
|
||||
|
||||
rx_hdr = VirtioNetHdrHash(
|
||||
hdr=hdr,
|
||||
hash_value=0,
|
||||
hash_report=HASH_REPORT_TOEPLITZ if enable_hash_report else HASH_REPORT_NONE,
|
||||
padding=0,
|
||||
)
|
||||
|
||||
result = rx_hdr.pack() + payload
|
||||
receipt = VirtioTransformReceipt(
|
||||
transform_type="hash_report",
|
||||
payload_bytes=len(payload),
|
||||
packet_count=1,
|
||||
result_bytes=len(result),
|
||||
hash_value=rx_hdr.hash_value,
|
||||
hash_report=rx_hdr.hash_report,
|
||||
gso_chunks=0,
|
||||
merge_count=0,
|
||||
raw_header_hex=rx_hdr.pack().hex(),
|
||||
)
|
||||
receipt.witness_hash = f"{_crc32(result):08x}"
|
||||
return result, receipt
|
||||
|
||||
|
||||
def transform_tso_split(
|
||||
payload: bytes,
|
||||
chunk_size: int = 64,
|
||||
) -> Tuple[List[bytes], VirtioTransformReceipt]:
|
||||
"""TSO gso_size transform: host splits large buffer via TCP segmentation offload.
|
||||
|
||||
The host's TSO engine reads a large TX descriptor and splits it into
|
||||
N receive-side descriptors, each of size gso_size.
|
||||
|
||||
This is a spatial partition: the same input produces multiple output
|
||||
chunks, each carrying a portion of the original payload. The chunk
|
||||
boundaries are determined by gso_size, not by the computation.
|
||||
|
||||
chunk_size bytes per output descriptor:
|
||||
ceil(len(payload) / chunk_size) chunks
|
||||
|
||||
Returns (chunks, receipt) where chunks include their headers.
|
||||
"""
|
||||
hdr = VirtioNetHdr(
|
||||
flags=VIRTIO_NET_HDR_NEEDS_CSUM,
|
||||
gso_type=GSO_TYPE_TCPV4,
|
||||
hdr_len=VIRTIO_NET_HDR_SIZE,
|
||||
gso_size=chunk_size,
|
||||
csum_start=14,
|
||||
csum_offset=4,
|
||||
num_buffers=1,
|
||||
)
|
||||
hdr_bytes = hdr.pack()
|
||||
chunks: List[bytes] = []
|
||||
offset = 0
|
||||
seq = 0
|
||||
while offset < len(payload):
|
||||
chunk = payload[offset:offset + chunk_size]
|
||||
chunks.append(hdr_bytes + chunk)
|
||||
offset += chunk_size
|
||||
seq += 1
|
||||
|
||||
receipt = VirtioTransformReceipt(
|
||||
transform_type="tso_split",
|
||||
payload_bytes=len(payload),
|
||||
packet_count=seq,
|
||||
result_bytes=sum(len(c) for c in chunks),
|
||||
hash_value=0,
|
||||
hash_report=0,
|
||||
gso_chunks=seq,
|
||||
merge_count=0,
|
||||
raw_header_hex=hdr_bytes.hex(),
|
||||
)
|
||||
receipt.witness_hash = _crc32(b"".join(chunks)).to_bytes(4, "big").hex()
|
||||
return chunks, receipt
|
||||
|
||||
|
||||
def transform_merge(
|
||||
payloads: List[bytes],
|
||||
) -> Tuple[bytes, VirtioTransformReceipt]:
|
||||
"""MRG_RXBUF transform: host merges multiple RX buffers.
|
||||
|
||||
The host's merge buffer combines multiple small TX descriptors into
|
||||
one larger RX descriptor, with num_buffers set to the merge count.
|
||||
|
||||
This is an aggregation primitive: N inputs produce 1 output.
|
||||
num_buffers in the RX header = merge count = N.
|
||||
|
||||
Returns (merged_rx, receipt) where merged_rx includes RX header.
|
||||
"""
|
||||
hdr = VirtioNetHdr(
|
||||
flags=0,
|
||||
gso_type=GSO_TYPE_NONE,
|
||||
hdr_len=0,
|
||||
gso_size=0,
|
||||
csum_start=0,
|
||||
csum_offset=0,
|
||||
num_buffers=len(payloads),
|
||||
)
|
||||
hdr_bytes = hdr.pack()
|
||||
merged = hdr_bytes + b"".join(payloads)
|
||||
|
||||
receipt = VirtioTransformReceipt(
|
||||
transform_type="merge",
|
||||
payload_bytes=sum(len(p) for p in payloads),
|
||||
packet_count=1,
|
||||
result_bytes=len(merged),
|
||||
hash_value=0,
|
||||
hash_report=0,
|
||||
gso_chunks=0,
|
||||
merge_count=len(payloads),
|
||||
raw_header_hex=hdr_bytes.hex(),
|
||||
)
|
||||
receipt.witness_hash = f"{_crc32(merged):08x}"
|
||||
return merged, receipt
|
||||
|
||||
|
||||
# ── CLI ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
|
||||
if not args or args[0] == "--example":
|
||||
print("=== Virtio-Net Transform ===")
|
||||
print()
|
||||
print("Three computation primitives via virtio-net rings:")
|
||||
print()
|
||||
print("1. HASH_REPORT (RSS Toeplitz):")
|
||||
print(" payload → host RSS → hash_value in RX header")
|
||||
print()
|
||||
print("2. TSO gso_size (TCP segmentation):")
|
||||
print(" large buffer → host splits into N chunks of size gso_size")
|
||||
print()
|
||||
print("3. MRG_RXBUF (buffer merge):")
|
||||
print(" N small buffers → host merges into one RX descriptor")
|
||||
print()
|
||||
print("=== Demo ===")
|
||||
|
||||
test_payload = b"\xde\xad\xbe\xef" * 16
|
||||
print(f"Payload ({len(test_payload)} bytes): {test_payload[:16].hex()}...")
|
||||
print()
|
||||
|
||||
# HASH_REPORT
|
||||
result, r = transform_hash_report(test_payload)
|
||||
print(f"HASH_REPORT receipt: {json.dumps(r.to_dict(), indent=2)}")
|
||||
print()
|
||||
|
||||
# TSO split
|
||||
chunks, r = transform_tso_split(test_payload, chunk_size=16)
|
||||
print(f"TSO split into {len(chunks)} chunks (gso_size=16):")
|
||||
for i, c in enumerate(chunks):
|
||||
print(f" chunk {i}: header={c[:12].hex()} data={c[12:].hex()}")
|
||||
print()
|
||||
|
||||
# Merge
|
||||
parts = [test_payload[i:i+16] for i in range(0, len(test_payload), 16)]
|
||||
merged, r = transform_merge(parts)
|
||||
print(f"Merge {len(parts)} parts → {len(merged)} bytes:")
|
||||
print(f" header={merged[:12].hex()}")
|
||||
print(f" merge_count={r.merge_count}, result_bytes={r.result_bytes}")
|
||||
print()
|
||||
return
|
||||
|
||||
if args[0] == "--analyze":
|
||||
if len(args) < 2:
|
||||
print("Usage: virtio_net_transform.py --analyze <payload.bin>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payload = Path(args[1]).read_bytes()
|
||||
print(f"=== {args[1]} ===")
|
||||
print(f" Size: {len(payload)} bytes")
|
||||
print(f" CRC32: {_crc32(payload):08x}")
|
||||
print(f" TSO chunks (64B): {-(len(payload) // -64)}")
|
||||
print(f" TSO chunks (256B): {-(len(payload) // -256)}")
|
||||
print(f" TSO chunks (1400B MTU): {-(len(payload) // -1400)}")
|
||||
return
|
||||
|
||||
if args[0] == "hash-report":
|
||||
if len(args) < 2:
|
||||
print("Usage: virtio_net_transform.py hash-report <payload.bin> [--key <16bytes>]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payload = Path(args[1]).read_bytes()
|
||||
key = b"\x00" * 40
|
||||
if "--key" in args:
|
||||
ki = args.index("--key")
|
||||
key = args[ki + 1].encode("hex") if len(args[ki + 1]) == 32 else b"\x00" * 40
|
||||
result, r = transform_hash_report(payload)
|
||||
print(json.dumps(r.to_dict(), indent=2))
|
||||
return
|
||||
|
||||
if args[0] == "tso-split":
|
||||
if len(args) < 2:
|
||||
print("Usage: virtio_net_transform.py tso-split <payload.bin> --chunk-size <N>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payload = Path(args[1]).read_bytes()
|
||||
chunk_size = 64
|
||||
if "--chunk-size" in args:
|
||||
ki = args.index("--chunk-size")
|
||||
chunk_size = int(args[ki + 1])
|
||||
chunks, r = transform_tso_split(payload, chunk_size)
|
||||
print(json.dumps(r.to_dict(), indent=2))
|
||||
return
|
||||
|
||||
if args[0] == "merge":
|
||||
if len(args) < 2:
|
||||
print("Usage: virtio_net_transform.py merge <payload.bin>...", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
payloads = [Path(p).read_bytes() for p in args[1:]]
|
||||
merged, r = transform_merge(payloads)
|
||||
print(json.dumps(r.to_dict(), indent=2))
|
||||
return
|
||||
|
||||
if args[0] == "receipt":
|
||||
if len(args) < 2:
|
||||
print("Usage: virtio_net_transform.py receipt <result.json>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
data = json.loads(Path(args[1]).read_text())
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
print("Usage: virtio_net_transform.py <command> [args]", file=sys.stderr)
|
||||
print("Commands: --example, --analyze, hash-report, tso-split, merge, receipt", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Reference in a new issue