Skip to content

Commit b573403

Browse files
committed
include integration tests for booting and allocation on differently sized vms
1 parent f0cacb3 commit b573403

9 files changed

Lines changed: 231 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 3.31.6)
2+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
3+
project(service)
4+
include(os)
5+
set(SOURCES
6+
service.cpp
7+
)
8+
os_add_executable(memory_available "Memory available test" ${SOURCES})
9+
os_add_stdout(memory_available default_stdout)
10+
11+
configure_file(test.py ${CMAKE_CURRENT_BINARY_DIR})
12+
13+
configure_file(vm-default.json ${CMAKE_CURRENT_BINARY_DIR})
14+
configure_file(vm-128m.json ${CMAKE_CURRENT_BINARY_DIR})
15+
configure_file(vm-2g.json ${CMAKE_CURRENT_BINARY_DIR})
16+
configure_file(vm-4g.json ${CMAKE_CURRENT_BINARY_DIR})
17+
configure_file(vm-6g.json ${CMAKE_CURRENT_BINARY_DIR})
18+
configure_file(vm-16g.json ${CMAKE_CURRENT_BINARY_DIR})
19+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include <os>
2+
#include <service>
3+
#include <kernel/memory.hpp>
4+
#include <print>
5+
#include <cstddef>
6+
#include <sys/mman.h>
7+
8+
static void* try_mmap(std::size_t size) {
9+
try {
10+
return os::mem::raw_allocator().allocate(size);
11+
} catch (...) {
12+
return MAP_FAILED;
13+
}
14+
}
15+
16+
static void try_munmap(void *p, size_t size) {
17+
if (p != MAP_FAILED) os::mem::raw_allocator().deallocate(p, size);
18+
}
19+
20+
void Service::start()
21+
{
22+
std::uintptr_t heap_start = 0;
23+
std::uintptr_t heap_end = 0;
24+
25+
for (const auto& [addr, entry] : os::mem::vmmap()) {
26+
if (entry.name() == std::string_view{"Heap"}) {
27+
heap_start = entry.addr_start();
28+
heap_end = entry.addr_end();
29+
break;
30+
}
31+
}
32+
33+
const std::size_t heap_size = heap_end - heap_start + 1;
34+
const std::size_t heap_mib = heap_size / (1024UL * 1024UL);
35+
36+
std::println("HEAP_START: {:#x}", heap_start);
37+
std::println("HEAP_END: {:#x}", heap_end);
38+
std::println("HEAP_MiB: {}", heap_mib);
39+
40+
// small allocation, always expected to succeed
41+
{
42+
constexpr std::size_t small = 1UL * 1024UL * 1024UL; // 1 MiB
43+
std::println("bytes free: {}", os::mem::raw_allocator().bytes_free());
44+
45+
void* p = try_mmap(small);
46+
std::println("HEAP_ALLOC_SMALL (1 MiB): {}", (p != MAP_FAILED) ? "OK" : "FAIL");
47+
try_munmap(p, small);
48+
}
49+
50+
std::println("HEAP_FREE: {}", heap_mib);
51+
// large allocation
52+
{
53+
const size_t PERCENTAGE = (heap_mib >= 256) ? 30 : 10;
54+
const std::size_t large = heap_size * PERCENTAGE / 100;
55+
const std::size_t large_mib = large / (1024UL * 1024UL);
56+
void* p = try_mmap(large);
57+
58+
std::println("bytes free: {}", os::mem::raw_allocator().bytes_free());
59+
std::println("HEAP_ALLOC_LARGE ({} MiB = {}% of heap): {}", large_mib, PERCENTAGE, (p != MAP_FAILED) ? "OK" : "FAIL");
60+
try_munmap(p, large);
61+
}
62+
63+
// oversized allocation, must fail
64+
{
65+
const std::size_t oversize = os::mem::raw_allocator().bytes_free() + 16*1024UL*1024UL; // + 16MiB
66+
const std::size_t oversize_mib = oversize / (1024UL * 1024UL);
67+
68+
std::println("bytes free: {}", os::mem::raw_allocator().bytes_free());
69+
void* p = try_mmap(oversize); // this should fail, i.e. MAP_FAILED => ok
70+
71+
std::println("HEAP_ALLOC_OVERSIZED ({} MiB): {}", oversize_mib, (p == MAP_FAILED) ? "OK" : "FAIL");
72+
}
73+
74+
std::println("Test done.");
75+
os::shutdown();
76+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mem": 128
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mem": 16384
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mem": 2048
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mem": 4096
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"mem": 6144
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}

0 commit comments

Comments
 (0)