compiler/
kernel/
lib/
scripts/
seed/
sublime/
test/
acceptance/
compile
549 B
metrics
5.4 KiB
replay
840 B
run
693 B
sizes
3.6 KiB
boot/
bootstrap/
cycles/
dispatch/
loader/
mmio/
modules/
native/
packages/
pages/
runtime/
scheduling/
shared/
slots/
smp/
sync/
termination/
tests/
trap/
run
2.7 KiB
runner.rad
10.3 KiB
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 B
test/acceptance/sizes
raw
| 1 | #!/usr/bin/env python3 |
| 2 | """Check kernel source limits and report the built image's fixed memory.""" |
| 3 | |
| 4 | import pathlib |
| 5 | import struct |
| 6 | import sys |
| 7 | |
| 8 | |
| 9 | def source_sizes(root): |
| 10 | """Count physical source lines and combine files that form one module.""" |
| 11 | totals = {"runtime": 0, "tests": 0, "tools": 0} |
| 12 | modules = {} |
| 13 | for path in sorted(root.rglob("*")): |
| 14 | if path.suffix not in (".rad", ".ras"): |
| 15 | continue |
| 16 | relative = path.relative_to(root) |
| 17 | category = "runtime" |
| 18 | if "tests" in relative.parts or path.stem == "tests": |
| 19 | category = "tests" |
| 20 | elif relative.parts[0] == "tools": |
| 21 | category = "tools" |
| 22 | lines = len(path.read_bytes().splitlines()) |
| 23 | totals[category] += lines |
| 24 | if category != "tests": |
| 25 | module = str(relative.with_suffix("")) |
| 26 | modules[module] = modules.get(module, 0) + lines |
| 27 | if not totals["runtime"]: |
| 28 | raise ValueError("kernel runtime sources are missing") |
| 29 | for module, lines in sorted(modules.items()): |
| 30 | print(f"module {module}: {lines} source lines") |
| 31 | if lines >= 1000: |
| 32 | raise ValueError(f"module {module} must have fewer than 1000 lines") |
| 33 | for category, lines in totals.items(): |
| 34 | print(f"kernel {category}: {lines} source lines") |
| 35 | if totals["runtime"] > 12000: |
| 36 | raise ValueError("kernel runtime exceeds 12000 source lines") |
| 37 | |
| 38 | |
| 39 | def image_sizes(path): |
| 40 | """Validate the native image extents and report each fixed memory segment.""" |
| 41 | with path.open("rb") as stream: |
| 42 | header = stream.read(64) |
| 43 | if len(header) != 64: |
| 44 | raise ValueError("native image header is truncated") |
| 45 | magic, version, entry = struct.unpack_from("<IIQ", header) |
| 46 | if magic != 0x30444152 or version != 2: |
| 47 | raise ValueError("expected a version 2 native image") |
| 48 | segments = [] |
| 49 | payload = 64 |
| 50 | for index, name in enumerate(("code", "read-only", "writable")): |
| 51 | address, initialized, memory = struct.unpack_from("<QII", header, 16 + index * 16) |
| 52 | alignment = 4 if index == 0 else 8 |
| 53 | if initialized > memory or address + memory > 1 << 64: |
| 54 | raise ValueError(f"invalid {name} segment extent") |
| 55 | if memory and address % alignment: |
| 56 | raise ValueError(f"misaligned {name} segment") |
| 57 | segments.append((address, initialized, memory)) |
| 58 | payload += initialized |
| 59 | print(f"image {name}: {memory} fixed bytes, {initialized} initialized bytes") |
| 60 | code, initialized, _ = segments[0] |
| 61 | if entry % 4 or initialized % 4 or not code <= entry < code + initialized: |
| 62 | raise ValueError("entry is outside initialized instructions") |
| 63 | for index, (start, _, size) in enumerate(segments): |
| 64 | for other, _, length in segments[index + 1:]: |
| 65 | if size and length and start < other + length and other < start + size: |
| 66 | raise ValueError("native image segments overlap") |
| 67 | if path.stat().st_size != payload: |
| 68 | raise ValueError("native image payload size does not match its header") |
| 69 | print(f"image total: {sum(segment[2] for segment in segments)} fixed bytes") |
| 70 | |
| 71 | |
| 72 | def main(): |
| 73 | """Check the worktree, or explicit source and image paths for a fixture.""" |
| 74 | if len(sys.argv) not in (1, 3): |
| 75 | raise ValueError("usage: sizes [kernel-directory native-image]") |
| 76 | root = pathlib.Path(sys.argv[1] if len(sys.argv) == 3 else "kernel") |
| 77 | image = pathlib.Path(sys.argv[2] if len(sys.argv) == 3 else "bin/kernel.rv64") |
| 78 | source_sizes(root) |
| 79 | image_sizes(image) |
| 80 | |
| 81 | |
| 82 | if __name__ == "__main__": |
| 83 | try: |
| 84 | main() |
| 85 | except (OSError, ValueError) as error: |
| 86 | print(f"kernel size check: {error}", file=sys.stderr) |
| 87 | sys.exit(1) |