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.
8 * Check that mmap always returns page size aligned memory.
9 * Check that the requested length must be a multiple of page size.
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.
22 #include <sys/fcntl.h>
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"
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 */
41 static const uint32_t k_nacl_page_size = (1 << 16);
42 static const uint32_t k_probe_stride = (4 << 10);
45 * do not allow release probabilities that are too small, since we end
46 * up spinning through the alloc list over and over again.
48 static const double k_min_release_probability = 1.0e-4;
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:
56 * run test cycle num_test_cycles times:
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.
67 * for each mmap allocation, verify that the returned address
68 * (non-MAP_FIXED) is 64k page aligned.
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.
78 * at end, release all mmap allocated memory.
81 unsigned long get_good_seed(void) {
84 NaClSrpcChannel ns_channel;
85 NaClSrpcError rpc_result;
91 if (-1 == nacl_nameservice(&ns)) {
92 fprintf(stderr, "nacl_nameservice failed\n");
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");
103 rpc_result = NaClSrpcInvokeBySignature(&ns_channel,
104 NACL_NAME_SERVICE_LOOKUP,
105 "SecureRandom", O_RDONLY,
107 assert(NACL_SRPC_RESULT_OK == rpc_result);
108 assert(NACL_NAME_SERVICE_SUCCESS == status);
109 assert(sizeof seed == read(rng, &seed, sizeof seed));
111 NaClSrpcDtor(&ns_channel);
117 struct MemInfo *next;
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;
129 size_t max_memory_carried_forward;
130 size_t total_bytes_allocated;
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
140 struct MemInfo *alloc_list;
141 struct MemInfo **alloc_list_end;
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) {
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;
169 self->total_bytes_allocated = 0;
171 (void) srand48(seed);
173 self->alloc_list = NULL;
174 self->alloc_list_end = &self->alloc_list;
179 uint32_t RunTest(struct TestState *self) {
180 uint32_t error_count = 0;
182 uint32_t num_allocations_this_cycle;
187 int bad_request_size;
190 struct MemInfo **mipp;
193 for (cycle = 0; cycle < self->num_test_cycles; ++cycle) {
194 if (g_verbosity > 0) {
195 printf("Test Cycle %u\n", cycle);
198 if (self->max_mmaps_per_cycle > 1) {
200 num_allocations_this_cycle = (lrand % (self->max_mmaps_per_cycle - 1) +
203 num_allocations_this_cycle = 1;
206 if (g_verbosity > 0) {
207 printf("Will allocate %d times\n", num_allocations_this_cycle);
210 for (alloc_num = 0; alloc_num < num_allocations_this_cycle; ++alloc_num) {
213 num_bytes = lrand % (self->max_num_pages * k_nacl_page_size);
215 if (g_verbosity > 0) {
216 printf("random choice: %zd (0x%zx) bytes\n", num_bytes, num_bytes);
219 /* clear low order bits with probabilty 1-bad_size_probability */
221 bad_request_size = (drand < self->bad_size_probability);
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");
228 if (bad_request_size) {
229 if (0 == (num_bytes & (k_nacl_page_size - 1))) {
230 ++num_bytes; /* make sure it's bad */
233 num_bytes = num_bytes & ~(k_nacl_page_size - 1);
235 } while (0 == num_bytes);
237 if (g_verbosity > 0) {
238 printf("Now will allocate %zd (0x%zx) bytes\n", num_bytes, num_bytes);
241 mip = malloc(sizeof *mip);
243 perror("pagesize_test");
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);
253 if (MAP_FAILED == mip->mem_addr) {
254 fprintf(stderr, "mmap 0x%zx bytes failed\n", num_bytes);
259 if (g_verbosity > 1) {
260 printf("readable test\n");
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;
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);
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;
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);
288 for (mipp = &self->alloc_list; NULL != (mip = *mipp); mipp = &mip->next) {
289 double drand = drand48();
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);
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);
301 assert(self->total_bytes_allocated >= mip->mem_bytes);
302 self->total_bytes_allocated -= mip->mem_bytes;
304 if (NULL == mip->next) {
305 self->alloc_list_end = mipp;
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);
326 int main(int ac, char **av) {
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;
337 unsigned num_failures = 0;
339 struct TestState tstate;
341 while (-1 != (opt = getopt(ac, av, "c:m:M:n:p:r:s:v"))) {
344 max_memory_carried_forward = strtoull(optarg, (char **) NULL, 0);
347 max_num_pages = strtoul(optarg, (char **) NULL, 0);
350 max_mmaps_per_cycle = strtoul(optarg, (char **) NULL, 0);
353 num_test_cycles = strtoul(optarg, (char **) NULL, 0);
356 bad_size_probability = strtod(optarg, (char **) NULL);
359 release_probability = strtod(optarg, (char **) NULL);
363 seed = strtoul(optarg, (char **) NULL, 0);
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"
376 if (!NaClSrpcModuleInit()) {
377 fprintf(stderr, "SRPC module init failed\n");
380 if (!seed_provided) {
381 seed = get_good_seed();
383 printf("seed = %lu (0x%lx)\n", seed, seed);
385 if (!TestStateCtor(&tstate,
389 bad_size_probability,
391 max_memory_carried_forward,
393 fprintf(stderr, "Test State ctor failure\n");
397 if (g_verbosity > 0) {
398 printf("TestStateCtor succeeded, starting tests\n");
401 num_failures = RunTest(&tstate);
403 printf("Tests finished; %u errors\n", num_failures);
404 if (0 == num_failures) {
410 NaClSrpcModuleFini();