Skip to main content

test_mem/
main.rs

1#![no_std]
2#![no_main]
3
4use core::panic::PanicInfo;
5use strat9_syscall::call;
6
7const PAGE_SIZE: usize = 4096;
8
9/// Writes fd.
10fn write_fd(fd: usize, msg: &str) {
11    let _ = call::write(fd, msg.as_bytes());
12}
13
14/// Implements log.
15fn log(msg: &str) {
16    write_fd(1, msg);
17}
18
19/// Implements log err.
20fn log_err(msg: &str) {
21    write_fd(2, msg);
22}
23
24/// Implements log u64.
25fn log_u64(mut value: u64) {
26    let mut buf = [0u8; 21];
27    if value == 0 {
28        log("0");
29        return;
30    }
31    let mut i = buf.len();
32    while value > 0 {
33        i -= 1;
34        buf[i] = b'0' + (value % 10) as u8;
35        value /= 10;
36    }
37    let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
38    log(s);
39}
40
41/// Implements log hex u64.
42fn log_hex_u64(mut value: u64) {
43    let mut buf = [0u8; 16];
44    for i in (0..16).rev() {
45        let nibble = (value & 0xF) as u8;
46        buf[i] = if nibble < 10 {
47            b'0' + nibble
48        } else {
49            b'a' + (nibble - 10)
50        };
51        value >>= 4;
52    }
53    log("0x");
54    let s = unsafe { core::str::from_utf8_unchecked(&buf) };
55    log(s);
56}
57
58/// Implements log line.
59fn log_line(label: &str, value: u64) {
60    log(label);
61    log_u64(value);
62    log("\n");
63}
64
65/// Implements section.
66fn section(title: &str) {
67    log("\n============================================================\n");
68    log("[test_mem] ");
69    log(title);
70    log("\n============================================================\n");
71}
72
73/// Implements query brk.
74fn query_brk() -> usize {
75    match call::brk(0) {
76        Ok(v) => v,
77        Err(e) => {
78            log_err("[test_mem] brk(0) failed: ");
79            log_err(e.name());
80            log_err("\n");
81            call::exit(101);
82        }
83    }
84}
85
86/// Sets brk.
87fn set_brk(new_brk: usize, ctx: &str) -> usize {
88    match call::brk(new_brk) {
89        Ok(v) => v,
90        Err(e) => {
91            log_err("[test_mem] brk(set) failed in ");
92            log_err(ctx);
93            log_err(": ");
94            log_err(e.name());
95            log_err("\n");
96            call::exit(102);
97        }
98    }
99}
100
101/// Implements fill and verify pages.
102fn fill_and_verify_pages(base: usize, pages: usize, seed: u8, tag: &str) {
103    log("[test_mem] fill_and_verify ");
104    log(tag);
105    log(" base=");
106    log_hex_u64(base as u64);
107    log(" pages=");
108    log_u64(pages as u64);
109    log(" seed=");
110    log_hex_u64(seed as u64);
111    log("\n");
112
113    let total = pages * PAGE_SIZE;
114    let buf = unsafe { core::slice::from_raw_parts_mut(base as *mut u8, total) };
115    for p in 0..pages {
116        let page_off = p * PAGE_SIZE;
117        let a = seed.wrapping_add((p as u8).wrapping_mul(3));
118        let b = seed.wrapping_add((p as u8).wrapping_mul(7)).wrapping_add(1);
119        let c = seed
120            .wrapping_add((p as u8).wrapping_mul(11))
121            .wrapping_add(2);
122
123        buf[page_off] = a;
124        buf[page_off + 17] = b;
125        buf[page_off + PAGE_SIZE - 1] = c;
126    }
127
128    for p in 0..pages {
129        let page_off = p * PAGE_SIZE;
130        let a = seed.wrapping_add((p as u8).wrapping_mul(3));
131        let b = seed.wrapping_add((p as u8).wrapping_mul(7)).wrapping_add(1);
132        let c = seed
133            .wrapping_add((p as u8).wrapping_mul(11))
134            .wrapping_add(2);
135
136        if buf[page_off] != a || buf[page_off + 17] != b || buf[page_off + PAGE_SIZE - 1] != c {
137            log_err("[test_mem] verify failed at page ");
138            log_u64(p as u64);
139            log_err(" in ");
140            log_err(tag);
141            log_err("\n");
142            call::exit(103);
143        }
144    }
145
146    log("[test_mem] verify OK for ");
147    log(tag);
148    log("\n");
149}
150
151/// Implements single roundtrip.
152fn single_roundtrip(case_name: &str, pages: usize, seed: u8) {
153    section(case_name);
154    let base = query_brk();
155    let target = base + pages * PAGE_SIZE;
156
157    log("[test_mem] base brk     = ");
158    log_hex_u64(base as u64);
159    log("\n[test_mem] target brk   = ");
160    log_hex_u64(target as u64);
161    log("\n[test_mem] grow pages   = ");
162    log_u64(pages as u64);
163    log("\n");
164
165    let after_grow = set_brk(target, case_name);
166    log("[test_mem] after grow   = ");
167    log_hex_u64(after_grow as u64);
168    log("\n");
169    if after_grow < target {
170        log_err("[test_mem] ERROR: brk grow returned too small value\n");
171        call::exit(104);
172    }
173
174    fill_and_verify_pages(base, pages, seed, case_name);
175
176    let after_shrink = set_brk(base, case_name);
177    log("[test_mem] after shrink = ");
178    log_hex_u64(after_shrink as u64);
179    log("\n");
180    if after_shrink > base {
181        log_err("[test_mem] ERROR: brk shrink did not return near base\n");
182        call::exit(105);
183    }
184}
185
186/// Implements saw tooth stress.
187fn saw_tooth_stress() {
188    section("Saw-tooth stress (split/coalesce pressure)");
189    let base = query_brk();
190    log("[test_mem] initial base = ");
191    log_hex_u64(base as u64);
192    log("\n");
193
194    let mut pages = 1usize;
195    for step in 0..18usize {
196        let target = base + pages * PAGE_SIZE;
197        log("[test_mem] step ");
198        log_u64(step as u64);
199        log(": grow to ");
200        log_u64(pages as u64);
201        log(" pages -> ");
202        log_hex_u64(target as u64);
203        log("\n");
204        let _ = set_brk(target, "saw_tooth_grow");
205        fill_and_verify_pages(base, pages, (0x40u8).wrapping_add(step as u8), "saw_tooth");
206
207        let back_pages = pages / 2;
208        let back_target = base + back_pages * PAGE_SIZE;
209        log("[test_mem] step ");
210        log_u64(step as u64);
211        log(": shrink to ");
212        log_u64(back_pages as u64);
213        log(" pages -> ");
214        log_hex_u64(back_target as u64);
215        log("\n");
216        let _ = set_brk(back_target, "saw_tooth_shrink");
217
218        pages = if pages >= 64 { 1 } else { pages * 2 };
219        let _ = call::sched_yield();
220    }
221
222    let final_brk = set_brk(base, "saw_tooth_final");
223    log("[test_mem] final brk = ");
224    log_hex_u64(final_brk as u64);
225    log("\n");
226}
227
228/// Implements churn many small ops.
229fn churn_many_small_ops() {
230    section("Many small grow/shrink cycles");
231    let base = query_brk();
232    for i in 0..80usize {
233        let pages = 1 + (i % 7);
234        let target = base + pages * PAGE_SIZE;
235        let _ = set_brk(target, "churn_grow");
236        fill_and_verify_pages(base, pages, (0x90u8).wrapping_add(i as u8), "churn");
237        let _ = set_brk(base, "churn_shrink");
238        if i % 10 == 0 {
239            log("[test_mem] churn checkpoint i=");
240            log_u64(i as u64);
241            log("\n");
242        }
243    }
244}
245
246#[panic_handler]
247/// Implements panic.
248fn panic(_info: &PanicInfo) -> ! {
249    log_err("[test_mem] PANIC\n");
250    call::exit(200)
251}
252
253#[no_mangle]
254/// Implements start.
255pub extern "C" fn _start() -> ! {
256    section("Strat9 userspace memory stress test (very verbose)");
257    log("[test_mem] objectif: valider grow/shrink BRK + accès page par page\n");
258    log("[test_mem] ce test est volontairement verbeux\n");
259
260    let pid = call::getpid().unwrap_or(0);
261    let tid = call::gettid().unwrap_or(0);
262    log("[test_mem] pid=");
263    log_u64(pid as u64);
264    log(" tid=");
265    log_u64(tid as u64);
266    log("\n");
267
268    single_roundtrip("Roundtrip 1 page", 1, 0x11);
269    single_roundtrip("Roundtrip 2 pages", 2, 0x22);
270    single_roundtrip("Roundtrip 4 pages", 4, 0x33);
271    single_roundtrip("Roundtrip 8 pages", 8, 0x44);
272    single_roundtrip("Roundtrip 16 pages", 16, 0x55);
273    single_roundtrip("Roundtrip 32 pages", 32, 0x66);
274
275    saw_tooth_stress();
276    churn_many_small_ops();
277
278    section("Completed successfully");
279    log_line("[test_mem] exit code = ", 0);
280    call::exit(0)
281}