]> rtime.felk.cvut.cz Git - hercules2020/jailhouse-build.git/blob - test/memguard-test.c
jailhouse-test: Add test for non-overflowed memory statistics
[hercules2020/jailhouse-build.git] / test / memguard-test.c
1 #define _GNU_SOURCE         /* See feature_test_macros(7) */
2 #include <err.h>
3 #include <sched.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <time.h>
9 #include <inttypes.h>
10 #include <sys/mman.h>
11 #include <stdbool.h>
12 #include <pthread.h>
13 #include <stdlib.h>
14 #include <error.h>
15
16 #ifndef SYS_memguard
17 #define SYS_memguard 793
18 #endif
19
20 /* Return value:
21  * 0-31  - Sum of memory events
22  * 32-55 - Total time
23  * 61    - Memory budget overrun
24  * 62    - Timeout
25  * 63    - Error */
26 #define MGRET_MEM_POS           0
27 #define MGRET_TIM_POS           32
28 #define MGRET_OVER_MEM_POS      61
29 #define MGRET_OVER_TIM_POS      62
30 #define MGRET_ERROR_POS         63
31
32 #define MGRET_TIM_MASK          (0x00FFFFFFul << MGRET_TIM_POS)
33 #define MGRET_MEM_MASK          (0xFFFFFFFFul << MGRET_MEM_POS)
34 #define MGRET_OVER_MEM_MASK     (1ul << MGRET_OVER_MEM_POS)
35 #define MGRET_OVER_TIM_MASK     (1ul << MGRET_OVER_TIM_POS)
36 #define MGRET_ERROR_MASK        (1ul << MGRET_ERROR_POS)
37
38 #define MAX_CORES               6
39
40 char memory[128*10204*1024];
41
42 static pthread_barrier_t barrier;
43
44 struct mg_ret {
45         uint32_t time;
46         uint32_t mem;
47         bool time_ovf;
48         bool mem_ovf;
49 };
50
51 static struct mg_ret mgret(uint64_t retval)
52 {
53         struct mg_ret mgr = {};
54         mgr.time = (retval & MGRET_TIM_MASK) >> MGRET_TIM_POS;
55         mgr.mem = (retval & MGRET_MEM_MASK) >> MGRET_MEM_POS;
56         mgr.time_ovf = (retval >> MGRET_OVER_TIM_POS) & 1;
57         mgr.mem_ovf = (retval >> MGRET_OVER_MEM_POS) & 1;
58         return mgr;
59 }
60
61 void compute_kernel(int time_us)
62 {
63         struct timespec ts;
64         uint64_t current_us, end_us;
65         clock_gettime(CLOCK_MONOTONIC, &ts);
66         end_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000 + time_us;
67         do {
68                 clock_gettime(CLOCK_MONOTONIC, &ts);
69                 current_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
70         } while (current_us < end_us);
71 }
72
73 void read_memory(long lines)
74 {
75         uint64_t sum = 0;
76
77         for (uint64_t i = 0; i < lines; i++) {
78                 sum += memory[(i * 64) % sizeof(memory)];
79         }
80         /* Do not optimize this function out */
81         volatile uint64_t x = sum;
82         (void)x;
83 }
84
85 void write_memory(long lines)
86 {
87         uint64_t *ptr;
88         for (uint64_t i = 0; i < lines; i++) {
89                 ptr = (void*)&memory[(i * 64) % sizeof(memory)];
90                 *ptr = i;
91         }
92 }
93
94 void read_memory_rnd(long lines)
95 {
96         static unsigned rnd;
97         uint64_t sum = 0;
98
99         for (long i = 0; i < lines; i++) {
100                 rnd = (rnd + 523) * 253573;
101                 sum += memory[rnd % sizeof(memory)];
102         }
103         /* Do not optimize this function out */
104         volatile uint64_t x = sum;
105         (void)x;
106 }
107
108 /* Memguard flags */
109 #define MGF_PERIODIC                                                           \
110         (1 << 0) /* Chooses between periodic or one-shot budget replenishment */
111 #define MGF_MASK_INT                                                           \
112         (1                                                                     \
113          << 1) /* Mask (disable) low priority interrupts until next memguard call */
114
115 long memguard(unsigned long timeout, unsigned long memory_budget,
116               unsigned long flags)
117 {
118         return syscall(SYS_memguard, timeout, memory_budget, flags);
119 }
120
121 void wvtest_pass(bool cond, const char* file, int line, const char* str)
122 {
123         printf("! %s:%d  %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
124 }
125
126 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
127
128 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
129                             int64_t retval, const char *code)
130 {
131         char call[80];
132         snprintf(call, sizeof(call), "memguard(%luus, %lumiss, %c%c):",
133                  timeout_us, mem_budget,
134                  flags & MGF_PERIODIC ? 'P' : '-',
135                  flags & MGF_MASK_INT ? 'I' : '-'
136                 );
137         struct mg_ret r = mgret(retval);
138         printf("Testing \"CPU%d: %-40s %-25s ⇒ time:%8u%c mem:%8u%c\" in %s:\n",
139                sched_getcpu(), call, code,
140                r.time, r.time_ovf ? '!' : ' ',
141                r.mem, r.mem_ovf ? '!' : ' ',
142                __FILE__);
143         WVPASS((retval & MGRET_ERROR_MASK) == 0);
144 }
145
146 pthread_mutex_t wvtest_lock = PTHREAD_MUTEX_INITIALIZER;
147
148 #define MGTEST(timeout_us, mem_budget, flags, code)                     \
149         ({                                                              \
150                 long retval;                                            \
151                 retval = memguard(timeout_us, mem_budget, flags);       \
152                 code;                                                   \
153                 retval = memguard(0, 0, 0);                             \
154                 pthread_mutex_lock(&wvtest_lock);                       \
155                 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
156                 mgret(retval);                                          \
157         })
158
159 #define MGTESTEND() pthread_mutex_unlock(&wvtest_lock)
160
161 void *test_thread(void *ptr)
162 {
163         cpu_set_t set;
164         int cpu = (intptr_t)ptr;
165
166         /* Ensure that our test thread does not migrate to another CPU
167          * during memguarding */
168         CPU_ZERO(&set);
169         CPU_SET(cpu, &set);
170         if (sched_setaffinity(0, sizeof(set), &set) < 0)
171                 err(1, "sched_setaffinity");
172
173         pthread_barrier_wait(&barrier);
174
175         struct mg_ret r;
176
177         for (uint64_t flags = 0; flags < 4; flags++) {
178                 compute_kernel(1); /* warm up */
179
180                 ///////////////////////////////////////////////////////
181                 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
182                 WVPASS(!r.time_ovf);
183                 WVPASS(r.time > 900);
184                 WVPASS(!r.mem_ovf < 10000);
185                 WVPASS(r.mem < 10000);
186                 MGTESTEND();
187
188                 r = MGTEST(500, 10000, flags, compute_kernel(1000));
189                 WVPASS(r.time_ovf);
190                 WVPASS(r.time > 900);
191                 MGTESTEND();
192
193                 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
194                 WVPASS(!r.time_ovf);
195                 WVPASS(r.time > 1900);
196                 MGTESTEND();
197
198                 r = MGTEST(500, 10000, flags, compute_kernel(2000));
199                 WVPASS(r.time_ovf);
200                 WVPASS(r.time > 1900);
201                 MGTESTEND();
202
203                 ///////////////////////////////////////////////////////
204                 r = MGTEST(100000, 500000, flags, read_memory(100000));
205                 WVPASS(!r.mem_ovf);
206                 WVPASS(r.mem >= 90000);
207                 MGTESTEND();
208
209                 r = MGTEST(100000, 50000,  flags, read_memory(100000));
210                 WVPASS(r.mem_ovf);
211                 WVPASS(r.mem >= 90000);
212                 MGTESTEND();
213
214                 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
215                 WVPASS(!r.mem_ovf);
216                 WVPASS(r.mem >= 90000);
217                 MGTESTEND();
218
219                 r = MGTEST(100000, 50000,  flags, read_memory_rnd(100000));
220                 WVPASS(r.mem_ovf);
221                 WVPASS(r.mem >= 90000);
222                 MGTESTEND();
223
224                 r = MGTEST(100000, 500000, flags, write_memory(100000));
225                 WVPASS(!r.mem_ovf);
226                 WVPASS(r.mem >= 90000);
227                 MGTESTEND();
228
229                 r = MGTEST(100000, 50000,  flags, write_memory(100000));
230                 WVPASS(r.mem_ovf);
231                 WVPASS(r.mem >= 90000);
232                 MGTESTEND();
233
234
235                 ///////////////////////////////////////////////////////
236                 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
237                 WVPASS(!r.mem_ovf);
238                 WVPASS(r.mem >= 900000);
239                 MGTESTEND();
240
241                 r = MGTEST(100000, 500000,  flags, read_memory(1000000));
242                 WVPASS(r.mem_ovf);
243                 WVPASS(r.mem >= 900000);
244                 MGTESTEND();
245
246                 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
247                 WVPASS(!r.mem_ovf);
248                 WVPASS(r.mem >= 900000);
249                 MGTESTEND();
250
251                 r = MGTEST(100000, 500000,  flags, read_memory_rnd(1000000));
252                 WVPASS(r.mem_ovf);
253                 WVPASS(r.mem >= 900000);
254                 MGTESTEND();
255
256                 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
257                 WVPASS(!r.mem_ovf);
258                 WVPASS(r.mem >= 900000);
259                 MGTESTEND();
260
261                 r = MGTEST(100000, 500000,  flags, write_memory(1000000));
262                 WVPASS(r.mem_ovf);
263                 WVPASS(r.mem >= 900000);
264                 MGTESTEND();
265
266                 printf("\n");
267         }
268
269         /* Throttling tests */
270         struct mg_ret r1, r2, r3;
271         r1 = MGTEST(10000, 9000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
272         WVPASS(r1.time > 100*1000);
273         MGTESTEND();
274
275         r2 = MGTEST(10000, 3000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
276         WVPASS(r2.time > 2 * r1.time);
277         MGTESTEND();
278
279         r3 = MGTEST(10000, 1000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
280         WVPASS(r3.time > 2 * r2.time);
281         MGTESTEND();
282
283         return NULL;
284 }
285
286 int main(int argc, char *argv[])
287 {
288         int cpu_mask = 1;
289         int cpu_count = 0;
290         pthread_t threads[MAX_CORES];
291
292         /* TODO: currently shared memory */
293         for (int i = 0; i < sizeof(memory); i += 64)
294                 memory[i] = 1;
295
296         if (argc > 1)
297                 cpu_mask = strtol(argv[1], NULL, 16);
298
299         for (int i = 0; i < MAX_CORES; i++) {
300                 if (cpu_mask & (1 << i)) {
301                         cpu_count++;
302                 }
303         }
304
305         printf("CPU count:%d  CPU mask:%#x\n", cpu_count, cpu_mask);
306         int s = pthread_barrier_init(&barrier, NULL, cpu_count);
307         if (s != 0)
308                 error(1, s, "pthread_barrier_init");
309
310         for (intptr_t i = 0; i < MAX_CORES; i++) {
311                 if (cpu_mask & (1 << i))
312                         pthread_create(&threads[i], NULL, test_thread, (void *)i);
313         }
314
315         for (int i = 0; i < MAX_CORES; i++) {
316                 if (cpu_mask & (1 << i)) {
317                         pthread_join(threads[i], NULL);
318                 }
319         }
320
321         return 0;
322 }