|
| 1 | +#!/usr/bin/env python3 |
| 2 | +from __future__ import print_function |
| 3 | +import sys |
| 4 | +import re |
| 5 | +from vmrunner import vmrunner |
| 6 | + |
| 7 | +IMAGE = "memory_available.elf.bin" |
| 8 | + |
| 9 | +CONFIGS = [ |
| 10 | + { |
| 11 | + "name": "default", |
| 12 | + "config": "vm-default.json", |
| 13 | + "heap_start_min": 0x1_000_000, # leaves room for LiveUpdate and SystemLog |
| 14 | + "heap_start_max": 0xFFFF_FFFF, # stays below the 32-bit boundary |
| 15 | + "heap_mib_min": 64, # observed to be 79 MiB |
| 16 | + "heap_mib_max": 128, # heap can't be bigger than the provided memory |
| 17 | + }, |
| 18 | + { |
| 19 | + "name": "128 MiB", # should match default |
| 20 | + "config": "vm-128m.json", |
| 21 | + "heap_start_min": 0x1_000_000, |
| 22 | + "heap_start_max": 0xFFFF_FFFF, |
| 23 | + "heap_mib_min": 64, |
| 24 | + "heap_mib_max": 128, |
| 25 | + }, |
| 26 | + { |
| 27 | + "name": "2 GiB", |
| 28 | + "config": "vm-2g.json", |
| 29 | + "heap_start_min": 0x1_000_000, |
| 30 | + "heap_start_max": 0xFFFF_FFFF, |
| 31 | + "heap_mib_min": 1400, # observed to be 1519 MiB |
| 32 | + "heap_mib_max": 2048, |
| 33 | + }, |
| 34 | + { |
| 35 | + "name": "4 GiB", |
| 36 | + "config": "vm-4g.json", |
| 37 | + "heap_start_min": 0x1_000_000, |
| 38 | + "heap_start_max": 0xFFFF_FFFF, |
| 39 | + "heap_mib_min": 2048, # observed to be 2287 MiB |
| 40 | + "heap_mib_max": 4096, |
| 41 | + }, |
| 42 | + { |
| 43 | + "name": "6 GiB", |
| 44 | + "config": "vm-6g.json", |
| 45 | + "heap_start_min": 0x1_0000_0000, # above 4G: heap will be located after the 32-bit area |
| 46 | + "heap_start_max": None, |
| 47 | + "heap_mib_min": 2800, # observed to be 3072 MiB |
| 48 | + "heap_mib_max": 6144, |
| 49 | + }, |
| 50 | + # 16GiB fails because buddy doesn't support large allocations |
| 51 | + # { |
| 52 | + # "name": "16 GiB", |
| 53 | + # "config": "vm-16g.json", |
| 54 | + # "heap_start_min": 0x1_0000_0000, |
| 55 | + # "heap_start_max": None, |
| 56 | + # "heap_mib_min": 12288, # observed to be 13312 MiB |
| 57 | + # "heap_mib_max": 16384, |
| 58 | + # }, |
| 59 | +] |
| 60 | + |
| 61 | + |
| 62 | +def boot_chain(configs): |
| 63 | + if not configs: return |
| 64 | + cfg, *rest = configs |
| 65 | + |
| 66 | + vm = vmrunner.vm(config=cfg["config"]) |
| 67 | + |
| 68 | + def on_heap_start(line): |
| 69 | + m = re.search(r'HEAP_START:\s+(0x[0-9a-fA-F]+)', line) |
| 70 | + if not m: return |
| 71 | + addr = int(m.group(1), 16) |
| 72 | + |
| 73 | + assert addr >= cfg["heap_start_min"], \ |
| 74 | + f"[{cfg['name']}] Heap start {addr:#x} below minimum {cfg['heap_start_min']:#x}" |
| 75 | + |
| 76 | + if cfg["heap_start_max"] is not None: |
| 77 | + assert addr <= cfg["heap_start_max"], \ |
| 78 | + f"[{cfg['name']}] Heap start {addr:#x} unexpectedly above 4G" |
| 79 | + |
| 80 | + def on_heap_mib(line): |
| 81 | + m = re.search(r'HEAP_MiB:\s+(\d+)', line) |
| 82 | + if not m: return |
| 83 | + mib = int(m.group(1)) |
| 84 | + |
| 85 | + assert mib >= cfg["heap_mib_min"], \ |
| 86 | + f"[{cfg['name']}] Heap size {mib} MiB below minimum {cfg['heap_mib_min']} MiB" |
| 87 | + if cfg["heap_mib_max"] is not None: |
| 88 | + assert mib <= cfg["heap_mib_max"], \ |
| 89 | + f"[{cfg['name']}] Heap size {mib} MiB above maximum {cfg['heap_mib_max']} MiB; " \ |
| 90 | + f"default config should give same heap as explicit 128m" |
| 91 | + |
| 92 | + def on_alloc_small(line): |
| 93 | + assert "HEAP_ALLOC_SMALL (1 MiB): OK" in line, \ |
| 94 | + f"[{cfg['name']}] Small allocation failed: {line}" |
| 95 | + |
| 96 | + def on_alloc_large(line): |
| 97 | + assert re.search(r'HEAP_ALLOC_LARGE \(\d+ MiB = \d+% of heap\): OK', line), \ |
| 98 | + f"[{cfg['name']}] Large allocation failed: {line}" |
| 99 | + |
| 100 | + def on_alloc_oversize(line): |
| 101 | + m = re.search(r'HEAP_ALLOC_OVERSIZED \(\d+ MiB\): (OK|FAIL)', line) |
| 102 | + # TODO: hard-fail here after buddy is replaced |
| 103 | + if m and m.group(1) == "FAIL": |
| 104 | + print("WARNING: oversized allocation succeeded --- known limitation of current buddy allocator") |
| 105 | + |
| 106 | + vm.on_output("HEAP_START:", on_heap_start) |
| 107 | + vm.on_output("HEAP_MiB:", on_heap_mib) |
| 108 | + vm.on_output("HEAP_ALLOC_SMALL", on_alloc_small) |
| 109 | + vm.on_output("HEAP_ALLOC_LARGE", on_alloc_large) |
| 110 | + vm.on_output("HEAP_ALLOC_OVERSIZED", on_alloc_oversize) |
| 111 | + vm.on_exit_success(lambda: boot_chain(rest)) |
| 112 | + |
| 113 | + print(f"Booting VM {len(CONFIGS) - len(configs) + 1}/{len(CONFIGS)}: {cfg['name']}") |
| 114 | + vm.boot(image_name=IMAGE) |
| 115 | + return vm |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +boot_chain(CONFIGS) |
0 commit comments