ad5d7ab5e48368aa1c6c3d3591aed85ee3d886c7
[platform/framework/web/crosswalk.git] / src / native_client / tests / pagesize / pagesize_test.c
1 /*
2  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 /*
8  * Check that mmap always returns page size aligned memory.
9  * Check that the requested length must be a multiple of page size.
10  *
11  * We use random.  The seed can be specified on the command line.  If
12  * not, we use the name service API to get access to the secure random
13  * number source to seed the generator, outputting the seed so the
14  * test is (hopefully) reproducible.
15  */
16
17 #include <assert.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <sys/fcntl.h>
23 #include <sys/mman.h>
24
25 #include "native_client/src/public/imc_syscalls.h"
26 #include "native_client/src/public/name_service.h"
27 #include "native_client/src/shared/srpc/nacl_srpc.h"
28
29 /* globals */
30 int g_verbosity = 0;
31
32 /* default values */
33 static const uint32_t k_num_test_cycles = 64;
34 static const uint32_t k_max_mmaps_per_cycle = 16;
35 static const uint32_t k_max_num_pages = 128;
36 static const double   k_bad_size_probability = 0.10;
37 static const double   k_release_probability = 0.05;
38 static const uint32_t k_max_memory_carried_forward = (1 << 20);
39 /* allow at most 2**24 or 16M to be carried forward between test cycles */
40
41 static const uint32_t k_nacl_page_size = (1 << 16);
42 static const uint32_t k_probe_stride = (4 << 10);
43
44 /*
45  * do not allow release probabilities that are too small, since we end
46  * up spinning through the alloc list over and over again.
47  */
48 static const double   k_min_release_probability = 1.0e-4;
49
50 /*
51  * In this experiment, we want to allocate memory using mmap of
52  * various request sizes and deallocate it in multiple test cycles.
53  * The choice of memory sizes for each mmap and whether or not to
54  * deallocate is probabilistic.  Essentially:
55  *
56  * run test cycle num_test_cycles times:
57  *
58  *   run a random number of allocations, up to max_mmaps_per_cycle
59  *   times where each mmap may allocate a random non-zero number of
60  *   64k pages up to max_num_pages (the product, if greater than some
61  *   value just shy of 1G, would imply mmap failure due to address
62  *   space exhaustion).  each mmap allocation may ask for a memory
63  *   block the size of which is bad (i.e., not a multiple of 64k) with
64  *   probability bad_size_probability.  in this case, expect either a
65  *   failure with EINVAL or a rounded length is used.
66  *
67  *   for each mmap allocation, verify that the returned address
68  *   (non-MAP_FIXED) is 64k page aligned.
69  *
70  *   deallocate some of the allocated memory.  do it randomly, but
71  *   simply: scan a free list and deallocate each block (which
72  *   corresponds to the allocation earlier -- we aren't testing for
73  *   munmaps that fragment a larger allocation in this test) with
74  *   probability release_probability, wrapping around as needed until
75  *   the total amount of memory still allocated is less than or equal
76  *   to max_memory_carried_forward.
77  *
78  * at end, release all mmap allocated memory.
79  */
80
81 unsigned long get_good_seed(void) {
82   int               ns;
83   int               connected_socket;
84   NaClSrpcChannel   ns_channel;
85   NaClSrpcError     rpc_result;
86   int               status;
87   int               rng;
88   unsigned long     seed;
89
90   ns = -1;
91   if (-1 == nacl_nameservice(&ns)) {
92     fprintf(stderr, "nacl_nameservice failed\n");
93     abort();
94   }
95   connected_socket = imc_connect(ns);
96   assert(-1 != connected_socket);
97   if (!NaClSrpcClientCtor(&ns_channel, connected_socket)) {
98     fprintf(stderr, "SRPC client channel ctor failed\n");
99     abort();
100   }
101   (void) close(ns);
102
103   rpc_result = NaClSrpcInvokeBySignature(&ns_channel,
104                                          NACL_NAME_SERVICE_LOOKUP,
105                                          "SecureRandom", O_RDONLY,
106                                          &status, &rng);
107   assert(NACL_SRPC_RESULT_OK == rpc_result);
108   assert(NACL_NAME_SERVICE_SUCCESS == status);
109   assert(sizeof seed == read(rng, &seed, sizeof seed));
110   close(rng);
111   NaClSrpcDtor(&ns_channel);
112
113   return seed;
114 }
115
116 struct MemInfo {
117   struct MemInfo  *next;
118   void            *mem_addr;
119   size_t          mem_bytes;
120 };
121
122 struct TestState {
123   uint32_t            num_test_cycles;
124   uint32_t            max_mmaps_per_cycle;
125   uint32_t            max_num_pages;
126   double              bad_size_probability;
127   double              release_probability;
128
129   size_t              max_memory_carried_forward;
130   size_t              total_bytes_allocated;
131
132   /*
133    * struct drand48_data rng_state; newlib has drand48_r, but it's
134    * different from glibc's -- newlibs take an REENT object as
135    * argument, as per newlib convention to hang reentrant state off of
136    * an explicit object used throughout reentrant versions of libc
137    * functions.
138    */
139
140   struct MemInfo      *alloc_list;
141   struct MemInfo      **alloc_list_end;
142 };
143
144 int TestStateCtor(struct TestState  *self,
145                   uint32_t          num_test_cycles,
146                   uint32_t          max_mmaps_per_cycle,
147                   uint32_t          max_num_pages,
148                   double            bad_size_probability,
149                   double            release_probability,
150                   size_t            max_memory_carried_forward,
151                   unsigned long     seed) {
152   /* validate ctor inputs */
153   if (0 == num_test_cycles ||
154       0 == max_mmaps_per_cycle ||
155       0 == max_num_pages ||
156       bad_size_probability < 0.0 ||
157       1.0 < bad_size_probability ||
158       release_probability < k_min_release_probability ||
159       1.0 < release_probability) {
160     return 0;
161   }
162   self->num_test_cycles = num_test_cycles;
163   self->max_mmaps_per_cycle = max_mmaps_per_cycle;
164   self->max_num_pages = max_num_pages;
165   self->bad_size_probability = bad_size_probability;
166   self->release_probability = release_probability;
167   self->max_memory_carried_forward = max_memory_carried_forward;
168
169   self->total_bytes_allocated = 0;
170
171   (void) srand48(seed);
172
173   self->alloc_list = NULL;
174   self->alloc_list_end = &self->alloc_list;
175
176   return 1;
177 }
178
179 uint32_t RunTest(struct TestState *self) {
180   uint32_t        error_count = 0;
181   uint32_t        cycle;
182   uint32_t        num_allocations_this_cycle;
183   long int        lrand;
184   uint32_t        alloc_num;
185   size_t          num_bytes;
186   double          drand;
187   int             bad_request_size;
188   uintptr_t       addr;
189   struct MemInfo  *mip;
190   struct MemInfo  **mipp;
191   struct MemInfo  *tmp;
192
193   for (cycle = 0; cycle < self->num_test_cycles; ++cycle) {
194     if (g_verbosity > 0) {
195       printf("Test Cycle %u\n", cycle);
196     }
197
198     if (self->max_mmaps_per_cycle > 1) {
199       lrand = lrand48();
200       num_allocations_this_cycle = (lrand % (self->max_mmaps_per_cycle - 1) +
201                                     1);
202     } else {
203       num_allocations_this_cycle = 1;
204     }
205
206     if (g_verbosity > 0) {
207       printf("Will allocate %d times\n", num_allocations_this_cycle);
208     }
209
210     for (alloc_num = 0; alloc_num < num_allocations_this_cycle; ++alloc_num) {
211       do {
212         lrand = lrand48();
213         num_bytes = lrand % (self->max_num_pages * k_nacl_page_size);
214
215         if (g_verbosity > 0) {
216           printf("random choice: %zd (0x%zx) bytes\n", num_bytes, num_bytes);
217         }
218
219         /* clear low order bits with probabilty 1-bad_size_probability */
220         drand = drand48();
221         bad_request_size = (drand < self->bad_size_probability);
222
223         if (g_verbosity > 0) {
224           printf("We will%s make the size not a multipe of page size\n",
225                  bad_request_size ? "" : " not");
226         }
227
228         if (bad_request_size) {
229           if (0 == (num_bytes & (k_nacl_page_size - 1))) {
230             ++num_bytes;  /* make sure it's bad */
231           }
232         } else {
233           num_bytes = num_bytes & ~(k_nacl_page_size - 1);
234         }
235       } while (0 == num_bytes);
236
237       if (g_verbosity > 0) {
238         printf("Now will allocate %zd (0x%zx) bytes\n", num_bytes, num_bytes);
239       }
240
241       mip = malloc(sizeof *mip);
242       if (0 == mip) {
243         perror("pagesize_test");
244         abort();
245       }
246       mip->next = NULL;
247       mip->mem_bytes = num_bytes;
248       mip->mem_addr = mmap(NULL, num_bytes, PROT_READ | PROT_WRITE,
249                            MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t) 0);
250       if (g_verbosity > 1) {
251         printf("mmap returned %p\n", mip->mem_addr);
252       }
253       if (MAP_FAILED == mip->mem_addr) {
254         fprintf(stderr, "mmap 0x%zx bytes failed\n", num_bytes);
255         ++error_count;
256         free(mip);
257         continue;
258       }
259       if (g_verbosity > 1) {
260         printf("readable test\n");
261       }
262       /* ensure memory region is readable */
263       for (addr = (uintptr_t) mip->mem_addr;
264            addr < (uintptr_t) mip->mem_addr + num_bytes;
265            addr += k_probe_stride) {
266         *(char volatile *) addr;
267       }
268
269       if (0 != ((uintptr_t) mip->mem_addr & (k_nacl_page_size - 1))) {
270         fprintf(stderr, "address %p not page aligned\n",
271                 (void *) mip->mem_addr);
272         ++error_count;
273       }
274
275       /* save allocation to memory list */
276       *self->alloc_list_end = mip;
277       self->alloc_list_end = &mip->next;
278       self->total_bytes_allocated += num_bytes;
279     }
280     /* free most(?) of allocated memory */
281     while (self->total_bytes_allocated > self->max_memory_carried_forward) {
282       if (g_verbosity > 0) {
283         printf("release probability %f\n", self->release_probability);
284         printf("allocated %zx, max %zx\n",
285                self->total_bytes_allocated, self->max_memory_carried_forward);
286         printf("head of list %p\n", (void *) self->alloc_list);
287       }
288       for (mipp = &self->alloc_list; NULL != (mip = *mipp); mipp = &mip->next) {
289         double drand = drand48();
290
291         if (g_verbosity > 0) {
292           printf("at %p, prob %f, addr %p, %zx\n", (void *) mip, drand,
293                  (void *) mip->mem_addr, mip->mem_bytes);
294         }
295         if (drand < self->release_probability) {
296           if (-1 == munmap(mip->mem_addr, mip->mem_bytes)) {
297             fprintf(stderr, "munmap 0x%p 0x%zx failed\n",
298                     (void *) mip->mem_addr, mip->mem_bytes);
299             ++error_count;
300           }
301           assert(self->total_bytes_allocated >= mip->mem_bytes);
302           self->total_bytes_allocated -= mip->mem_bytes;
303           *mipp = mip->next;
304           if (NULL == mip->next) {
305             self->alloc_list_end = mipp;
306           }
307           free(mip);
308           break;
309         }
310       }
311     }
312   }
313   /* free any memory not already munmapped */
314   for (mip = self->alloc_list; NULL != mip; mip = tmp) {
315     if (-1 == munmap(mip->mem_addr, mip->mem_bytes)) {
316       fprintf(stderr, "cleanup munmap 0x%p 0x%zx failed\n",
317               (void *) mip->mem_addr, mip->mem_bytes);
318       ++error_count;
319     }
320     tmp = mip->next;
321     free(mip);
322   }
323   return error_count;
324 }
325
326 int main(int ac, char **av) {
327   int               opt;
328   unsigned long     seed = 0;
329   int               seed_provided = 0;
330   uint32_t          max_num_pages = k_max_num_pages;
331   uint32_t          max_mmaps_per_cycle = k_max_mmaps_per_cycle;
332   uint32_t          num_test_cycles = k_num_test_cycles;
333   double            bad_size_probability = k_bad_size_probability;
334   double            release_probability = k_release_probability;
335   size_t            max_memory_carried_forward = k_max_memory_carried_forward;
336
337   unsigned          num_failures = 0;
338
339   struct TestState  tstate;
340
341   while (-1 != (opt = getopt(ac, av, "c:m:M:n:p:r:s:v"))) {
342     switch (opt) {
343       case 'c':
344         max_memory_carried_forward = strtoull(optarg, (char **) NULL, 0);
345         break;
346       case 'm':
347         max_num_pages = strtoul(optarg, (char **) NULL, 0);
348         break;
349       case 'M':
350         max_mmaps_per_cycle = strtoul(optarg, (char **) NULL, 0);
351         break;
352       case 'n':
353         num_test_cycles = strtoul(optarg, (char **) NULL, 0);
354         break;
355       case 'p':
356         bad_size_probability = strtod(optarg, (char **) NULL);
357         break;
358       case 'r':
359         release_probability = strtod(optarg, (char **) NULL);
360         break;
361       case 's':
362         seed_provided = 1;
363         seed = strtoul(optarg, (char **) NULL, 0);
364         break;
365       case 'v':
366         ++g_verbosity;
367         break;
368       default:
369         fprintf(stderr,
370                 "Usage: pagesize_test [-v] [-m max_pages_per_allocation]\n"
371                 "       [-M mmaps_per_test_cycle] [-n num_test_cycles]\n"
372                 "       [-p bad_size_probability] [-r release_probability]\n"
373                 "       [-s seed]\n");
374     }
375   }
376   if (!NaClSrpcModuleInit()) {
377     fprintf(stderr, "SRPC module init failed\n");
378     return 1;
379   }
380   if (!seed_provided) {
381     seed = get_good_seed();
382   }
383   printf("seed = %lu (0x%lx)\n", seed, seed);
384
385   if (!TestStateCtor(&tstate,
386                      num_test_cycles,
387                      max_mmaps_per_cycle,
388                      max_num_pages,
389                      bad_size_probability,
390                      release_probability,
391                      max_memory_carried_forward,
392                      seed)) {
393     fprintf(stderr, "Test State ctor failure\n");
394     return 1;
395   }
396
397   if (g_verbosity > 0) {
398     printf("TestStateCtor succeeded, starting tests\n");
399   }
400
401   num_failures = RunTest(&tstate);
402
403   printf("Tests finished; %u errors\n", num_failures);
404   if (0 == num_failures) {
405     printf("PASSED\n");
406   } else {
407     printf("FAILED\n");
408   }
409
410   NaClSrpcModuleFini();
411   return num_failures;
412 }