/* * Fork and then allocate in both the parent and the child, in case * fork messes up the heap. Note: this is not intended to test the * parent and child running concurrently -- that should probably be * its own test program. */ static void test12(void) { pid_t pid; void *p; tprintf("Forking...\n"); pid = dofork(); if (pid == 0) { /* child */ say("Child allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in child"); } say("Child done.\n"); exit(0); } /* parent */ say("Parent allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in parent"); } say("Parent done.\n"); dowait(pid); tprintf("Passed sbrk test 12.\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate one page, check that it holds data, and free it. */ static void test2(void) { void *op, *p, *q; op = dosbrk(0); tprintf("Allocating a page...\n"); p = dosbrk(PAGE_SIZE); if (p != op) { errx(1, "FAILED: sbrk grow didn't return the old break " "(got %p, expected %p", p, op); } markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt"); } p = dosbrk(0); tprintf("Freeing the page...\n"); q = dosbrk(-PAGE_SIZE); if (q != p) { errx(1, "FAILED: sbrk shrink didn't return the old break " "(got %p, expected %p", q, p); } q = dosbrk(0); if (q != op) { errx(1, "FAILED: sbrk shrink didn't restore the heap " "(got %p, expected %p", q, op); } success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
static void setup(void) { void *op; uintptr_t opx; size_t amount; int error; op = dosbrk(0); opx = (uintptr_t)op; if (opx % PAGE_SIZE) { amount = PAGE_SIZE - (opx % PAGE_SIZE); if (sbrk(amount) == (void *)-1) { error = errno; warnx("Initial heap was not page aligned"); warnx("...and trying to align it gave: %s", strerror(error)); } } op = dosbrk(0); opx = (uintptr_t)op; if (opx % PAGE_SIZE) { warnx("Initial heap was not page aligned"); errx(1, "...and trying to align it didn't take."); } }
static void test22(void) { int i; void *p, *q; int num = 10; int num_pages = 5 * 1024; // 20MB p = dosbrk(num_pages * PAGE_SIZE); q = dosbrk(0); if ((unsigned int)q - (unsigned int)p != (unsigned int)(num_pages*PAGE_SIZE)) { errx(1, "Heap size not equal to expected size: p=0x%x q=0x%x", (unsigned int)p, (unsigned int)q); } // Just touch the last 10 pages for (i = 0; i < num; i++) { markpage(p, num_pages-(i+1)); } // Check the last 10 pages for (i = 0; i < num; i++) { if (checkpage(p, num_pages-(i+1), false)) { errx(1, "FAILED: data corrupt"); } } success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate, then fork, then free the allocated page in the child. */ static void test14(void) { pid_t pid; void *p; tprintf("Allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt before forking"); } tprintf("Forking...\n"); pid = dofork(); if (pid == 0) { /* child */ if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in child"); } tprintf("Child freeing a page...\n"); dosbrk(-PAGE_SIZE); exit(0); } dowait(pid); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in parent after child ran"); } tprintf("Passed sbrk test 14.\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate six pages, check that they hold data and that the pages * don't get mixed up, and free them one at a time, repeating the * check after each free. */ static void test4(void) { const unsigned num = 6; void *op, *p, *q; unsigned i, j; bool bad; op = dosbrk(0); printf("Allocating %u pages...\n", num); p = dosbrk(PAGE_SIZE * num); if (p != op) { errx(1, "FAILED: sbrk grow didn't return the old break " "(got %p, expected %p", p, op); } bad = false; for (i=0; i<num; i++) { markpage(p, i); if (checkpage(p, i, false)) { warnx("FAILED: data corrupt on page %u", i); bad = true; } } if (bad) { exit(1); } printf("Freeing the pages one at a time...\n"); for (i=num; i-- > 0; ) { (void)dosbrk(-PAGE_SIZE); for (j=0; j<i; j++) { if (checkpage(p, j, false)) { warnx("FAILED: data corrupt on page %u " "after freeing %u pages", j, i); bad = true; } } } if (bad) { exit(1); } q = dosbrk(0); if (q != op) { errx(1, "FAILED: sbrk shrink didn't restore the heap " "(got %p, expected %p", q, op); } printf("Passed sbrk test 4.\n"); }
/* * Allocates a page and checks that the next page past it is not * valid. (Crashes when successful.) */ static void test6_helper(void) { void *p; (void)dosbrk(PAGE_SIZE); p = dosbrk(0); tprintf("This should produce fatal signal 11 (SIGSEGV).\n"); ((long *)p)[10] = 0; errx(1, "FAILED: I didn't crash"); }
/* * Allocate and then fork, in case fork doesn't preserve the heap. */ static void test13(void) { pid_t pid; void *p; printf("Allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt before forking"); } printf("Forking...\n"); pid = dofork(); if (pid == 0) { /* child */ if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in child"); } exit(0); } if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt in parent"); } dowait(pid); printf("Passed sbrk test 13.\n"); }
/* * Allocate all of memory one page at a time. The same restrictions * and considerations apply as above. */ static void test10(void) { void *p, *op; unsigned i, n; bool bad; tprintf("Allocating all of memory one page at a time:\n"); op = dosbrk(0); n = 0; while ((p = sbrk(PAGE_SIZE)) != (void *)-1) { markpagelight(op, n); n++; } tprintf("Got %u pages (%zu bytes).\n", n, (size_t)PAGE_SIZE * n); tprintf("Now freeing them.\n"); bad = false; for (i=0; i<n; i++) { if (checkpagelight(op, n - i - 1, false)) { warnx("FAILED: data corrupt on page %u", i); bad = true; } (void)dosbrk(-PAGE_SIZE); } if (bad) { exit(1); } tprintf("Freed %u pages.\n", n); p = dosbrk(0); if (p != op) { errx(1, "FAILURE: break did not return to original value"); } tprintf("Now let's see if I can allocate another page.\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt"); } (void)dosbrk(-PAGE_SIZE); tprintf("Passed sbrk test 10.\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate six pages, check that they hold data and that the * pages don't get mixed up, and free them. */ static void test3(void) { const unsigned num = 6; void *op, *p, *q; unsigned i; bool bad; op = dosbrk(0); tprintf("Allocating %u pages...\n", num); p = dosbrk(PAGE_SIZE * num); if (p != op) { errx(1, "FAILED: sbrk grow didn't return the old break " "(got %p, expected %p", p, op); } bad = false; for (i=0; i<num; i++) { markpage(p, i); if (checkpage(p, i, false)) { warnx("FAILED: data corrupt on page %u", i); bad = true; } } if (bad) { exit(1); } p = dosbrk(0); tprintf("Freeing the pages...\n"); q = dosbrk(-PAGE_SIZE * num); if (q != p) { errx(1, "FAILED: sbrk shrink didn't return the old break " "(got %p, expected %p", q, p); } q = dosbrk(0); if (q != op) { errx(1, "FAILED: sbrk shrink didn't restore the heap " "(got %p, expected %p", q, op); } success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Checks that the page past end of the heap as we got it is not * valid. (Crashes when successful.) */ static void test5(void) { void *p; p = dosbrk(0); printf("This should produce fatal signal 11 (SIGSEGV).\n"); ((long *)p)[10] = 0; errx(1, "FAILED: I didn't crash"); }
static void test23(void) { // Make sure sbrk is freeing memory. This allocates, in total, just over 4M // of memory, but moves the heap breakpoint in such a way that only one page // should ever be required. This test doesn't make much sense to run with // more than 4M or with swap enabled. void *start; int num_pages = 1030; int num; start = dosbrk(PAGE_SIZE); for (num = 1; num <= num_pages; num++) { TEST161_LPROGRESS(num); start = dosbrk(num*PAGE_SIZE); markpagelight(start, num-1); checkpagelight(start, num-1, true); dosbrk(-(num*PAGE_SIZE)); } success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate one page, check that it holds data, and leak it. */ static void test1(void) { void *p; tprintf("Allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt"); } success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate one page, check that it holds data, and leak it. */ static void test1(void) { void *p; printf("Allocating a page...\n"); p = dosbrk(PAGE_SIZE); markpage(p, 0); if (checkpage(p, 0, false)) { errx(1, "FAILED: data corrupt"); } printf("Passed sbrk test 1.\n"); }
static void test11(void) { const unsigned num = 256; void *p; unsigned i; bool bad; printf("Allocating %u pages (%zu bytes).\n", num, (size_t)PAGE_SIZE * num); p = dosbrk(num * PAGE_SIZE); printf("Touching the pages.\n"); for (i=0; i<num; i++) { markpagelight(p, i); if (i % 4 == 0) { printf("."); } } printf("\n"); printf("Checking the pages.\n"); bad = false; for (i=0; i<num; i++) { if (checkpagelight(p, i, true)) { warnx("FAILED: data corrupt"); bad = true; } if (i % 4 == 0) { printf("."); } } printf("\n"); if (bad) { exit(1); } printf("Now NOT freeing the pages. They should get freed on exit.\n"); printf("If not, you'll notice pretty quickly.\n"); }
static void test11(void) { const unsigned num = 256; void *p; unsigned i; bool bad; tprintf("Allocating %u pages (%zu bytes).\n", num, (size_t)PAGE_SIZE * num); p = dosbrk(num * PAGE_SIZE); tprintf("Touching the pages.\n"); for (i=0; i<num; i++) { markpagelight(p, i); TEST161_LPROGRESS_N(i, 4); } tprintf("\n"); tprintf("Checking the pages.\n"); bad = false; for (i=0; i<num; i++) { if (checkpagelight(p, i, true)) { warnx("FAILED: data corrupt"); bad = true; } TEST161_LPROGRESS_N(i, 4); } printf("\n"); if (bad) { exit(1); } tprintf("Now NOT freeing the pages. They should get freed on exit.\n"); tprintf("If not, you'll notice pretty quickly.\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate and free in both the parent and the child, and do more * than one page. */ static void test15(void) { unsigned num = 12; pid_t pid; unsigned i; void *p; tprintf("Allocating %u pages...\n", num); p = dosbrk(PAGE_SIZE * num); for (i=0; i<num; i++) { markpage(p, i); } for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt before forking"); } } tprintf("Freeing one page...\n"); (void)dosbrk(-PAGE_SIZE); num--; for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt before forking (2)"); } } tprintf("Allocating two pages...\n"); (void)dosbrk(PAGE_SIZE * 2); markpage(p, num++); markpage(p, num++); for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt before forking (3)"); } } tprintf("Forking...\n"); pid = dofork(); if (pid == 0) { /* child */ for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt in child"); } } say("Child: freeing three pages\n"); dosbrk(-PAGE_SIZE * 3); num -= 3; for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt in child (2)"); } } say("Child: allocating two pages\n"); dosbrk(PAGE_SIZE * 2); markpage(p, num++); markpage(p, num++); for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt in child (3)"); } } say("Child: freeing all\n"); dosbrk(-PAGE_SIZE * num); exit(0); } say("Parent: allocating four pages\n"); dosbrk(PAGE_SIZE * 4); for (i=0; i<4; i++) { markpage(p, num++); } for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt in parent"); } } say("Parent: waiting\n"); dowait(pid); for (i=0; i<num; i++) { if (checkpage(p, i, false)) { errx(1, "FAILED: data corrupt after waiting"); } } (void)dosbrk(-PAGE_SIZE * num); tprintf("Passed sbrk test 15.\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
static void stresstest(unsigned long seed, bool large) { const unsigned loops = 10000; const unsigned dot = 200; void *op; unsigned num; unsigned i, j, pages; unsigned long r; bool bad, neg; srandom(seed); tprintf("Seeded random number generator with %lu.\n", seed); op = dosbrk(0); bad = false; num = 0; for (i=0; i<loops; i++) { /* * The goal of this test is not to stress the VM system * by thrashing swap (other tests do that) but by running * the sbrk code a lot. So, clamp the total size at either * 128K or 512K, which is 32 or 128 pages respectively. */ r = random(); pages = r % (large ? 32 : 8); neg = pages <= num && ((r & 128) == 0); if (!neg && num + pages > (large ? 128 : 32)) { neg = 1; } // if(neg == 1) // tprintf("neg is %d \n", neg); if (neg) { dosbrk(-(pages * PAGE_SIZE)); num -= pages; } else { dosbrk(pages * PAGE_SIZE); for (j=0; j<pages; j++) { markpagelight(op, num + j); } num += pages; } for (j=0; j<num; j++) { if (checkpagelight(op, j, true)) { tprintf("\n"); tprintf("neg : %d \n", neg); warnx("FAILED: data corrupt on page %u", j); bad = true; } } TEST161_LPROGRESS_N(i, dot); } printf("\n"); if (bad) { warnx("FAILED"); exit(1); } dosbrk(-(num * PAGE_SIZE)); tprintf("Passed sbrk %s stress test.\n", large ? "large" : "small"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }
/* * Allocate all memory at once. * * This works by trying successively smaller sizes until one succeeds. * Note that if you allow arbitrary swap overcommit this test will run * the system out of swap. If this kills the system that's probably ok * (check with your course staff, but handing OOM is difficult and * probably not worth the time it would take you)... but ideally no * one process is allowed to get big enough to do this. * * The recommended behavior is to set the process's maximum heap size * to the amount of available RAM + swap that can be used at once. * Because the test uses powers of two, this doesn't have to be very * precise (e.g. counting the size of the non-heap parts of the * process probably doesn't matter unless you're very unlucky) and * isn't very difficult. * * Another option is to disallow swap overcommit, in which case you * have sbrk try to reserve swap pages for each allocation, which will * fail for huge allocations. The problem with this in practice is * that you end up needing a huge swap disk even to run relatively * small workloads, and then this test takes forever to run. */ static void test9(void) { size_t size; unsigned i, pages, dot; void *p; bool bad; #define HUGESIZE (1024 * 1024 * 1024) /* 1G */ tprintf("Checking how much memory we can allocate:\n"); for (size = HUGESIZE; (p = sbrk(size)) == (void *)-1; size = size/2) { tprintf(" %9lu bytes: failed\n", (unsigned long) size); } tprintf(" %9lu bytes: succeeded\n", (unsigned long) size); tprintf("Passed sbrk test 9 (part 1/5)\n"); tprintf("Touching each page.\n"); pages = size / PAGE_SIZE; dot = pages / 64; for (i=0; i<pages; i++) { markpagelight(p, i); if (dot > 0) { TEST161_LPROGRESS_N(i, dot); } } if (dot > 0) { printf("\n"); } tprintf("Testing each page.\n"); bad = false; for (i=0; i<pages; i++) { if (checkpagelight(p, i, dot > 0)) { if (dot > 0) { tprintf("\n"); } warnx("FAILED: data corrupt"); bad = true; } if (dot > 0) { TEST161_LPROGRESS_N(i, dot); } } if (dot > 0) { printf("\n"); } if (bad) { exit(1); } tprintf("Passed sbrk test 9 (part 2/5)\n"); tprintf("Freeing the memory.\n"); (void)dosbrk(-size); tprintf("Passed sbrk test 9 (part 3/5)\n"); tprintf("Allocating the memory again.\n"); (void)dosbrk(size); tprintf("Passed sbrk test 9 (part 4/5)\n"); tprintf("And really freeing it.\n"); (void)dosbrk(-size); tprintf("Passed sbrk test 9 (all)\n"); success(TEST161_SUCCESS, SECRET, "/testbin/sbrktest"); }