Threading and file mgmt

profileAlex_marc29
assign4-2-fixed.zip

assign4-2/.DS_Store

assign4-2/assign4_part2/mymem.h

#include <stddef.h> 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);

assign4-2/assign4_part2/memorytests.c

#include <errno.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include <time.h> #include <unistd.h> #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.\n"); return; } fprintf(log,"Running randomized tests: pool size == %d, fill ratio == %f, block size is from %d to %d, %d iterations\n",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.\n"); return; } fprintf(log,"\t=== %s ===\n",strategy_name(strategy)); fprintf(log,"\tTest took %.2fms.\n", (execend.tv_sec - execstart.tv_sec) * 1000 + (execend.tv_nsec - execstart.tv_nsec) / 1000000.0); fprintf(log,"\tAverage hole size: %f\n",sum_hole_size/iterations); fprintf(log,"\tAverage largest free block: %f\n",sum_largest_free/iterations); fprintf(log,"\tAverage allocated bytes: %f\n",sum_allocated/iterations); fprintf(log,"\tAverage number of small blocks: %f\n",sum_small/iterations); fprintf(log,"\tFailed allocations: %d\n",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\n", strategy_name(strategy), i,lastPointer+1,pointer); return 1; } lastPointer = pointer; } if (mem_holes() != correct_holes) { printf("Holes not counted as %d with %s\n", correct_holes, strategy_name(strategy)); return 1; } if (mem_allocated() != correct_alloc) { printf("Allocated memory not reported as %d with %s\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", correct_holes, strategy_name(strategy)); return 1; } if (mem_allocated() != correct_alloc) { printf("Memory not reported as %d with %s\n", 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\n", 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.\n"); 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\n", 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\n", strategy_name(strategy), i,lastPointer+1,pointer); return 1; } lastPointer = pointer; } if (mem_holes() != correct_holes) { printf("Holes not counted as %d with %s\n", correct_holes, strategy_name(strategy)); return 1; } if (mem_allocated() != correct_alloc) { printf("Memory not reported as %d with %s\n", 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\n", 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 <test> <strategy> \n"); 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 <test> <strategy> | mem -try <arg1> <arg2> ... \n"); 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 <test> <strategy> | mem -try <arg1> <arg2> ... \n"); exit(-1); } }

assign4-2/assign4_part2/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);

assign4-2/assign4_part2/README.txt

LMP2: Memory Management ======================= 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: initmem(): Initialize memory structures. mymalloc(): Like malloc(), this allocates a new block of memory. myfree(): Like free(), this deallocates a block of memory. mem_holes(): How many free blocks are in memory? mem_allocated(): How much memory is currently allocated? mem_free(): How much memory is NOT allocated? mem_largest_free(): How large is the largest free block? mem_small_free(): How many small unallocated blocks are currently in memory? 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 <args>", 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 <test> <strategy>" to test your code with our tests. 3) "mem -try <args>" 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 MPs, 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.out" . 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 ------- A) 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. Questions ========= 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.out). 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.

assign4-2/assign4_part2/mymem.c

#include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "mymem.h" #include <time.h> /* 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.\n",mem_allocated(),mem_total()); printf("%d bytes are free in %d holes; maximum allocatable block is %d bytes.\n",mem_free(),mem_holes(),mem_largest_free()); printf("Average hole size is %f.\n\n",((float)mem_free())/mem_holes()); } /* Use this function to see what happens when your malloc and free * implementations are called. Run "mem -try <args>" 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(); }

assign4-2/assign4_part2/testrunner.c

/* A simple testrunner framework Original Author: L. Angrave */ #include <sys/types.h> #include <sys/wait.h> #include <errno.h> #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #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,"\n%s(%d):%s\n",__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\n",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\n"); 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\n", (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<count;i++) array[i]=tests[i].name; qsort(array,count,sizeof(array[0]),mystrcmp); printf("\nValid tests : all"); for(i=0,previous="";i<count; i++) if(!eql(previous,array[i])) printf(" %s",(previous=array[i])); /* Sort the suite names and print unique entries*/ for(i=0;i<count;i++) array[i]=tests[i].suite; qsort(array, count,sizeof(array[0]),mystrcmp); printf("\nValid suites:"); for(i=0,previous="";i<count; i++) if(!eql(previous,array[i])) printf(" %s",(previous=array[i])); printf("\nValid strategies: all "); for(i=1;i<5;i++) printf("%s ",strategy_name(i)); printf("\n"); } /* * 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_count && (max_errors_before_quit<1 || stats.failed != max_errors_before_quit);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...\n"); 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 ("\nTest Results:%d tests,%d passed,%d failed.\n", stats.ran, stats.passed, stats.failed); } return stats.passed == stats.ran && target_matched ? 0 : 1; }

assign4-2/assign4_part2/Makefile

CC = gcc CCOPTS = -c -g -Wall LINKOPTS = -g EXEC=mem OBJECTS=testrunner.o mymem.o memorytests.o all: $(EXEC) $(EXEC): $(OBJECTS) $(CC) $(LINKOPTS) -o $@ $^ -lrt %.o:%.c $(CC) $(CCOPTS) -o $@ $^ clean: - $(RM) $(EXEC) - $(RM) $(OBJECTS) - $(RM) *~ - $(RM) core.* test: mem ./mem -test -f0 all all stage1-test: mem ./mem -test -f0 all first pretty: indent *.c *.h -kr

assign4-2/assign4_part2/tests.log

Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1 to 1000, 10000 iterations === best === Test took 0.18ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.16ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.15ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.15ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1 to 2000, 10000 iterations === best === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.15ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.14ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.18ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1000 to 2000, 10000 iterations === best === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.17ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.17ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1 to 3000, 10000 iterations === best === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.17ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.17ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.17ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1 to 4000, 10000 iterations === best === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.18ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.19ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.250000, block size is from 1 to 5000, 10000 iterations === best === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.21ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1 to 1000, 10000 iterations === best === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.20ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1 to 2000, 10000 iterations === best === Test took 0.13ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1000 to 2000, 10000 iterations === best === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.11ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1 to 3000, 10000 iterations === best === Test took 0.11ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1 to 4000, 10000 iterations === best === Test took 0.11ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.13ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.12ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1 to 5000, 10000 iterations === best === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.500000, block size is from 1000 to 1000, 10000 iterations === best === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.750000, block size is from 1 to 1000, 10000 iterations === best === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.750000, block size is from 500 to 1000, 10000 iterations === best === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.750000, block size is from 1 to 2000, 10000 iterations === best === Test took 0.10ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 Running randomized tests: pool size == 10000, fill ratio == 0.900000, block size is from 1 to 500, 10000 iterations === best === Test took 0.09ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === worst === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === first === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0 === next === Test took 0.08ms. Average hole size: 0.000000 Average largest free block: 0.000000 Average allocated bytes: 0.000000 Average number of small blocks: 0.000000 Failed allocations: 0

assign4-2/assign4_part1/setup.sh

#!/bin/sh #*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE *************** rm -rf ./testdata mkdir -p ./testdata/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20 echo -n "Hello" > ./testdata/hello.txt echo -n "OldFile" > ./testdata/oldfile.txt cp ./testdata/hello.txt ./testdata/1/2/3/4/5/6/7/8/9/10/ cp ./testdata/oldfile.txt ./testdata/1/2/3/4/5/6/7/8/9/ touch -m -d 20050101 ./testdata/oldfile.txt touch -a -d 20050103 ./testdata/oldfile.txt if [ ! -f /tmp/bigtestscratchfile2forLMP1 ] ; then echo "Creating large file (first time only)" cat /usr/lib/*.so >/tmp/bigtestscratchfile2forLMP1 chmod 644 /tmp/bigtestscratchfile2forLMP1 fi ln -s /tmp/bigtestscratchfile2forLMP1 ./testdata/bigfile exit 0

assign4-2/assign4_part1/restart.c

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ #include <errno.h> #include <fcntl.h> #include <limits.h> #include <string.h> #include <sys/select.h> #include <sys/time.h> #include <sys/wait.h> #include "restart.h" #define BLKSIZE PIPE_BUF #define MILLION 1000000L #define D_MILLION 1000000.0 /* Private functions */ static int gettimeout(struct timeval end, struct timeval *timeoutp) { gettimeofday(timeoutp, NULL); timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec; timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec; if (timeoutp->tv_usec >= MILLION) { timeoutp->tv_sec++; timeoutp->tv_usec -= MILLION; } if (timeoutp->tv_usec < 0) { timeoutp->tv_sec--; timeoutp->tv_usec += MILLION; } if ((timeoutp->tv_sec < 0) || ((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) { errno = ETIME; return -1; } return 0; } /* Restart versions of traditional functions */ int r_close(int fildes) { int retval; while (retval = close(fildes), retval == -1 && errno == EINTR); return retval; } int r_dup2(int fildes, int fildes2) { int retval; while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR); return retval; } int r_open2(const char *path, int oflag) { int retval; while (retval = open(path, oflag), retval == -1 && errno == EINTR); return retval; } int r_open3(const char *path, int oflag, mode_t mode) { int retval; while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR); return retval; } ssize_t r_read(int fd, void *buf, size_t size) { ssize_t retval; while (retval = read(fd, buf, size), retval == -1 && errno == EINTR); return retval; } pid_t r_wait(int *stat_loc) { pid_t retval; while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)); return retval; } pid_t r_waitpid(pid_t pid, int *stat_loc, int options) { pid_t retval; while (((retval = waitpid(pid, stat_loc, options)) == -1) && (errno == EINTR)); return retval; } ssize_t r_write(int fd, void *buf, size_t size) { char *bufp; size_t bytestowrite; ssize_t byteswritten; size_t totalbytes; for (bufp = buf, bytestowrite = size, totalbytes = 0; bytestowrite > 0; bufp += byteswritten, bytestowrite -= byteswritten) { byteswritten = write(fd, bufp, bytestowrite); if ((byteswritten) == -1 && (errno != EINTR)) return -1; if (byteswritten == -1) byteswritten = 0; totalbytes += byteswritten; } return totalbytes; } /* Utility functions */ struct timeval add2currenttime(double seconds) { struct timeval newtime; gettimeofday(&newtime, NULL); newtime.tv_sec += (int) seconds; newtime.tv_usec += (int) ((seconds - (int) seconds) * D_MILLION + 0.5); if (newtime.tv_usec >= MILLION) { newtime.tv_sec++; newtime.tv_usec -= MILLION; } return newtime; } int copyfile(int fromfd, int tofd) { int bytesread; int totalbytes = 0; while ((bytesread = readwrite(fromfd, tofd)) > 0) totalbytes += bytesread; return totalbytes; } ssize_t readblock(int fd, void *buf, size_t size) { char *bufp; ssize_t bytesread; size_t bytestoread; size_t totalbytes; for (bufp = buf, bytestoread = size, totalbytes = 0; bytestoread > 0; bufp += bytesread, bytestoread -= bytesread) { bytesread = read(fd, bufp, bytestoread); if ((bytesread == 0) && (totalbytes == 0)) return 0; if (bytesread == 0) { errno = EINVAL; return -1; } if ((bytesread) == -1 && (errno != EINTR)) return -1; if (bytesread == -1) bytesread = 0; totalbytes += bytesread; } return totalbytes; } int readline(int fd, char *buf, int nbytes) { int numread = 0; int returnval; while (numread < nbytes - 1) { returnval = read(fd, buf + numread, 1); if ((returnval == -1) && (errno == EINTR)) continue; if ((returnval == 0) && (numread == 0)) return 0; if (returnval == 0) break; if (returnval == -1) return -1; numread++; if (buf[numread - 1] == '\n') { buf[numread] = '\0'; return numread; } } errno = EINVAL; return -1; } ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) { struct timeval timedone; timedone = add2currenttime(seconds); if (waitfdtimed(fd, timedone) == -1) return (ssize_t) (-1); return r_read(fd, buf, nbyte); } int readwrite(int fromfd, int tofd) { char buf[BLKSIZE]; int bytesread; if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0) return -1; if (bytesread == 0) return 0; if (r_write(tofd, buf, bytesread) < 0) return -1; return bytesread; } int readwriteblock(int fromfd, int tofd, char *buf, int size) { int bytesread; bytesread = readblock(fromfd, buf, size); if (bytesread != size) /* can only be 0 or -1 */ return bytesread; return r_write(tofd, buf, size); } int waitfdtimed(int fd, struct timeval end) { fd_set readset; int retval; struct timeval timeout; if ((fd < 0) || (fd >= FD_SETSIZE)) { errno = EINVAL; return -1; } FD_ZERO(&readset); FD_SET(fd, &readset); if (gettimeout(end, &timeout) == -1) return -1; while (((retval = select(fd + 1, &readset, NULL, NULL, &timeout)) == -1) && (errno == EINTR)) { if (gettimeout(end, &timeout) == -1) return -1; FD_ZERO(&readset); FD_SET(fd, &readset); } if (retval == 0) { errno = ETIME; return -1; } if (retval == -1) return -1; return 0; }

assign4-2/assign4_part1/lmp1_tests.c

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ #include <unistd.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <sys/stat.h> #include <string.h> #include "testrunner.h" #include "fileio.h" #include "util.h" /* CONSTANTS */ /* These constants work in tandem with test data created by setup.sh */ #define TESTDATA_DIR "testdata/" #define NEW_FILE "testdata/newfile.txt" #define OLD_FILE "testdata/oldfile.txt" #define HELLO_FILE "testdata/hello.txt" #define BIG_FILE "testdata/bigfile" #define NO_SUCH_FILE "testdata/no_such_file" #define NEW_DIR "testdata/newdir" #define SUBDIR1_DIR "testdata/1" extern int bonnie_main(int, const char **); /* Remove testdata subdirectory (assumes that the current directory has not changed. */ void teardown() { if (system("./teardown.sh")) fprintf(stderr, "\nteardown failed\n"); } /* Create test data and register the clean up function */ void setup() { quit_if(system("./setup.sh")); quit_if(atexit(teardown)); errno = 0; } /* ---------- Suite 1 Tests --------------- */ /* test file_read() */ int test_file_read(int argc, const char **argv) { char b[255], dots[sizeof(b)]; int bytes_read, i; setup(); quit_if(IOERR_INVALID_ARGS != file_read(HELLO_FILE, 0, b, 0)); quit_if(IOERR_INVALID_ARGS != file_read(HELLO_FILE, 0, NULL, sizeof(b))); quit_if(IOERR_INVALID_ARGS != file_read(HELLO_FILE, -1, b, sizeof(b))); quit_if(IOERR_INVALID_ARGS != file_read(NULL, 0, b, sizeof(b))); quit_if(IOERR_INVALID_PATH != file_read(NO_SUCH_FILE, 0, b, sizeof(b))); bytes_read = file_read(HELLO_FILE, 0, b, sizeof(b)); quit_if(bytes_read != 5); quit_if(strncmp("Hello", b, 5)); bytes_read = file_read(HELLO_FILE, 1, b, sizeof(b)); quit_if(bytes_read != 4); quit_if(strncmp("ello", b, 4)); bytes_read = file_read(HELLO_FILE, -1, b, sizeof(b)); quit_if(bytes_read >= 0); for (i = 0; i < sizeof(dots); i++) { b[i] = dots[i] = i == sizeof(dots) - 1 ? '\0' : '.'; } bytes_read = file_read(BIG_FILE, 1, b, sizeof(b)); quit_if(bytes_read != sizeof(b)); quit_if(!memcmp(b, dots, sizeof(b))); return 0; } /* Test file_info() */ int test_file_info(int argc, const char **argv) { char b[255], ftype[2]; int sz, params; long accessed, modified; char fmt[] = "Size:%d Accessed:%d Modified:%d Type %s"; setup(); // Test invalid arguments quit_if(IOERR_INVALID_ARGS != file_info(HELLO_FILE, b, 0)); quit_if(IOERR_INVALID_ARGS != file_info(HELLO_FILE, NULL, sizeof(b))); quit_if(IOERR_INVALID_ARGS != file_info(NULL, b, sizeof(b))); // Test stat on old file quit_if(file_info(OLD_FILE, b, sizeof(b))); params = sscanf(b, fmt, &sz, &accessed, &modified, &ftype); quit_if(params != 4); int one_day = 86400; int jan1_2005 = 1104534000; // seconds since epoch int jan3_2005 = jan1_2005 + one_day + one_day; quit_if(modified < jan1_2005 || modified > jan1_2005 + one_day); quit_if(accessed < jan3_2005 || accessed > jan3_2005 + one_day); return 0; } /* Test file_write() */ int test_file_write(int argc, const char **argv) { char b[] = "qwertyuiop", b2[255]; setup(); // Test invalid arguments quit_if(IOERR_INVALID_ARGS != file_write(NEW_FILE, 0, NULL, sizeof(b))); quit_if(IOERR_INVALID_ARGS != file_write(NULL, 0, b, sizeof(b))); // Test new file quit_if(sizeof(b) != file_write(NEW_FILE, 0, b, sizeof(b))); quit_if(strlen(b) + 1 != file_read(NEW_FILE, 0, b2, sizeof(b2))); quit_if(strncmp(b, b2, strlen(b))); return 0; } /* ---------- Suite 3 Tests --------------- */ /* Test the higher level function file_create. */ int test_file_create(int argc, const char **argv) { char pattern[] = "ab", b[255]; int repeat = 10; char expected[] = "abababababababababab"; setup(); quit_if(file_create(NEW_FILE, pattern, repeat)); quit_if(strlen(expected) != file_read(NEW_FILE, 0, b, sizeof(b))); quit_if(strncmp(expected, b, strlen(expected))); return 0; } /* Test file_removal. */ int test_file_remove(int argc, const char **argv) { setup(); quit_if(IOERR_INVALID_ARGS != file_remove(NULL)); // Test correct removal quit_if(file_remove(OLD_FILE)); // Cant remove twice quit_if(IOERR_INVALID_PATH != file_remove(OLD_FILE)); return 0; } /* ---------- Suite 2 Tests --------------- */ /* Test dir_create (directory create)*/ int test_dir_create(int argc, const char **argv) { setup(); quit_if(IOERR_INVALID_ARGS != dir_create(NULL)); quit_if(IOERR_INVALID_PATH != dir_create(HELLO_FILE)); quit_if(dir_create(NEW_DIR)); struct stat s; quit_if(stat(NEW_DIR, &s) == -1); quit_if(!S_ISDIR(s.st_mode)) return 0; } /* Test dir_list (directory listing) */ int test_dir_list(int argc, const char **argv) { char b[10000], *next_line, *last, names_found[10000]; int entries; setup(); quit_if(IOERR_INVALID_ARGS != dir_list(NULL, b, sizeof(b))); quit_if(IOERR_INVALID_ARGS != dir_list(HELLO_FILE, NULL, sizeof(b))); quit_if(IOERR_INVALID_ARGS != dir_list(HELLO_FILE, b, 0)); quit_if(IOERR_INVALID_PATH != dir_list(NO_SUCH_FILE, b, sizeof(b))); quit_if(IOERR_INVALID_PATH != dir_list(HELLO_FILE, b, sizeof(b))); quit_if(IOERR_BUFFER_TOO_SMALL != dir_list(TESTDATA_DIR, b, 1)); quit_if(dir_list(TESTDATA_DIR, b, sizeof(b))); quit_if(!memchr(b, 0, sizeof(b))); // check terminating nul byte entries = 0; names_found[0] = '|'; names_found[1] = '\0'; next_line = strtok_r(b, "\n", &last); while (next_line) { int args_read; char name[256]; entries++; args_read = sscanf(next_line, "%255s", name); quit_if(args_read != 1); quit_if(strlen(names_found) + strlen(name) + 2 >= sizeof(names_found)); strcat(names_found, name); strcat(names_found, "|"); next_line = strtok_r(NULL, "\n", &last); } quit_if(!strstr(names_found, "|1|")); quit_if(!strstr(names_found, "|.|")); quit_if(!strstr(names_found, "|..|")); quit_if(!strstr(names_found, "|hello.txt|")); quit_if(!strstr(names_found, "|oldfile.txt|")); quit_if(!strstr(names_found, "|bigfile|")); quit_if(entries != 6); return 0; } /* ---------- Suite 4 Tests --------------- */ /* Test file_checksum */ int test_file_checksum(int argc, const char **argv) { setup(); char hello[] = "Hello"; unsigned short cksum = checksum(hello, strlen(hello), 0); quit_if(IOERR_INVALID_ARGS != file_checksum(NULL)); quit_if(cksum != file_checksum(HELLO_FILE)); return 0; } /* in setup.sh() mkdir -p ./testdata/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20 cp ./testdata/hello.txt ./testdata/1/2/3/4/5/6/7/8/9/10/ cp ./testdata/oldfile.txt ./testdata/1/2/3/4/5/6/7/8/9/ This checksum algorithm commutes so we can calculate the the final checksum as follows... */ /* Test dir_checksum */ int test_dir_checksum(int argc, const char **argv) { setup(); char dname[10]; int i; unsigned short cksum; int cksum2; quit_if(IOERR_INVALID_ARGS != dir_checksum(NULL)); cksum = checksum("OldFile", 7, checksum("Hello", 5, 0)); for (i = 2; i <= 20; i++) { sprintf(dname, "%d", i); cksum = checksum(dname, strlen(dname), cksum); } cksum2 = dir_checksum(SUBDIR1_DIR); quit_if(0 > cksum2); quit_if(cksum != cksum2); return 0; } /* * Main entry point for test harness */ int run_lmp1_tests(int argc, const char **argv) { /* Tests can be invoked by matching their name or their suite name or 'all' */ testentry_t tests[] = { {"read", "suite1", test_file_read}, {"info", "suite1", test_file_info}, {"write", "suite1", test_file_write}, {"dirlist", "suite2", test_dir_list}, {"dircreate", "suite2", test_dir_create}, {"remove", "suite3", test_file_remove}, {"create", "suite3", test_file_create}, {"filechecksum", "suite4", test_file_checksum}, {"dirchecksum", "suite4", test_dir_checksum} }; return run_testrunner(argc, argv, tests, sizeof(tests) / sizeof(testentry_t)); } /* The real main function. */ int main(int argc, const char **argv) { if (argc > 1 && !strcmp(argv[1], "-test")) { return run_lmp1_tests(argc - 1, argv + 1); } else { return bonnie_main(argc, argv); } }

assign4-2/assign4_part1/fileio.c

#include <sys/types.h> #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include "restart.h" #include "fileio.h" #if 1 #define VERBOSE(p) (p) #else #define VERBOSE(p) (0) #endif int file_read(char *path, int offset, void *buffer, size_t bufbytes) { return IOERR_NOT_YET_IMPLEMENTED; } int file_info(char *path, void *buffer, size_t bufbytes) { if (!path || !buffer || bufbytes < 1) return IOERR_INVALID_ARGS; return IOERR_NOT_YET_IMPLEMENTED; } int file_write(char *path, int offset, void *buffer, size_t bufbytes) { return IOERR_NOT_YET_IMPLEMENTED; } int file_create(char *path, char *pattern, int repeatcount) { return IOERR_NOT_YET_IMPLEMENTED; } int file_remove(char *path) { return IOERR_NOT_YET_IMPLEMENTED; } int dir_create(char *path) { return IOERR_NOT_YET_IMPLEMENTED; } int dir_list(char *path, void *buffer, size_t bufbytes) { return IOERR_NOT_YET_IMPLEMENTED; } int file_checksum(char *path) { return IOERR_NOT_YET_IMPLEMENTED; } int dir_checksum(char *path) { return IOERR_NOT_YET_IMPLEMENTED; }

assign4-2/assign4_part1/util.c

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ /* execl*/ #include <unistd.h> /* errno */ #include <errno.h> /* waitpid,WEXITSTATUS*/ #include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> #include <wait.h> /* fprintf*/ #include <stdio.h> #include <string.h> #include "util.h" /* Print a detailed error message to the given file descriptor. The pointers f,mesg,src can be null. */ void print_error(FILE * f, int errn, char *mesg, char *src, int line) { char errmesg[255]; *errmesg = '\0'; if (strerror_r(errn, errmesg, sizeof(errmesg))) strcpy(errmesg, "Unknown"); fprintf(f ? f : stderr, "\nFAILED AT %s(%d); %s%s. errno=%d(%s)\n", src ? src : "notset", line, mesg ? "because " : "", mesg ? mesg : "", errn, errmesg); } /* Print a detailed error message and then exit with a status value of 1.*/ void quit_with_message(int errn, char *mesg, char *src, int line) { print_error(stderr, errn, mesg, src, line); exit(1); } /* Calculate a very simple checksum for a character buffer.*/ unsigned short checksum(char *buffer, size_t sz, unsigned short val) { if ((!buffer) || (!sz)) return val; while ((sz--) > 0) { val += *(buffer++); } return val; }

assign4-2/assign4_part1/Makefile

CC = gcc CCOPTS = -Wall -c -g LINKOPTS = -g -pthread -lrt EXEC = lmp1 OBJECTS = restart.o testrunner.o fileio.o lmp1_tests.o bonnie.o util.o all: $(EXEC) $(EXEC): $(OBJECTS) $(CC) $(LINKOPTS) -o $@ $^ %.o: %.c $(CC) $(CCOPTS) -o $@ $^ test: ./$(EXEC) -test -f0 all clean: - $(RM) $(EXEC) - $(RM) $(OBJECTS) - $(RM) *~ - $(RM) core.* - $(RM) -rf testdata pretty: indent *.c *.h -kr

assign4-2/assign4_part1/bonnie.c

/* * This is a file system benchmark which attempts to study bottlenecks - * it is named 'Bonnie' after Bonnie Raitt, who knows how to use one. * * Commentary on Bonnie's operations may be found at * http://www.textuality.com/bonnie/intro.html * * COPYRIGHT NOTICE: * Copyright (c) Tim Bray, 1990-1996. * */ #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> #if defined(SysV) #include <limits.h> #include <sys/times.h> #else #include <sys/resource.h> #endif #define IntSize (sizeof(int)) /* * N.B. in seeker_reports, CPU appears and Start/End time, but not Elapsed, * so position 1 is re-used; icky data coupling. */ #define CPU (0) #define Elapsed (1) #define StartTime (1) #define EndTime (2) #define Seeks (4000) #define UpdateSeek (10) #define SeekProcCount (3) #define Chunk (16384) /* labels for the tests, used as an array index */ typedef enum { Putc, ReWrite, FastWrite, Getc, FastRead, Lseek, TestCount } tests_t; static double cpu_so_far(); static void doseek(off_t where, int fd, int update); static void get_delta_t(tests_t test); static void io_error(char *message); static void newfile(char *name, int *fd, FILE * *stream, int create); static void fill_file_char(); static void fill_file_block(); static void file_read_rewrite_block(); static void file_read_getc(); static void file_read_chunk(); #if defined(SysV) /* System V wrappers for randomizers */ static long random(); static void srandom(int seed); #endif static void report(char *machine, off_t size); static double time_so_far(); static void timestamp(); static void usage(); /* * Housekeeping variables to build up timestamps for the tests; * global to make it easy to keep track of the progress of time. * all of this could have been done with non-global variables, * but the code is easier to read this way and I don't anticipate * much software engineering down the road */ static int basetime; /* when we started */ static double delta[(int) TestCount][2]; /* array of DeltaT values */ static double last_cpustamp = 0.0; /* for computing delta-t */ static double last_timestamp = 0.0; /* for computing delta-t */ char name[Chunk]; FILE *stream; int fd; off_t words; off_t size; int buf[Chunk / IntSize]; int bufindex; int chars[256]; int next; /* entry point for LMP1 */ int bonnie_main(int argc, char **argv) { int child; char *dir; double first_start; double last_stop; int lseek_count = 0; char *machine; int seek_control[2]; int seek_feedback[2]; char seek_tickets[Seeks + SeekProcCount]; double seeker_report[3]; fd = -1; basetime = (int) time((time_t *) NULL); size = 100; dir = "."; machine = ""; /* parse the argument sent from the command line */ for (next = 1; next < argc; next++) { if (strcmp(argv[next], "-s") == 0) size = atol(argv[++next]); else if (strcmp(argv[next], "-m") == 0) machine = argv[++next]; else usage(); } if (size < 1) { usage(); } /* CHECK SYSTEM CAPABILITY: We proceed to check the size of files because 32-bit machines has a limit */ if (sizeof(off_t) <= 4 && size > 2047) { fprintf(stderr, "File too large for 32-bit machine, sorry\n"); exit(1); } sprintf(name, "%s/LMP1.%d", dir, getpid()); /* The size is in Megabytes, and is rounded down to multiples of Chunk */ size *= (1024 * 1024); size = Chunk * (size / Chunk); fprintf(stderr, "File '%s', size: %ld\n", name, size); fill_file_char(name, &fd, &stream); /* For specific details please go down to the body of this function */ file_read_rewrite_block(name, &fd, &stream); /* For specific details please go down to the body of this function */ fill_file_block(name, &fd, &stream); /* For specific details please go down to the body of this function */ file_read_getc(name, &fd, &stream); /* For specific details please go down to the body of this function */ /* use the frequency count */ for (words = 0; words < 256; words++) sprintf((char *) buf, "%d", chars[words]); file_read_chunk(name, &fd, &stream); /* For specific details please go down to the body of this function */ /* use the frequency count */ for (words = 0; words < 256; words++) sprintf((char *) buf, "%d", chars[words]); /* * Now test random seeks; first, set up for communicating with children. * The object of this e game is to do "Seeks" lseek() calls as quickly * as possible. So we'll farm them out among SeekProcCount processes. * We'll control them by writing 1-byte tickets down a pipe which * the children all read. We write "Seeks" bytes with val 1, whichever * child happens to get them does it and the right number of seeks get * done. * The idea is that since the write() of the tickets is probably * atomic, the parent process likely won't get scheduled while the * children are seeking away. If you draw a picture of the likely * timelines for three children, it seems likely that the seeks will * overlap very nicely with the process scheduling with the effect * that there will *always* be a seek() outstanding on the file. * Question: should the file be opened *before* the fork, so that * all the children are lseeking on the same underlying file object? */ if (pipe(seek_feedback) == -1 || pipe(seek_control) == -1) io_error("pipe"); for (next = 0; next < Seeks; next++) seek_tickets[next] = 1; for (; next < (Seeks + SeekProcCount); next++) seek_tickets[next] = 0; /* launch some parallel seek processes */ for (next = 0; next < SeekProcCount; next++) { /* for each seek proc */ if ((child = fork()) == -1) io_error("fork"); else if (child == 0) { /* child process */ /* set up and wait for the go-ahead */ close(seek_feedback[0]); close(seek_control[1]); newfile(name, &fd, &stream, 0); srandom(getpid()); fprintf(stderr, "Seeker %d...", next + 1); /* wait for the go-ahead */ if (read(seek_control[0], seek_tickets, 1) != 1) io_error("read ticket"); timestamp(); seeker_report[StartTime] = time_so_far(); /* loop until we read a 0 ticket back from our parent */ while (seek_tickets[0]) { /* until Mom says stop */ doseek((long) (random() % (size / Chunk)), fd, ((lseek_count++ % UpdateSeek) == 0)); if (read(seek_control[0], seek_tickets, 1) != 1) io_error("read ticket"); } /* until Mom says stop */ if (close(fd) == -1) io_error("close after seek"); /* report to parent */ get_delta_t(Lseek); seeker_report[EndTime] = time_so_far(); seeker_report[CPU] = delta[(int) Lseek][CPU]; if (write (seek_feedback[1], seeker_report, sizeof(seeker_report)) != sizeof(seeker_report)) io_error("pipe write"); exit(0); } /* child process */ } /* for each seek proc */ /* * Back in the parent; in an effort to ensure the children get an even * start, wait a few seconds for them to get scheduled, open their * files & so on. */ close(seek_feedback[1]); close(seek_control[0]); sleep(5); fprintf(stderr, "start 'em..."); if (write(seek_control[1], seek_tickets, sizeof(seek_tickets)) != sizeof(seek_tickets)) io_error("write tickets"); /* read back from children */ for (next = 0; next < SeekProcCount; next++) { /* for each child */ if (read (seek_feedback[0], (char *) seeker_report, sizeof(seeker_report)) != sizeof(seeker_report)) io_error("pipe read"); /* * each child writes back its CPU, start & end times. The elapsed time * to do all the seeks is the time the first child started until the * time the last child stopped */ delta[(int) Lseek][CPU] += seeker_report[CPU]; if (next == 0) { /* first time */ first_start = seeker_report[StartTime]; last_stop = seeker_report[EndTime]; } /* first time */ else { /* not first time */ first_start = (first_start < seeker_report[StartTime]) ? first_start : seeker_report[StartTime]; last_stop = (last_stop > seeker_report[EndTime]) ? last_stop : seeker_report[EndTime]; } /* not first time */ if (wait(&child) == -1) io_error("wait"); fprintf(stderr, "done..."); } /* for each child */ fprintf(stderr, "\n"); delta[(int) Lseek][Elapsed] = last_stop - first_start; report(machine, size); unlink(name); return EXIT_SUCCESS; } /*********************************************************************/ /* FUNCTION report */ /*********************************************************************/ /* INPUT: Machine description and size of file */ /*********************************************************************/ /* PROCESS: */ /* 1. Print in the monitor the results from the */ /* operations the program supports. */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void report(char *machine, off_t size) { printf(" "); printf ("-------Sequential Output-------- ---Sequential Input-- --Random--\n"); printf(" "); printf ("-Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks---\n"); printf("Machine MB "); printf("K/sec %%CPU K/sec %%CPU K/sec %%CPU K/sec %%CPU K/sec "); printf("%%CPU /sec %%CPU\n"); printf("%-8.8s %4ld ", machine, size / (1024 * 1024)); printf("%5d %4.1f %5d %4.1f %5d %4.1f ", (int) (((double) size) / (delta[(int) Putc][Elapsed] * 1024.0)), delta[(int) Putc][CPU] / delta[(int) Putc][Elapsed] * 100.0, (int) (((double) size) / (delta[(int) FastWrite][Elapsed] * 1024.0)), delta[(int) FastWrite][CPU] / delta[(int) FastWrite][Elapsed] * 100.0, (int) (((double) size) / (delta[(int) ReWrite][Elapsed] * 1024.0)), delta[(int) ReWrite][CPU] / delta[(int) ReWrite][Elapsed] * 100.0); printf("%5d %4.1f %5d %4.1f ", (int) (((double) size) / (delta[(int) Getc][Elapsed] * 1024.0)), delta[(int) Getc][CPU] / delta[(int) Getc][Elapsed] * 100.0, (int) (((double) size) / (delta[(int) FastRead][Elapsed] * 1024.0)), delta[(int) FastRead][CPU] / delta[(int) FastRead][Elapsed] * 100.0); printf("%5.1f %4.1f\n", ((double) Seeks) / delta[(int) Lseek][Elapsed], delta[(int) Lseek][CPU] / delta[(int) Lseek][Elapsed] * 100.0); } /*********************************************************************/ /* FUNCTION newfile */ /*********************************************************************/ /* INPUT: Name of the file, File Descriptor, Buffer, Create Flag */ /*********************************************************************/ /* PROCESS: */ /* 1. If Create Flag is one */ /* i) Checks if the file is unlinked */ /* a) If it is not returns error */ /* ii) Obatain File Descriptor with proper parameters */ /* and flags.NOTE: Use man pages to view the specific */ /* parameters open uses. Make sure you understand */ /* the return values, and see the errors the function */ /* returns) */ /* Else Create flag is not equal to zero */ /* Obtain file descriptor with proper parameters and */ /* flags.(NOTE: Use man pages to view the specific */ /* parameters open uses. Make sure you understand */ /* the return values, and see the errors the function */ /* returns) */ /* 2. Fill buffer when calling fdopen with proper */ /* parameters and flags.(NOTE: Use man pages to view the */ /* specific parameters open uses. Make sure you understand */ /* the return values, and see the errors the function */ /* returns) */ /* 3. Check stream */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void newfile(char *name, int *fd, FILE ** stream, int create) { if (create) { if (unlink(name) == -1 && *fd != -1) io_error("unlink"); *fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0777); } else *fd = open(name, O_RDWR, 0777); if (*fd == -1) io_error(name); *stream = fdopen(*fd, "r+"); if (*stream == NULL) io_error("fdopen"); } /*********************************************************************/ /* FUNCTION fill_file_char */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Open file */ /* 2. Record timestamp */ /* 3. For cycle to fill the file one character at a time*/ /* until it reaches the EndOfFile(EOF) */ /* 4. Close file */ /* 5. Call get_delta_t to find the performance of putc */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void fill_file_char() { fprintf(stderr, "FUNCTION fill_file_char() start..."); newfile(name, &fd, &stream, 1); timestamp(); for (words = 0; words < size; words++) if (putc(words & 0x7f, stream) == EOF) io_error("putc"); if (fclose(stream) == -1) io_error("fclose after putc"); get_delta_t(Putc); fprintf(stderr, "...done FUNCTION file_fill_char()\n"); } /*********************************************************************/ /* FUNCTION fill_file_block */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Open file */ /* 2. Initialize every position of the buffer */ /* 3. Record timestamp */ /* 4. FOR cycle so for each word you need to: */ /* i) properly set the buffer index so that it points */ /* at the correct position. */ /* ii) Call write.(NOTE: Use man pages to view the */ /* specific parameters write uses. Make sure you */ /* understand the return values, and see the errors */ /* the function returns) */ /* 4. Close file */ /* 5. Call get_delta_t to find the performance of putc */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void fill_file_block() { fprintf(stderr, "FUNCTION fill_file_block() start..."); newfile(name, &fd, &stream, 1); for (words = 0; words < Chunk / IntSize; words++) buf[words] = 0; timestamp(); for (words = bufindex = 0; words < (size / Chunk); words++) { if (bufindex == (Chunk / IntSize)) bufindex = 0; buf[bufindex++]++; if (write(fd, (char *) buf, Chunk) == -1) io_error("write(2)"); } if (close(fd) == -1) io_error("close after fast write"); get_delta_t(FastWrite); fprintf(stderr, "...done FUNCTION fill_file_block()\n"); } /*********************************************************************/ /* FUNCTION file_read_rewrite_block */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Open file */ /* 2. Call lseek. (NOTE: Use man pages to view the */ /* specific parameters lseek uses. Make sure you */ /* understand the return values, and see the errors the */ /* function returns) */ /* 3. Record timestamp */ /* 4. Make sure the buffer index point to zero */ /* 5. Call read. (NOTE: Use man pages to view the */ /* specific parameters read uses. Make sure you */ /* understand the return values, and see the errors the */ /* function returns) */ /* 6. While we can read a block, increment the index to */ /* point to the the next position. Call lseek and call */ /* write.(NOTE: Use man pages to view the specific */ /* parameters write uses. Make sure you understand the */ /* return values,and see the errors the function */ /* returns) */ /* Call read until the while condition is false. */ /* 7. Close file */ /* 8. Call get_delta_t to find the performance of putc */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void file_read_rewrite_block() { fprintf(stderr, "FUNCTION file_read_rewrite() start..."); newfile(name, &fd, &stream, 0); if (lseek(fd, (off_t) 0, 0) == (off_t) - 1) io_error("lseek(2) before rewrite"); fprintf(stderr, "Rewriting"); timestamp(); bufindex = 0; if ((words = read(fd, (char *) buf, Chunk)) == -1) io_error("rewrite read"); while (words == Chunk) { if (bufindex == Chunk / IntSize) bufindex = 0; buf[bufindex++]++; if (lseek(fd, (off_t) - words, 1) == -1) io_error("relative lseek(2)"); if (write(fd, (char *) buf, words) == -1) io_error("re write(2)"); if ((words = read(fd, (char *) buf, Chunk)) == -1) io_error("rwrite read"); } if (close(fd) == -1) io_error("close after rewrite"); get_delta_t(ReWrite); fprintf(stderr, "...done FUNCTION file_read_rewrite_block()\n"); } /*********************************************************************/ /* FUNCTION file_read_getc */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Open file */ /* 2. Initialize every position of the buffer words */ /* 3. Record timestamp */ /* 4. FOR cycle to fill the file 1 byte at a time until */ /* it reaches the EndOfFile(EOF).Make sure you notice */ /* the increment of the index pointing at chars */ /* 7. Close file */ /* 8. Call get_delta_t to find the performance of getc */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void file_read_getc() { fprintf(stderr, "FUNCTION fill_read_getc() start..."); newfile(name, &fd, &stream, 0); for (words = 0; words < 256; words++) chars[words] = 0; timestamp(); for (words = 0; words < size; words++) { if ((next = getc(stream)) == EOF) io_error("getc(3)"); chars[next]++; } if (fclose(stream) == -1) io_error("fclose after getc"); get_delta_t(Getc); fprintf(stderr, "...done FUNCTION file_read_getc()\n"); } /*********************************************************************/ /* FUNCTION file_read_chunk */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Open file */ /* 2. Call lseek().(NOTE:Use man pages to view the */ /* specific */ /* parameters write uses. Make sure you understand the */ /* return values,and see the errors the function */ /* returns) */ /* 3. Record timestamp */ /* 6. DO cycle read a block.(NOTE: Use man pages to view*/ /* the specific parameters read uses. Make sure you */ /* understand the return values, and see the errors the */ /* function returns) Call read until the while condition is */ /* false. */ /* 7. Close file */ /* 8. Call get_delta_t to find the performance of getc */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void file_read_chunk() { fprintf(stderr, "FUNCTION file_read_chunk() start..."); newfile(name, &fd, &stream, 0); if (lseek(fd, (off_t) 0, 0) == -1) io_error("lseek before read"); timestamp(); do { if ((words = read(fd, (char *) buf, Chunk)) == -1) io_error("read(2)"); chars[buf[abs(buf[0]) % (Chunk / IntSize)] & 0x7f]++; } while (words); if (close(fd) == -1) io_error("close after read"); get_delta_t(FastRead); fprintf(stderr, "...done FUNCTION file_read_chunk()\n"); } /*********************************************************************/ /* FUNCTION usage */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Presents all the options the program has */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void usage() { fprintf(stderr, "usage: ./lmp1 [-s size-in-Mb] [-m machine-label]\n"); exit(1); } /*********************************************************************/ /* FUNCTION timestamp */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Call time_so_far() and store it in last_timestamp */ /* 2. Call cpu_so_far() and store it in last_cpustamp */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void timestamp() { last_timestamp = time_so_far(); last_cpustamp = cpu_so_far(); } /*********************************************************************/ /* FUNCTION get_delta_t */ /*********************************************************************/ /* INPUT: Receives the structure tests_t */ /*********************************************************************/ /* PROCESS: */ /* 1. Identifies the procedure that was executed */ /* 2. Calls time_so_far() and deletes the value of */ /* of the global variable last_timestamp obtaining the time */ /* the procedure spent. */ /* 3. Calls cpu_so_far() and substracts the value of */ /* of the global variable last_cpustamp obtaining the */ /* amount of CPU procedure spent. */ /*********************************************************************/ /* OUTPUT: doesn´t return any value because it is void */ /*********************************************************************/ static void get_delta_t(test) tests_t test; { int which = (int) test; delta[which][Elapsed] = time_so_far() - last_timestamp; delta[which][CPU] = cpu_so_far() - last_cpustamp; } /*********************************************************************/ /* FUNCTION cpu_so_far */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Identifies the System to check some variables */ /* if SysV */ /* i) Creates a structure tms. */ /* ii) returns the CPU time after adding over */ /* certain fields in the tms structure. */ /* Else */ /* i) Creates a structure rusage. */ /* ii) Calls getrusage().(NOTE: Use man pages */ /* to view the specific parameters write uses. */ /* Make sure you understand the return values, */ /* and see the errors the function returns). */ /* iii) returns the CPU time after adding over */ /* certain fields in the rusage structure. */ /*********************************************************************/ /* OUTPUT: return the CPU usage until the time it has been called */ /*********************************************************************/ static double cpu_so_far() { #if defined(SysV) struct tms tms; if (times(&tms) == -1) io_error("times"); return ((double) tms.tms_utime) / ((double) sysconf(_SC_CLK_TCK)) + ((double) tms.tms_stime) / ((double) sysconf(_SC_CLK_TCK)); #else struct rusage rusage; getrusage(RUSAGE_SELF, &rusage); return ((double) rusage.ru_utime.tv_sec) + (((double) rusage.ru_utime.tv_usec) / 1000000.0) + ((double) rusage.ru_stime.tv_sec) + (((double) rusage.ru_stime.tv_usec) / 1000000.0); #endif } /*********************************************************************/ /* FUNCTION time_so_far */ /*********************************************************************/ /* INPUT: does not receive any parameter */ /*********************************************************************/ /* PROCESS: */ /* 1. Identifies the System to check some variables */ /* if SysV */ /* i) Creates a structure tms. */ /* ii) Call the times function and assign the */ /* value it returns to the variable val */ /* iii) returns the CPU time after dividing val*/ /* by the returning value from sysconf() */ /* Else */ /* i) Creates a structure timeval tp. */ /* ii) Calls gettimeofday().(NOTE:Use man pages*/ /* to view the specific parameters */ /* gettimeofday uses. Make sure you understand */ /* the return values, and see the errors the */ /* function returns). */ /* iii) returns the CPU time after substractin */ /* certain fields in the tp structure. */ /*********************************************************************/ /* OUTPUT: return the time it has been used */ /*********************************************************************/ static double time_so_far() { #if defined(SysV) int val; struct tms tms; if ((val = times(&tms)) == -1) io_error("times"); return ((double) val) / ((double) sysconf(_SC_CLK_TCK)); #else struct timeval tp; if (gettimeofday(&tp, (struct timezone *) NULL) == -1) io_error("gettimeofday"); return ((double) (tp.tv_sec - basetime)) + (((double) tp.tv_usec) / 1000000.0); #endif } /*********************************************************************/ /* FUNCTION io_error */ /*********************************************************************/ /* INPUT: Receives the error string */ /*********************************************************************/ /* PROCESS: */ /* 1. Call perror().(NOTE:Use man pages to view the */ /* specific parameters gettimeofday uses. Make sure you */ /* understand the return values, and see the errors the */ /* function returns). */ /* 2. Call exit().(NOTE:Use man pages to view the */ /* specific parameters gettimeofday uses. Make sure you */ /* understand the return values, and see the errors the */ /* function returns). */ /*********************************************************************/ /* OUTPUT: return the time it has been used */ /*********************************************************************/ static void io_error(char *message) { char buf[Chunk]; sprintf(buf, "Program: drastic I/O error (%s)", message); perror(buf); exit(1); } /* * Do a typical-of-something random I/O. Any serious application that * has a random I/O bottleneck is going to be smart enough to operate * in a page mode. * The 'where' argument is used as a chunk number * To keep the cache from getting too clever, some pages must be updated. * However an application that updated each of many random pages that * it looked at is hard to imagine. * However, it would be wrong to put the update percentage in as a * parameter - the effect is too nonlinear. */ static void doseek(off_t where, int fd, int update) { int buf[Chunk / IntSize]; off_t probe; off_t size; probe = where * Chunk; if (lseek(fd, probe, 0) != probe) io_error("lseek in doseek"); if ((size = read(fd, (char *) buf, Chunk)) == -1) io_error("read in doseek"); /* every so often, update a block */ if (update) { /* update this block */ /* touch a word */ buf[((int) random() % (size / IntSize - 2)) + 1]--; if (lseek(fd, (long) probe, 0) != probe) io_error("lseek in doseek update"); if (write(fd, (char *) buf, size) == -1) io_error("write in doseek"); } /* update this block */ } #if defined(SysV) static char randseed[32]; static void srandom(int seed) { sprintf(randseed, "%06d", seed); } static long random() { return nrand48(randseed); } #endif

assign4-2/assign4_part1/testrunner.h

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ typedef int (*test_fp) (int, const char **); typedef struct { const char *name; const char *suite; test_fp test_function; } testentry_t; int run_testrunner(int argc, const char **argv, testentry_t * entries, int entry_count); void set_testrunner_default_timeout(int s); void set_testrunner_timeout(int s);

assign4-2/assign4_part1/teardown.sh

#!/bin/sh #*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE *************** rm -rf ./testdata

assign4-2/assign4_part1/restart.h

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ #include <fcntl.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #ifndef ETIME #define ETIME ETIMEDOUT #endif struct timeval add2currenttime(double seconds); int copyfile(int fromfd, int tofd); int r_close(int fildes); int r_dup2(int fildes, int fildes2); int r_open2(const char *path, int oflag); int r_open3(const char *path, int oflag, mode_t mode); ssize_t r_read(int fd, void *buf, size_t size); pid_t r_wait(int *stat_loc); pid_t r_waitpid(pid_t pid, int *stat_loc, int options); ssize_t r_write(int fd, void *buf, size_t size); ssize_t readblock(int fd, void *buf, size_t size); int readline(int fd, char *buf, int nbytes); ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds); int readwrite(int fromfd, int tofd); int readwriteblock(int fromfd, int tofd, char *buf, int size); int waitfdtimed(int fd, struct timeval end);

assign4-2/assign4_part1/util.h

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ #include <stdlib.h> /* stdlib required for size_t */ #define quit_if_ne(p1,p2) {if((p1) != (p2)) quit_with_message((int)errno,"" #p1 "!=" #p2 ,__FILE__,__LINE__);} #define quit_if(mutley) {if(mutley) quit_with_message((int)errno, #mutley,__FILE__,__LINE__);} #define quit(mesg) {quit_with_message((int)errno,mesg,__FILE__,__LINE__);} void quit_with_message(int errn, char *mesg, char *src, int line); unsigned short checksum(char *buffer, size_t sz, unsigned short val);

assign4-2/assign4_part1/fileio.h

#include <stdlib.h> #define IOERR_INVALID_ARGS (-1) #define IOERR_INVALID_PATH (-2) #define IOERR_POSIX (-3) #define IOERR_BUFFER_TOO_SMALL (-4) #define IOERR_NOT_YET_IMPLEMENTED (-5) /* You will implement all of the following functions in fileio.c. */ /* ERROR HANDLING These functions will support basic error handling: If the arguments are bad (e.g., a pointer is NULL), then the function should immediately return IOERR_INVALID_ARGS. If a POSIX error occurs, the function should return IOERR_POSIX, unless the error is due to a bad path, in which case the return value is IOERR_INVALID_PATH. These functions do not return if a POSIX function is interrupted by a signal; only dirlist returns IOERR_BUFFER_TOO_SMALL (see below). You may find some of the library functions in restart.c useful. COMMON PARAMETERS path - file to be read offest - where to start reading/writing from buffer - buffer to write bytes into bufbytes - maximum number of bytes to read FUNCTION DESCRIPTIONS int file_read(char *path, int offset, void *buffer, size_t bufbytes); Reads bytes from a file into the buffer. Return value: >=0 number of bytes read, <0 ERROR (see above) int file_info(char *path, void *buffer, size_t bufbytes) Writes into buffer a string that describes meta information about the file. The string is in format: "Size:NNN Accessed:NNN Modified:NNN Type X" X can be 'f' or 'd' (file or directory, respectively) Size is in bytes Accessed and Modified - accessed and modified times (seconds since epoch) int file_write(char *path, int offset, void *buffer, size_t bufbytes); Writes bytes from 'buffer' into file 'path' at position 'offset'. Return value: >0 number of bytes written, <0 ERROR (see above) int file_create(char *path, char *pattern, int repeatcount); Creates a new file with the string 'pattern' repeated 'repeatcount' times. Return value:0 Success , <0 ERROR (see above) int file_remove(char *path); Removes an existing file if it exists. Return value: 0 file removed, <0 ERROR (see above) int dir_create(char *path); Creates a new directory. Return value: 0 directory created, <0 ERROR (see above) int dir_list(char *path, void *buffer, size_t bufbytes); Writes a list file and directory names inside path (including "." and ".." entries). Each entry is line terminated with the newline character '\n'. Return value: 0 success, <0 ERROR (see above) Returns IOERR_BUFFER_TOO_SMALL if the buffer is not large enough to write all entries. Hint: man opendir, readdir, closedir int file_checksum(char *path) Calculates a checksum value calculated by summing all the bytes of a file using an unsigned short. Return value: >=0 checksum value, <0 ERROR (see above) Hint: use the checksum function in util.c int dir_checksum(char *path); Recursively calculated a checksum: checksums of regular files are calculated using file_checksum; directory entries are traversed, also the subdirectory names are included in the checksum calculation See lmp1_tests.c for details of what we are looking for. Return value: >=0 checksum value, <0 ERROR (see above) Hint: during development add debug statements, e.g.: fprintf(stderr,"xxx=%d\n",numbytes); write(2,buffer,numbytes); write(2,"RETURNING x",11); to your code in fileio.c or lmp1_tests.c Hint2: don't forget the POSIX man pages of open/close/write/read/lseek/opendir/readdir/closedir/mkdir */ int file_read(char *path, int offset, void *buffer, size_t bufbytes); int file_info(char *path, void *buffer, size_t bufbytes); int file_write(char *path, int offset, void *buffer, size_t bufbytes); int dir_create(char *path); int dir_list(char *path, void *buffer, size_t bufbytes); int file_create(char *path, char *pattern, int repeatcount); int file_remove(char *path); int file_checksum(char *path); int dir_checksum(char *path);

assign4-2/assign4_part1/README.txt

LMP1: I/O and Filesystems ========================= Welcome to LMP1, the first long MP. LMP1 is the first stage of a project aimed at creating a simple yet functional networked filesystem. In this MP, you will learn about and use POSIX file system calls, while subsequent LMPs will introduce memory management, messaging, and networking functionality. If you implement all parts of this MP correctly, you will be able to reuse your code for future MPs. This first LMP concentrates on the file I/O portion of the project. Specifically, you will implement a custom filesystem and test its performance using a filesystem benchmark. A benchmark is an application used to test the performance of some aspect of the system. We will be using Bonnie, a real filesystem benchmark, to test various performance aspects of the filesystem we implement. LMP1 consists of four steps: 1. Read the code; run the Bonnie benchmark and the LMP1 test suite. 2. Implement Test Suite 1 functionality, encompassing basic file I/O operations. 3. Implement Test Suite 2-4 functionality (directory operations, file creation/deletion, and recursive checksumming). 4. Modify Bonnie to use your client-server file I/O methods. Code structure -------------- The code for this project is structured according to the client-server model. The client code (filesystem benchmark) will interact with the server (filesystem) only through interface functions defined in fileio.h: int file_read(char *path, int offset, void *buffer, size_t bufbytes); int file_info(char *path, void *buffer, size_t bufbytes); int file_write(char *path, int offset, void *buffer, size_t bufbytes); int file_create(char *path,char *pattern, int repeatcount); int file_remove(char *path); int dir_create(char *path); int dir_list(char *path,void *buffer, size_t bufbytes); int file_checksum(char *path); int dir_checksum(char *path); These functions represent a simple interface to our filesystem. In Steps 2 and 3 of this MP, you will write the code for functions implementing this interface, replacing the stub code in fileio.c. In Step 4, you will modify a Bonnie method to use this interface, rather than calling the normal POSIX I/O functions directly. The purpose of Step 4 is to help test our implementation. Step 1: Understanding the code ------------------------------ 1. Compile the project, execute Bonnie and the test framework. Note: you may need to add execute permissions to the .sh files using the command "chmod +x *.sh". Try the following: make ./lmp1 (this runs the Bonnie benchmark - it may take a little while) ./lmp1 -test suite1 (run Test Suite 1 - this has to work for stage1) make test (run all tests - this has to work for stage2) 2. Read through the provided .c and .h files and understand how this project is organized: bonnie.c - a version of the filesystem benchmark fileio.c - file I/O functions to be implemented fileio.h - declaration of file I/O functions restart.c - restart library (available for use in fileio.c) restart.h - declaration of restart library functions util.c - useful utility functions util.h - declaration of useful utility functions and macros In particular, pay close attention to the comments in fileio.h and bonnieb.c. You should understand what each of the following functions in bonnie.c does before undertaking the remainder of the MP: fill_file_char() file_read_rewrite() file_read_rewrite_block() fill_file_block() fill_read_getc() file_read_chunk() newfile() Step 2: Basic I/O operations ---------------------------- Implement file_read, file_write and file_info operations in fileio.c. If done correctly, your code should pass all suite1 tests: ./lmp1 -test suite1 Running tests... 1.read ::pass 2.info ::pass 3.write ::pass Test Results:3 tests,3 passed,0 failed. IMPORTANT: fileio.c is the only file you should modify for this step. Step 3: More filesystem operations ---------------------------------- Implement file and directory operations for suite2 (dir_create and dir_list), suite3 (file_create and file_remove), and suite4 (file_checksum and dir_checksum). You can test each operation and test suite individually: ./lmp1 -test dirlist ./lmp1 -test suite2 All tests should now pass: make test Running tests... 1.read ::pass 2.info ::pass 3.write ::pass 4.dirlist ::pass 5.dircreate ::pass 6.remove ::pass 7.create ::pass 8.filechecksum ::pass 9.dirchecksum ::pass Test Results:9 tests,9 passed,0 failed. Step 4: Performance testing --------------------------- In this step, we will change parts of Bonnie to use our filesystem interface. Make the function file_read_rewrite_block() in bonnie.c to call your fileio.c functions instead of POSIX I/O operations. When answering the questions below, use this modified version of bonnie.c. Before making this change, it's a good idea to write pseudocode comments for what each part of file_read_rewrite_block() does, so that you understand the code and can perform the change correctly. There may not be an exact one-to-one correspondence between our filesystem interface and the POSIX commands. Note: In future LMPs, we will be using the fileio.h interface in a similar manner, but we will call the functions remotely, via a message queue. Questions --------- Q1. Briefly explain what the following code from bonnie.c does: if ((words = read(fd, (char *) buf, Chunk)) == -1) ... Q2. Is the above an example of a block read or a character read? What is the value of the variable 'words' if the read succeeds? Fails? Q3. Explain the meaning of the flag value (O_CREAT | O_WRONLY | O_APPEND) for the POSIX function open(). Q4. Run Bonnie. What is being measured by each test function? Q5. Look at the summary results from the Bonnie run in Q4. Does Bonnie measure latency, throughput or something else? Justify your answer. Q6. Compare character reads with block reads using Bonnie. Which is faster? Why do you think this is the case? Q7. Copy and paste the performance measures output when running Bonnie benchmarks in a local directory and again in an NFS-mounted directory. Is one kind of disk access noticeably slower over the network, or are all tests significantly slower? Your home directory may be an NFS mount, whereas /tmp and /scratch are local disks. To test your code in /tmp, do the following: mkdir /tmp/your_username cp lmp1 /tmp/your_username cd /tmp/your_username ./lmp1 (record the output) cd rm -fr /tmp/your_username Q8. How does Bonnie handle incomplete reads, e.g., due to interruptions from signals? Justify why Bonnie's approach is good or bad for a filesystem benchmark program. Q9. By now you should be very familiar with the self-evaluation test harness we provide for the MPs. Examine the function test_file_read() in lmp1_tests.c, which tests your file_read() function from Step 2. What does this test check for, specifically? You may want to copy and paste the code for this function in your answer, and annotate each quit_if or group of related quit_ifs with a comment.

assign4-2/assign4_part1/testrunner.c

/*************** YOU SHOULD NOT MODIFY ANYTHING IN THIS FILE ***************/ /* A simple testrunner framework Original Author: L. Angrave */ #include <sys/types.h> #include <sys/wait.h> #include <errno.h> #include <stdio.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include "testrunner.h" /* Constants */ #define false (0) #define true (1) #define test_killed (2) /* defaults */ static int default_timeout_seconds = 15; 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,"\n%s(%d):%s\n",__fname__,__LINE__,mesg); exit(1);} static int eql(const char *s1, const 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((const char *) p1, (const 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, const 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\n", 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\n"); 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, const 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\n", (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) { const char **array; const char *previous; int i; array = (const char **) calloc(sizeof(const char *), count); /* Sort the test names and print unique entries */ for (i = 0; i < count; i++) array[i] = tests[i].name; qsort(array, count, sizeof(array[0]), mystrcmp); printf("\nValid tests : all"); for (i = 0, previous = ""; i < count; i++) if (!eql(previous, array[i])) printf(" %s", (previous = array[i])); /* Sort the suite names and print unique entries */ for (i = 0; i < count; i++) array[i] = tests[i].suite; qsort(array, count, sizeof(array[0]), mystrcmp); printf("\nValid suites:"); for (i = 0, previous = ""; i < count; i++) if (!eql(previous, array[i])) printf(" %s", (previous = array[i])); printf("\n"); } /* * Main entry point for test harness */ int run_testrunner(int argc, const char **argv, testentry_t tests[], int test_count) { const 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_count && (max_errors_before_quit < 1 || stats.failed != max_errors_before_quit); 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...\n"); 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("\nTest Results:%d tests,%d passed,%d failed.\n", stats.ran, stats.passed, stats.failed); } return stats.passed == stats.ran && target_matched ? 0 : 1; }