Scott Haseley 0ab862abfa 1) Moved tprintf and related functions to their own file in common/libc/printf/tprintf.c.
This file is included by both libc and hostcompat.

2) Changed printf -> tprintf in all testbin programs
2016-01-15 13:33:11 -05:00

1149 lines
23 KiB
C

/*
* Copyright (c) 2014
* The President and Fellows of Harvard College.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#define _PATH_RANDOM "random:"
/*
* Caution: OS/161 doesn't provide any way to get this properly from
* the kernel. The page size is 4K on almost all hardware... but not
* all. If porting to certain weird machines this will need attention.
*/
#define PAGE_SIZE 4096
////////////////////////////////////////////////////////////
// support code
static
int
geti(void)
{
int val=0;
int ch, digits=0;
while (1) {
ch = getchar();
if (ch=='\n' || ch=='\r') {
putchar('\n');
break;
}
else if ((ch=='\b' || ch==127) && digits>0) {
tprintf("\b \b");
val = val/10;
digits--;
}
else if (ch>='0' && ch<='9') {
putchar(ch);
val = val*10 + (ch-'0');
digits++;
}
else {
putchar('\a');
}
}
if (digits==0) {
return -1;
}
return val;
}
static
unsigned long
getseed(void)
{
int fd, len;
unsigned long seed;
fd = open(_PATH_RANDOM, O_RDONLY);
if (fd < 0) {
err(1, "%s", _PATH_RANDOM);
}
len = read(fd, &seed, sizeof(seed));
if (len < 0) {
err(1, "%s", _PATH_RANDOM);
}
else if (len < (int)sizeof(seed)) {
errx(1, "%s: Short read", _PATH_RANDOM);
}
close(fd);
return seed;
}
static
pid_t
dofork(void)
{
pid_t pid;
pid = fork();
if (pid < 0) {
err(1, "fork");
}
return pid;
}
static
void
dowait(pid_t pid)
{
int status;
int result;
result = waitpid(pid, &status, 0);
if (result == -1) {
err(1, "waitpid");
}
if (WIFSIGNALED(status)) {
errx(1, "child: Signal %d", WTERMSIG(status));
}
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
errx(1, "child: Exit %d", WEXITSTATUS(status));
}
}
static
void
say(const char *msg)
{
/* Use one write so it's atomic (tprintf usually won't be) */
write(STDOUT_FILENO, msg, strlen(msg));
}
////////////////////////////////////////////////////////////
// memory checking
/*
* Fill a page of memory with a test pattern.
*/
static
void
markpage(volatile void *baseptr, unsigned pageoffset)
{
volatile char *pageptr;
size_t n, i;
volatile unsigned long *pl;
unsigned long val;
pageptr = baseptr;
pageptr += (size_t)PAGE_SIZE * pageoffset;
pl = (volatile unsigned long *)pageptr;
n = PAGE_SIZE / sizeof(unsigned long);
for (i=0; i<n; i++) {
val = ((unsigned long)i ^ (unsigned long)pageoffset);
pl[i] = val;
}
}
/*
* Check a page marked with markblock()
*/
static
int
checkpage(volatile void *baseptr, unsigned pageoffset, bool neednl)
{
volatile char *pageptr;
size_t n, i;
volatile unsigned long *pl;
unsigned long val;
pageptr = baseptr;
pageptr += (size_t)PAGE_SIZE * pageoffset;
pl = (volatile unsigned long *)pageptr;
n = PAGE_SIZE / sizeof(unsigned long);
for (i=0; i<n; i++) {
val = ((unsigned long)i ^ (unsigned long)pageoffset);
if (pl[i] != val) {
if (neednl) {
tprintf("\n");
}
tprintf("FAILED: data mismatch at offset %lu of page "
"at 0x%lx: %lu vs. %lu\n",
(unsigned long) (i*sizeof(unsigned long)),
(unsigned long)(uintptr_t)pl,
pl[i], val);
return -1;
}
}
return 0;
}
/*
* Light version; touches just the first word of a page.
*/
static
void
markpagelight(volatile void *baseptr, unsigned pageoffset)
{
volatile char *pageptr;
volatile unsigned long *pl;
pageptr = baseptr;
pageptr += (size_t)PAGE_SIZE * pageoffset;
pl = (volatile unsigned long *)pageptr;
pl[0] = pageoffset;
}
/*
* Light version; checks just the first word of a page.
*/
static
int
checkpagelight(volatile void *baseptr, unsigned pageoffset, bool neednl)
{
volatile char *pageptr;
volatile unsigned long *pl;
pageptr = baseptr;
pageptr += (size_t)PAGE_SIZE * pageoffset;
pl = (volatile unsigned long *)pageptr;
if (pl[0] != pageoffset) {
if (neednl) {
tprintf("\n");
}
tprintf("FAILED: data mismatch at offset 0 of page "
"at 0x%lx: %lu vs. %u\n",
(unsigned long)(uintptr_t)pl,
pl[0], pageoffset);
return -1;
}
return 0;
}
////////////////////////////////////////////////////////////
// error wrapper
static
void *
dosbrk(ssize_t size)
{
void *p;
p = sbrk(size);
if (p == (void *)-1) {
err(1, "FAILED: sbrk");
}
if (p == NULL) {
errx(1, "FAILED: sbrk returned NULL, which is illegal");
}
return p;
}
////////////////////////////////////////////////////////////
// align the heap
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.");
}
}
////////////////////////////////////////////////////////////
// simple allocation
/*
* 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");
}
tprintf("Passed sbrk test 1.\n");
}
/*
* 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);
}
tprintf("Passed sbrk test 2.\n");
}
/*
* 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);
}
tprintf("Passed sbrk test 3.\n");
}
/*
* 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);
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);
}
tprintf("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);
}
tprintf("Passed sbrk test 4.\n");
}
////////////////////////////////////////////////////////////
// crashing off the end
/*
* 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);
tprintf("This should produce fatal signal 11 (SIGSEGV).\n");
((long *)p)[10] = 0;
errx(1, "FAILED: I didn't crash");
}
/*
* Allocates a page and checks that the next page past it is not
* valid. (Crashes when successful.)
*/
static
void
test6(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");
}
/*
* Allocates and frees a page and checks that the page freed is no
* longer valid. (Crashes when successful.)
*/
static
void
test7(void)
{
void *p;
(void)dosbrk(PAGE_SIZE);
(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");
}
/*
* Allocates some pages, frees half of them, and checks that the page
* past the new end of the heap is no longer valid. (Crashes when
* successful.)
*/
static
void
test8(void)
{
void *p;
(void)dosbrk(PAGE_SIZE * 12);
(void)dosbrk(-PAGE_SIZE * 6);
p = dosbrk(0);
tprintf("This should produce fatal signal 11 (SIGSEGV).\n");
((long *)p)[10] = 0;
errx(1, "FAILED: I didn't crash");
}
////////////////////////////////////////////////////////////
// heap size
/*
* 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 && i % dot == 0) {
tprintf(".");
}
}
if (dot > 0) {
tprintf("\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 && i % dot == 0) {
tprintf(".");
}
}
if (dot > 0) {
tprintf("\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");
}
/*
* 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");
}
////////////////////////////////////////////////////////////
// leaking and cleanup on exit
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);
if (i % 4 == 0) {
tprintf(".");
}
}
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;
}
if (i % 4 == 0) {
tprintf(".");
}
}
tprintf("\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");
}
////////////////////////////////////////////////////////////
// forking
/*
* 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");
}
/*
* Allocate and then fork, in case fork doesn't preserve the heap.
*/
static
void
test13(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");
}
exit(0);
}
if (checkpage(p, 0, false)) {
errx(1, "FAILED: data corrupt in parent");
}
dowait(pid);
tprintf("Passed sbrk test 13.\n");
}
/*
* 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");
}
/*
* 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");
}
////////////////////////////////////////////////////////////
// stress testing
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) {
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");
warnx("FAILED: data corrupt on page %u", j);
bad = true;
}
}
if (i % dot == 0) {
tprintf(".");
}
}
tprintf("\n");
if (bad) {
warnx("FAILED");
exit(1);
}
dosbrk(-(num * PAGE_SIZE));
tprintf("Passed sbrk %s stress test.\n", large ? "large" : "small");
}
static
void
test16(void)
{
stresstest(0, false);
}
static
void
test17(void)
{
stresstest(getseed(), false);
}
static
void
test18(void)
{
tprintf("Enter random seed: ");
stresstest(geti(), false);
}
static
void
test19(void)
{
stresstest(0, true);
}
static
void
test20(void)
{
stresstest(getseed(), true);
}
static
void
test21(void)
{
tprintf("Enter random seed: ");
stresstest(geti(), true);
}
////////////////////////////////////////////////////////////
// main
static const struct {
int num;
const char *desc;
void (*func)(void);
} tests[] = {
{ 1, "Allocate one page", test1 },
{ 2, "Allocate and free one page", test2 },
{ 3, "Allocate and free several pages", test3 },
{ 4, "Allocate several pages and free them one at a time", test4 },
{ 5, "Check the heap end (crashes)", test5 },
{ 6, "Allocate and check the heap end (crashes)", test6 },
{ 7, "Allocate and free and check the heap end (crashes)", test7 },
{ 8, "Allocate several, free some, check heap end (crashes)", test8 },
{ 9, "Allocate all memory in a big chunk", test9 },
{ 10, "Allocate all memory a page at a time", test10 },
{ 11, "Allocate a lot and intentionally leak it", test11 },
{ 12, "Fork and then allocate", test12 },
{ 13, "Allocate and then fork", test13 },
{ 14, "Allocate and then fork and free", test14 },
{ 15, "Allocate, fork, allocate more, and free (and spam)", test15 },
{ 16, "Small stress test", test16 },
{ 17, "Randomized small stress test", test17 },
{ 18, "Small stress test with particular seed", test18 },
{ 19, "Large stress test", test19 },
{ 20, "Randomized large stress test", test20 },
{ 21, "Large stress test with particular seed", test21 },
};
static const unsigned numtests = sizeof(tests) / sizeof(tests[0]);
static
int
dotest(int tn)
{
unsigned i;
for (i=0; i<numtests; i++) {
if (tests[i].num == tn) {
tests[i].func();
return 0;
}
}
return -1;
}
int
main(int argc, char *argv[])
{
int i, tn;
unsigned j;
bool menu = true;
setup();
if (argc > 1) {
for (i=1; i<argc; i++) {
dotest(atoi(argv[i]));
}
return 0;
}
while (1) {
if (menu) {
for (j=0; j<numtests; j++) {
tprintf(" %2d %s\n", tests[j].num,
tests[j].desc);
}
menu = false;
}
tprintf("sbrktest: ");
tn = geti();
if (tn < 0) {
break;
}
if (dotest(tn)) {
menu = true;
}
}
return 0;
}