1 /* Run this as: for i in $(seq 0 5); do prem-test $i & done */
3 #define _GNU_SOURCE /* See feature_test_macros(7) */
17 #ifndef SYS_prem_memguard_check
18 #define SYS_prem_memguard_check 793
23 * 32-55 - Sum of memory events
24 * 61 - Memory budget overrun
27 #define MGRET_TIM_POS 0
28 #define MGRET_MEM_POS 32
29 #define MGRET_OVER_MEM_POS 61
30 #define MGRET_OVER_TIM_POS 62
32 #define MGRET_TIM_MASK 0xFFFFFFFFlu
33 #define MGRET_MEM_MASK 0xFFFFFFlu
34 #define MGRET_OVER_MEM_MASK 0x1lu
35 #define MGRET_OVER_TIM_MASK 0x1lu
39 char memory[128*10204*1024];
41 static pthread_barrier_t barrier;
50 static struct mg_ret mgret(uint64_t retval)
52 struct mg_ret mgr = {};
53 mgr.time = (retval >> MGRET_TIM_POS) & MGRET_TIM_MASK;
54 mgr.mem = (retval >> MGRET_MEM_POS) & MGRET_MEM_MASK;
55 mgr.time_ovf = (retval >> MGRET_OVER_TIM_POS) & 1;
56 mgr.mem_ovf = (retval >> MGRET_OVER_MEM_POS) & 1;
60 void compute_kernel(int time_us)
63 uint64_t current_us, end_us;
64 clock_gettime(CLOCK_MONOTONIC, &ts);
65 end_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000 + time_us;
67 clock_gettime(CLOCK_MONOTONIC, &ts);
68 current_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
69 } while (current_us < end_us);
72 void read_memory(long lines)
76 for (uint64_t i = 0; i < lines; i++) {
77 sum += memory[(i * 64) % sizeof(memory)];
79 /* Do not optimize this function out */
80 volatile uint64_t x = sum;
84 void write_memory(long lines)
87 for (uint64_t i = 0; i < lines; i++) {
88 ptr = (void*)&memory[(i * 64) % sizeof(memory)];
93 void read_memory_rnd(long lines)
98 for (long i = 0; i < lines; i++) {
99 rnd = (rnd + 523) * 253573;
100 sum += memory[rnd % sizeof(memory)];
102 /* Do not optimize this function out */
103 volatile uint64_t x = sum;
108 #define MGF_PERIODIC \
109 (1 << 0) /* Chooses between periodic or one-shot budget replenishment */
110 #define MGF_MASK_INT \
112 << 1) /* Mask (disable) low priority interrupts until next memguard call */
114 long prem_memguard_check(unsigned long timeout, unsigned long memory_budget,
117 return syscall(SYS_prem_memguard_check, timeout, memory_budget, flags);
120 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
121 int64_t retval, const char *code)
124 snprintf(call, sizeof(call), "memguard(%luus, %lumiss, %c%c):",
125 timeout_us, mem_budget,
126 flags & MGF_PERIODIC ? 'P' : '-',
127 flags & MGF_MASK_INT ? 'I' : '-'
129 struct mg_ret r = mgret(retval);
130 printf("Testing \"%-40s %-25s ⇒ time:%8u%c mem:%8u%c\" in %s:\n",
132 r.time, r.time_ovf ? '!' : ' ',
133 r.mem, r.mem_ovf ? '!' : ' ',
137 void wvtest_pass(bool cond, const char* file, int line, const char* str)
139 printf("! %s:%d %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
142 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
144 #define MGTEST(timeout_us, mem_budget, flags, code) \
147 retval = prem_memguard_check(timeout_us, mem_budget, flags); \
149 retval = prem_memguard_check(0, 0, 0); \
150 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
154 void *test_thread(void *ptr)
160 /* Ensure that memory phase starts and ends on the same CPU */
163 if (sched_setaffinity(getpid(), sizeof(set), &set) < 0)
164 err(1, "sched_setaffinity");
165 printf("Pinned to CPU %d\n", *cpu);
167 pthread_barrier_wait(&barrier);
170 for (uint64_t flags = 0; flags < 4; flags++) {
171 compute_kernel(1); /* warm up */
173 ///////////////////////////////////////////////////////
174 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
176 WVPASS(r.time > 900);
178 r = MGTEST(500, 10000, flags, compute_kernel(1000));
180 WVPASS(r.time > 900);
182 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
184 WVPASS(r.time > 1900);
186 r = MGTEST(500, 10000, flags, compute_kernel(2000));
188 WVPASS(r.time > 1900);
190 ///////////////////////////////////////////////////////
191 r = MGTEST(100000, 500000, flags, read_memory(100000));
193 WVPASS(r.mem >= 100000);
195 r = MGTEST(100000, 50000, flags, read_memory(100000));
197 WVPASS(r.mem >= 100000);
199 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
201 WVPASS(r.mem >= 100000);
203 r = MGTEST(100000, 50000, flags, read_memory_rnd(100000));
205 WVPASS(r.mem >= 100000);
207 r = MGTEST(100000, 500000, flags, write_memory(100000));
209 WVPASS(r.mem >= 100000);
211 r = MGTEST(100000, 50000, flags, write_memory(100000));
213 WVPASS(r.mem >= 100000);
216 ///////////////////////////////////////////////////////
217 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
219 WVPASS(r.mem >= 900000);
221 r = MGTEST(100000, 500000, flags, read_memory(1000000));
223 WVPASS(r.mem >= 900000);
225 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
227 WVPASS(r.mem >= 900000);
229 r = MGTEST(100000, 500000, flags, read_memory_rnd(1000000));
231 WVPASS(r.mem >= 900000);
233 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
235 WVPASS(r.mem >= 900000);
237 r = MGTEST(100000, 500000, flags, write_memory(1000000));
239 WVPASS(r.mem >= 900000);
247 int main(int argc, char *argv[])
252 pthread_t threads[MAX_CORES];
253 int cores[MAX_CORES];
254 int retvals[MAX_CORES];
256 /* TODO: currently shared memory */
257 for (int i = 0; i < sizeof(memory); i += 64)
261 cpu_mask = strtol(argv[1], NULL, 16);
263 for(int i=0; i<MAX_CORES; i++){
264 if(cpu_mask & (1 << i)){
269 int s = pthread_barrier_init(&barrier, NULL, cpu_count);
271 err(1, "pthread_barrier_init");
273 for(int i=0; i<MAX_CORES; i++){
274 if(cpu_mask & (1 << i)){
276 retvals[i] = pthread_create(&threads[i], NULL, test_thread, (void*) &cores[i]);
280 for(int i=0; i<MAX_CORES; i++){
281 if(cpu_mask & (1 << i)){
282 pthread_join(threads[i], NULL);