Question: Memory management in Linux. Questions to be answered: 1) Why is it so important that adjacent free blocks not be left as such? What would

Memory management in Linux.

Questions to be answered:

1) Why is it so important that adjacent free blocks not be left as such? What would happen if they were permitted?

2) Which function(s) need to be concerned about adjacent free blocks?

3) Name one advantage of each strategy.

4) Run the stress test on all strategies, and look at the results (tests.log). What is the significance of "Average largest free block"? Which strategy generally has the best performance in this metric? Why do you think this is?

5) In the stress test results (see Question 4), what is the significance of "Average number of small blocks"? Which strategy generally has the best performance in this metric? Why do you think this is?

6) Eventually, the many mallocs and frees produces many small blocks scattered across the memory pool. There may be enough space to allocate a new block, but not in one place. It is possible to compact the memory, so all the free blocks are moved to one large free block. How would

you implement this in the system you have built?

7) If you did implement memory compaction, what changes would you need to make in how such a system is invoked (i.e. from a user's perspective)?

8) How would you use the system you have built to implement realloc? (Brief explanation; no code)

9) Which function(s) need to know which strategy is being used? Briefly explain why this/these and not others.

10) Give one advantage of implementing memory management using a linked list over a bit array, where every bit tells whether its corresponding byte is allocated.

Description:

This machine problem will focus on memory. You will implement your own version of malloc() and free(), using a variety of allocation strategies. You will be implementing a memory manager for a block of memory. You will implement routines for allocating and deallocating memory, and keeping track of what memory is in use. You will implement four strategies for selecting in which block to place a new requested memory black:

1) First-fit: select the first suitable block with smallest address. 2) Best-fit: select the smallest suitable block. 3) Worst-fit: select the largest suitable block. 4) Next-fit: select the first suitable block after the last block allocated (with wraparound from end to

beginning). Here, "suitable" means "free, and large enough to fit the new data".

Here are the functions you will need to implement: 1) initmem(): Initialize memory structures. 2) mymalloc(): Like malloc(), this allocates a new block of memory. 3) myfree(): Like free(), this deallocates a block of memory. 4) mem_holes(): How many free blocks are in memory? 5) mem_allocated(): How much memory is currently allocated? 6) mem_free(): How much memory is NOT allocated? 7) mem_largest_free(): How large is the largest free block? 8) mem_small_free(): How many small unallocated blocks are currently in memory? 9) mem_is_alloc(): Is a particular byte allocated or not?

We have given you a structure to use to implement these functions. It is a doubly-linked list of blocks in memory (both allocated and free blocks). Every malloc and free can create new blocks, or combine existing blocks. You may modify this structure, or even use a different one entirely. However, do not change function prototypes or files other than mymem.c.

IMPORTANT NOTE: Regardless of how you implement memory management, make sure that there are no adjacent free blocks. Any such blocks should be merged into one large block.

We have also given you a few functions to help you monitor what happens when you call your functions. Most important is the try_mymem() function. If you run your code with "mem -try ", it will call this function, which you can use to demonstrate the effects of your memory operations. These functions have no effect on test code, so use them to your advantage.

Running your Code:

After running "make", run

1) "mem" to see the available tests and strategies.

2) "mem -test " to test your code with our tests.

3) "mem -try " to run your code with your own tests (the try_mymem function).

You can also use "make test" and "make stage1-test" for testing. "make stage1-test" only runs the tests relevant to stage 1.

As in previous assignments, running "mem -test -f0 ..." will allow tests to run even after previous tests have failed. Similarly, using "all" for a test or strategy name runs all of the tests or strategies. Note that if "all" is selected as the strategy, the 4 tests are shown as one.

One of the tests, "stress", runs an assortment of randomized tests on each strategy. The results of the tests are placed in "tests.log". You may want to view this file to see the relative performance of each strategy.

Stage 1:

Implement all the above functions for the first-fit strategy. Use "mem test all first" to test your implementation.

Stage 2:

Implement the other three strategies: worst-fit, best-fit, and next-fit. The strategy is passed to initmem(), and stored in the global variable "myStrategy". Some of your functions will need to check this variable to implement the correct strategy.

You can test your code with "mem -test all worst", etc., or test all 4 together with "mem -test all all". The latter command does not test the strategies separately; your code passes the test only if all four strategies pass.

mymem.c

#include

#include

#include

#include

#include "mymem.h"

#include

/* The main structure for implementing memory allocation.

* You may change this to fit your implementation.

*/

struct memoryList

{

// doubly-linked list

struct memoryList *last;

struct memoryList *next;

int size; // How many bytes in this block?

char alloc; // 1 if this block is allocated,

// 0 if this block is free.

void *ptr; // location of block in memory pool.

};

strategies myStrategy = NotSet; // Current strategy

size_t mySize;

void *myMemory = NULL;

static struct memoryList *head;

static struct memoryList *next;

/* initmem must be called prior to mymalloc and myfree.

initmem may be called more than once in a given exeuction;

when this occurs, all memory you previously malloc'ed *must* be freed,

including any existing bookkeeping data.

strategy must be one of the following:

- "best" (best-fit)

- "worst" (worst-fit)

- "first" (first-fit)

- "next" (next-fit)

sz specifies the number of bytes that will be available, in total, for all mymalloc requests.

*/

void initmem(strategies strategy, size_t sz)

{

myStrategy = strategy;

/* all implementations will need an actual block of memory to use */

mySize = sz;

if (myMemory != NULL) free(myMemory); /* in case this is not the first time initmem2 is called */

/* TODO: release any other memory you were using for bookkeeping when doing a re-initialization! */

myMemory = malloc(sz);

/* TODO: Initialize memory management structure. */

}

/* Allocate a block of memory with the requested size.

* If the requested block is not available, mymalloc returns NULL.

* Otherwise, it returns a pointer to the newly allocated block.

* Restriction: requested >= 1

*/

void *mymalloc(size_t requested)

{

assert((int)myStrategy > 0);

switch (myStrategy)

{

case NotSet:

return NULL;

case First:

return NULL;

case Best:

return NULL;

case Worst:

return NULL;

case Next:

return NULL;

}

return NULL;

}

/* Frees a block of memory previously allocated by mymalloc. */

void myfree(void* block)

{

return;

}

/****** Memory status/property functions ******

* Implement these functions.

* Note that when we refer to "memory" here, we mean the

* memory pool this module manages via initmem/mymalloc/myfree.

*/

/* Get the number of contiguous areas of free space in memory. */

int mem_holes()

{

return 0;

}

/* Get the number of bytes allocated */

int mem_allocated()

{

return 0;

}

/* Number of non-allocated bytes */

int mem_free()

{

return 0;

}

/* Number of bytes in the largest contiguous area of unallocated memory */

int mem_largest_free()

{

return 0;

}

/* Number of free blocks smaller than "size" bytes. */

int mem_small_free(int size)

{

return 0;

}

char mem_is_alloc(void *ptr)

{

return 0;

}

/*

* Feel free to use these functions, but do not modify them.

* The test code uses them, but you may ind them useful.

*/

//Returns a pointer to the memory pool.

void *mem_pool()

{

return myMemory;

}

// Returns the total number of bytes in the memory pool. */

int mem_total()

{

return mySize;

}

// Get string name for a strategy.

char *strategy_name(strategies strategy)

{

switch (strategy)

{

case Best:

return "best";

case Worst:

return "worst";

case First:

return "first";

case Next:

return "next";

default:

return "unknown";

}

}

// Get strategy from name.

strategies strategyFromString(char * strategy)

{

if (!strcmp(strategy,"best"))

{

return Best;

}

else if (!strcmp(strategy,"worst"))

{

return Worst;

}

else if (!strcmp(strategy,"first"))

{

return First;

}

else if (!strcmp(strategy,"next"))

{

return Next;

}

else

{

return 0;

}

}

/*

* These functions are for you to modify however you see fit. These will not

* be used in tests, but you may find them useful for debugging.

*/

/* Use this function to print out the current contents of memory. */

void print_memory()

{

return;

}

/* Use this function to track memory allocation performance.

* This function does not depend on your implementation,

* but on the functions you wrote above.

*/

void print_memory_status()

{

printf("%d out of %d bytes allocated. ",mem_allocated(),mem_total());

printf("%d bytes are free in %d holes; maximum allocatable block is %d bytes. ",mem_free(),mem_holes(),mem_largest_free());

printf("Average hole size is %f. ",((float)mem_free())/mem_holes());

}

/* Use this function to see what happens when your malloc and free

* implementations are called. Run "mem -try " to call this function.

* We have given you a simple example to start.

*/

void try_mymem(int argc, char **argv) {

strategies strat;

void *a, *b, *c, *d, *e;

if(argc > 1)

strat = strategyFromString(argv[1]);

else

strat = First;

/* A simple example.

Each algorithm should produce a different layout. */

initmem(strat,500);

a = mymalloc(100);

b = mymalloc(100);

c = mymalloc(100);

myfree(b);

d = mymalloc(50);

myfree(a);

e = mymalloc(25);

print_memory();

print_memory_status();

}

mymem.h

#include

typedef enum strategies_enum

{

NotSet = 0,

Best = 1,

Worst = 2,

First = 3,

Next = 4

} strategies;

char *strategy_name(strategies strategy);

strategies strategyFromString(char * strategy);

void initmem(strategies strategy, size_t sz);

void *mymalloc(size_t requested);

void myfree(void* block);

int mem_holes();

int mem_allocated();

int mem_free();

int mem_total();

int mem_largest_free();

int mem_small_free(int size);

char mem_is_alloc(void *ptr);

void* mem_pool();

void print_memory();

void print_memory_status();

void try_mymem(int argc, char **argv);

memorytests.c

#include

#include

#include

#include

#include

#include

#include

#include "mymem.h"

#include "testrunner.h"

/* performs a randomized test:

totalSize == the total size of the memory pool, as passed to initmem2

totalSize must be less than 10,000 * minBlockSize

fillRatio == when the allocated memory is >= fillRatio * totalSize, a block is freed;

otherwise, a new block is allocated.

If a block cannot be allocated, this is tallied and a random block is freed immediately thereafter in the next iteration

minBlockSize, maxBlockSize == size for allocated blocks is picked uniformly at random between these two numbers, inclusive

*/

void do_randomized_test(int strategyToUse, int totalSize, float fillRatio, int minBlockSize, int maxBlockSize, int iterations)

{

void * pointers[10000];

int storedPointers = 0;

int strategy;

int lbound = 1;

int ubound = 4;

int smallBlockSize = maxBlockSize/10;

if (strategyToUse>0)

lbound=ubound=strategyToUse;

FILE *log;

log = fopen("tests.log","a");

if(log == NULL) {

perror("Can't append to log file. ");

return;

}

fprintf(log,"Running randomized tests: pool size == %d, fill ratio == %f, block size is from %d to %d, %d iterations ",totalSize,fillRatio,minBlockSize,maxBlockSize,iterations);

fclose(log);

for (strategy = lbound; strategy <= ubound; strategy++)

{

double sum_largest_free = 0;

double sum_hole_size = 0;

double sum_allocated = 0;

int failed_allocations = 0;

double sum_small = 0;

struct timespec execstart, execend;

int force_free = 0;

int i;

storedPointers = 0;

initmem(strategy,totalSize);

clock_gettime(CLOCK_REALTIME, &execstart);

for (i = 0; i < iterations; i++)

{

if ( (i % 10000)==0 )

srand ( time(NULL) );

if (!force_free && (mem_free() > (totalSize * (1-fillRatio))))

{

int newBlockSize = (rand()%(maxBlockSize-minBlockSize+1))+minBlockSize;

/* allocate */

void * pointer = mymalloc(newBlockSize);

if (pointer != NULL)

pointers[storedPointers++] = pointer;

else

{

failed_allocations++;

force_free = 1;

}

}

else

{

int chosen;

void * pointer;

/* free */

force_free = 0;

if (storedPointers == 0)

continue;

chosen = rand() % storedPointers;

pointer = pointers[chosen];

pointers[chosen] = pointers[storedPointers-1];

storedPointers--;

myfree(pointer);

}

sum_largest_free += mem_largest_free();

sum_hole_size += (mem_free() / mem_holes());

sum_allocated += mem_allocated();

sum_small += mem_small_free(smallBlockSize);

}

clock_gettime(CLOCK_REALTIME, &execend);

log = fopen("tests.log","a");

if(log == NULL) {

perror("Can't append to log file. ");

return;

}

fprintf(log,"\t=== %s === ",strategy_name(strategy));

fprintf(log,"\tTest took %.2fms. ", (execend.tv_sec - execstart.tv_sec) * 1000 + (execend.tv_nsec - execstart.tv_nsec) / 1000000.0);

fprintf(log,"\tAverage hole size: %f ",sum_hole_size/iterations);

fprintf(log,"\tAverage largest free block: %f ",sum_largest_free/iterations);

fprintf(log,"\tAverage allocated bytes: %f ",sum_allocated/iterations);

fprintf(log,"\tAverage number of small blocks: %f ",sum_small/iterations);

fprintf(log,"\tFailed allocations: %d ",failed_allocations);

fclose(log);

}

}

/* run randomized tests against the various strategies with various parameters */

int do_stress_tests(int argc, char **argv)

{

int strategy = strategyFromString(*(argv+1));

unlink("tests.log"); // We want a new log file

do_randomized_test(strategy,10000,0.25,1,1000,10000);

do_randomized_test(strategy,10000,0.25,1,2000,10000);

do_randomized_test(strategy,10000,0.25,1000,2000,10000);

do_randomized_test(strategy,10000,0.25,1,3000,10000);

do_randomized_test(strategy,10000,0.25,1,4000,10000);

do_randomized_test(strategy,10000,0.25,1,5000,10000);

do_randomized_test(strategy,10000,0.5,1,1000,10000);

do_randomized_test(strategy,10000,0.5,1,2000,10000);

do_randomized_test(strategy,10000,0.5,1000,2000,10000);

do_randomized_test(strategy,10000,0.5,1,3000,10000);

do_randomized_test(strategy,10000,0.5,1,4000,10000);

do_randomized_test(strategy,10000,0.5,1,5000,10000);

do_randomized_test(strategy,10000,0.5,1000,1000,10000); /* watch what happens with this test!...why? */

do_randomized_test(strategy,10000,0.75,1,1000,10000);

do_randomized_test(strategy,10000,0.75,500,1000,10000);

do_randomized_test(strategy,10000,0.75,1,2000,10000);

do_randomized_test(strategy,10000,0.9,1,500,10000);

return 0; /* you nominally pass for surviving without segfaulting */

}

/* basic sequential allocation of single byte blocks */

int test_alloc_1(int argc, char **argv) {

strategies strategy;

int lbound = 1;

int ubound = 4;

if (strategyFromString(*(argv+1))>0)

lbound=ubound=strategyFromString(*(argv+1));

for (strategy = lbound; strategy <= ubound; strategy++)

{

int correct_holes = 0;

int correct_alloc = 100;

int correct_largest_free = 0;

int i;

void* lastPointer = NULL;

initmem(strategy,100);

for (i = 0; i < 100; i++)

{

void* pointer = mymalloc(1);

if ( i > 0 && pointer != (lastPointer+1) )

{

printf("Allocation with %s was not sequential at %i; expected %p, actual %p ", strategy_name(strategy), i,lastPointer+1,pointer);

return 1;

}

lastPointer = pointer;

}

if (mem_holes() != correct_holes)

{

printf("Holes not counted as %d with %s ", correct_holes, strategy_name(strategy));

return 1;

}

if (mem_allocated() != correct_alloc)

{

printf("Allocated memory not reported as %d with %s ", correct_alloc, strategy_name(strategy));

return 1;

}

if (mem_largest_free() != correct_largest_free)

{

printf("Largest memory block free not reported as %d with %s ", correct_largest_free, strategy_name(strategy));

return 1;

}

}

return 0;

}

/* alloc, alloc, free, alloc */

int test_alloc_2(int argc, char **argv) {

strategies strategy;

int lbound = 1;

int ubound = 4;

if (strategyFromString(*(argv+1))>0)

lbound=ubound=strategyFromString(*(argv+1));

for (strategy = lbound; strategy <= ubound; strategy++)

{

int correct_holes;

int correct_alloc;

int correct_largest_free;

int correct_small;

void* first;

void* second;

void* third;

int correctThird;

initmem(strategy,100);

first = mymalloc(10);

second = mymalloc(1);

myfree(first);

third = mymalloc(1);

if (second != (first+10))

{

printf("Second allocation failed; allocated at incorrect offset with strategy %s", strategy_name(strategy));

return 1;

}

correct_alloc = 2;

correct_small = (strategy == First || strategy == Best);

switch (strategy)

{

case Best:

correctThird = (third == first);

correct_holes = 2;

correct_largest_free = 89;

break;

case Worst:

correctThird = (third == second+1);

correct_holes = 2;

correct_largest_free = 88;

break;

case First:

correctThird = (third == first);

correct_holes = 2;

correct_largest_free = 89;

break;

case Next:

correctThird = (third == second+1);

correct_holes = 2;

correct_largest_free = 88;

break;

case NotSet:

break;

}

if (!correctThird)

{

printf("Third allocation failed; allocated at incorrect offset with %s", strategy_name(strategy));

return 1;

}

if (mem_holes() != correct_holes)

{

printf("Holes counted as %d, should be %d with %s ", mem_holes(), correct_holes, strategy_name(strategy));

return 1;

}

if (mem_small_free(9) != correct_small)

{

printf("Small holes counted as %d, should be %d with %s ", mem_small_free(9), correct_small, strategy_name(strategy));

return 1;

}

if (mem_allocated() != correct_alloc)

{

printf("Memory reported as %d, should be %d with %s ", mem_allocated(0), correct_alloc, strategy_name(strategy));

return 1;

}

if (mem_largest_free() != correct_largest_free)

{

printf("Largest memory block free reported as %d, should be %d with %s ", mem_largest_free(), correct_largest_free, strategy_name(strategy));

return 1;

}

}

return 0;

}

/* basic sequential allocation followed by 50 frees */

int test_alloc_3(int argc, char **argv) {

strategies strategy;

int lbound = 1;

int ubound = 4;

if (strategyFromString(*(argv+1))>0)

lbound=ubound=strategyFromString(*(argv+1));

for (strategy = lbound; strategy <= ubound; strategy++)

{

int correct_holes = 50;

int correct_alloc = 50;

int correct_largest_free = 1;

int i;

void* lastPointer = NULL;

initmem(strategy,100);

for (i = 0; i < 100; i++)

{

void* pointer = mymalloc(1);

if ( i > 0 && pointer != (lastPointer+1) )

{

printf("Allocation with %s was not sequential at %i; expected %p, actual %p ", strategy_name(strategy), i,lastPointer+1,pointer);

return 1;

}

lastPointer = pointer;

}

for (i = 1; i < 100; i+= 2)

{

myfree(mem_pool() + i);

}

if (mem_holes() != correct_holes)

{

printf("Holes not counted as %d with %s ", correct_holes, strategy_name(strategy));

return 1;

}

if (mem_allocated() != correct_alloc)

{

printf("Memory not reported as %d with %s ", correct_alloc, strategy_name(strategy));

return 1;

}

if (mem_largest_free() != correct_largest_free)

{

printf("Largest memory block free not reported as %d with %s ", correct_largest_free, strategy_name(strategy));

return 1;

}

for(i=0;i<100;i++) {

if(mem_is_alloc(mem_pool()+i) == i%2) {

printf("Byte %d in memory claims to ",i);

if(i%2)

printf("not ");

printf("be allocated. It should ");

if(!i%2)

printf("not ");

printf("be allocated. ");

return 1;

}

}

}

return 0;

}

/* basic sequential allocation followed by 50 frees, then another 50 allocs */

int test_alloc_4(int argc, char **argv) {

strategies strategy;

int lbound = 1;

int ubound = 4;

if (strategyFromString(*(argv+1))>0)

lbound=ubound=strategyFromString(*(argv+1));

for (strategy = lbound; strategy <= ubound; strategy++)

{

int correct_holes = 0;

int correct_alloc = 100;

int correct_largest_free = 0;

int i;

void* lastPointer = NULL;

initmem(strategy,100);

for (i = 0; i < 100; i++)

{

void* pointer = mymalloc(1);

if ( i > 0 && pointer != (lastPointer+1) )

{

printf("Allocation with %s was not sequential at %i; expected %p, actual %p ", strategy_name(strategy), i,lastPointer+1,pointer);

return 1;

}

lastPointer = pointer;

}

for (i = 1; i < 100; i+= 2)

{

myfree(mem_pool() + i);

}

for (i = 1; i < 100; i+=2)

{

void* pointer = mymalloc(1);

if ( i > 1 && pointer != (lastPointer+2) )

{

printf("Second allocation with %s was not sequential at %i; expected %p, actual %p ", strategy_name(strategy), i,lastPointer+1,pointer);

return 1;

}

lastPointer = pointer;

}

if (mem_holes() != correct_holes)

{

printf("Holes not counted as %d with %s ", correct_holes, strategy_name(strategy));

return 1;

}

if (mem_allocated() != correct_alloc)

{

printf("Memory not reported as %d with %s ", correct_alloc, strategy_name(strategy));

return 1;

}

if (mem_largest_free() != correct_largest_free)

{

printf("Largest memory block free not reported as %d with %s ", correct_largest_free, strategy_name(strategy));

return 1;

}

}

return 0;

}

int run_memory_tests(int argc, char **argv)

{

if (argc < 3)

{

printf("Usage: mem -test ");

return 0;

}

set_testrunner_default_timeout(20);

/* Tests can be invoked by matching their name or their suite name or 'all'*/

testentry_t tests[] = {

{"alloc1","suite1",test_alloc_1},

{"alloc2","suite2",test_alloc_2},

{"alloc3","suite1",test_alloc_3},

{"alloc4","suite2",test_alloc_4},

{"stress","suite3",do_stress_tests},

};

return run_testrunner(argc,argv,tests,sizeof(tests)/sizeof(testentry_t));

}

int main(int argc, char **argv)

{

if( argc < 2) {

printf("Usage: mem -test | mem -try ... ");

exit(-1);

}

else if (!strcmp(argv[1],"-test"))

return run_memory_tests(argc-1,argv+1);

else if (!strcmp(argv[1],"-try")) {

try_mymem(argc-1,argv+1);

return 0;

} else {

printf("Usage: mem -test | mem -try ... ");

exit(-1);

}

}

testrunner.c

/*

A simple testrunner framework

Original Author: L. Angrave

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "testrunner.h"

#include "mymem.h"

/* Constants */

#define false (0)

#define true (1)

#define test_killed (2)

/* defaults */

static int default_timeout_seconds=5;

static int timeout_seconds;

void set_testrunner_default_timeout(int s) {

assert(s>0);

default_timeout_seconds=s;

}

void set_testrunner_timeout(int s) {

assert(s>0);

timeout_seconds=s;

}

/* --- Helper macros and functions --- */

#define DIE(mesg) {fprintf(stderr," %s(%d):%s ",__fname__,__LINE__,mesg); exit(1);}

static int eql( char*s1, char*s2) {return s1&&s2&&!strcmp(s1,s2);}

/* Callback function for qsort on strings */

static int mystrcmp( const void *p1, const void *p2) {

return eql( ( char*)p1, ( char*)p2);

}

/* Stats of all tests run so far */

typedef struct

{

int ran, passed, failed;

} stats_t;

/* -- Signal handlers -- */

static pid_t child_pid;

static int sent_child_timeout_kill_signal;

static void kill_child_signal_handler(intsigno) {

if(!child_pid) return;

char m[]="-Timeout(Killing test process)-";

write(0,m,sizeof(m)-1);

kill(child_pid,SIGKILL);

sent_child_timeout_kill_signal=1;

}

/* Internal function to run a test as a forked child. The child process is terminated if it runs for more than a few seconds */

static int invoke_test_with_timelimit(testentry_t* test, int redirect_stdouterr,int argc, char **argv)

{

char fname[255];

int wait_status;

pid_t wait_val;

struct sigaction action;

assert(!child_pid);

assert(test && test->test_function && test->name);

set_testrunner_timeout(default_timeout_seconds);

errno=0;

child_pid = fork ();

if (child_pid == -1) {

fprintf(stderr,"-fork failed so running test inline-");

return test->test_function (argc, argv);

}

if (child_pid == 0)

{

if(redirect_stdouterr) {

snprintf(fname,(int)sizeof(fname),"stdout-%s.txt",test->name);

fname[sizeof(fname)-1]=0;

freopen(fname, "w", stdout);

memcpy(fname+3,"err",3);

freopen(fname, "w", stderr);

}

exit(test->test_function(argc,argv));

}else {

wait_status=-1;

sigemptyset(&action.sa_mask);

action.sa_handler=kill_child_signal_handler;

sigaction(SIGALRM,&action,NULL);

sent_child_timeout_kill_signal=0;

alarm(timeout_seconds);

wait_val = waitpid (child_pid, &wait_status, 0);

int child_exited_normally= WIFEXITED (wait_status);

int child_exit_value=WEXITSTATUS (wait_status);

int child_term_by_signal=WIFSIGNALED(wait_status);

int child_term_signal=WTERMSIG(wait_status);

if(child_term_by_signal) {

fprintf(stderr,"testrunner:Test terminated by signal %d ",child_term_signal);

fprintf(stderr,"testrunner:waitpid returned %d (child_pid=%d,wait_status=%d)",wait_val,child_pid,wait_status);

}

if(child_pid != wait_val)

fprintf(stderr,"testrunner: strange... wait_val != child_pid ");

int passed= (child_pid == wait_val) && (child_exit_value==0) && (child_exited_normally!=0);

alarm(0);

kill(child_pid,SIGKILL);

child_pid=0;

return sent_child_timeout_kill_signal ? test_killed : passed ? 0 : 1;

}

}

/*

* run a test and update the stats. The main guts of this functionality is provided by invoke_test_with_timelimit

* This outer wrapper updates thes output and statistics before and after running the test.

*/

static int

run_one_test (stats_t * stats, testentry_t * test, int redirect_stdouterr,int argc, char **argv)

{

int test_result;

assert (stats && test->name && argc > 0 && argv && *argv);

stats->ran++;

stats->failed++;

printf ("%2d.%-20s:", stats->ran, test->name);

fflush(stdout);

test_result=invoke_test_with_timelimit(test,redirect_stdouterr,argc,argv);

if (test_result == 0)

{

stats->failed--;

stats->passed++;

}

printf(":%s ", (test_result == 0 ? "pass" : test_result ==

2 ? "TIMEOUT * " : "FAIL *"));

return test_result!=0;

}

/* Help functionality to print out sorted list of test names and suite names */

static void print_targets(testentry_t tests[], int count) {

char**array;

char *previous;

int i;

array=(char**)calloc(sizeof(char*),count);

/* Sort the test names and print unique entries*/

for(i=0;i

qsort(array,count,sizeof(array[0]),mystrcmp);

printf(" Valid tests : all");

for(i=0,previous="";i

/* Sort the suite names and print unique entries*/

for(i=0;i

qsort(array, count,sizeof(array[0]),mystrcmp);

printf(" Valid suites:");

for(i=0,previous="";i

printf(" Valid strategies: all ");

for(i=1;i<5;i++)

printf("%s ",strategy_name(i));

printf(" ");

}

/*

* Main entry point for test harness

*/

int

run_testrunner(int argc, char **argv,testentry_t tests[],int test_count)

{

char *test_name, *target;

int i;

stats_t stats;

int target_matched,max_errors_before_quit,redirect_stdouterr;

memset (&stats, 0, sizeof (stats));

max_errors_before_quit=1;

redirect_stdouterr=0;

assert (tests != NULL);

assert(test_count>0);

assert (argc > 0 && argv && *argv);

while(true) {

target = argc > 1 ? argv[1] : "";

assert (target);

if(*target!='-') break;

argc--;argv++;

if(target[1]=='f' && target[2])

max_errors_before_quit=atoi(target+1);

else if(target[1]=='r')

redirect_stdouterr=1;

}

target_matched = false;

for (i=0;i

test_name = tests[i].name;

assert(test_name);

assert(tests[i].suite);

assert(tests[i].test_function);

if (eql(target,test_name)||eql(target,"all") || eql (target,tests[i].suite) ) {

if(!target_matched) printf("Running tests... ");

target_matched = true;

run_one_test (&stats, &tests[i],redirect_stdouterr, argc - 1,argv + 1);

}

}

if (!target_matched)

{

fprintf (stderr, "Test '%s' not found", (strlen(target)>0?target : "(empty)"));

print_targets(tests,test_count);

}

else {

printf (" Test Results:%d tests,%d passed,%d failed. ", stats.ran,

stats.passed, stats.failed);

}

return stats.passed == stats.ran && target_matched ? 0 : 1;

}

testrunner.h

typedef int (*test_fp) (int, char **);

typedef struct

{

char *name;

char *suite;

test_fp test_function;

} testentry_t;

int run_testrunner(int argc, char **argv, testentry_t *entries,int entry_count);

void set_testrunner_default_timeout(int s);

void set_testrunner_timeout(int s);

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!