commit 92fa2c791ac4d2a27497c5d6bd0fb33430bb5003 Author: Benjamin Scott Pruett Date: Wed Mar 25 14:04:21 2015 -0400 initial import of base files diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..499f902 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# +# Students' Makefile for the Malloc Lab +# +VERSION = 1 + +CC = gcc +CFLAGS = -Wall -O3 -Werror -m32 +# for debugging +#CFLAGS = -Wall -g -Werror -m32 + +SHARED_OBJS = mdriver.o memlib.o fsecs.o fcyc.o clock.o ftimer.o list.o +OBJS = $(SHARED_OBJS) mm.o +BOOK_IMPL_OBJS = $(SHARED_OBJS) mm-book-implicit.o +GBACK_IMPL_OBJS = $(SHARED_OBJS) mm-gback-implicit.o + +mdriver: $(OBJS) + $(CC) $(CFLAGS) -o mdriver $(OBJS) + +mdriver-book: $(BOOK_IMPL_OBJS) + $(CC) $(CFLAGS) -o $@ $(BOOK_IMPL_OBJS) + +mdriver-gback: $(GBACK_IMPL_OBJS) + $(CC) $(CFLAGS) -o $@ $(GBACK_IMPL_OBJS) + +mdriver.o: mdriver.c fsecs.h fcyc.h clock.h memlib.h config.h mm.h +memlib.o: memlib.c memlib.h +mm.o: mm.c mm.h memlib.h +fsecs.o: fsecs.c fsecs.h config.h +fcyc.o: fcyc.c fcyc.h +ftimer.o: ftimer.c ftimer.h config.h +clock.o: clock.c clock.h +list.o: list.c list.h + +handin: + /home/courses/cs3214/bin/submit.pl p3 mm.c + +clean: + rm -f *~ *.o mdriver + + diff --git a/clock.c b/clock.c new file mode 100644 index 0000000..8afcfde --- /dev/null +++ b/clock.c @@ -0,0 +1,279 @@ +/* + * clock.c - Routines for using the cycle counters on x86, + * Alpha, and Sparc boxes. + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + */ + +#include +#include +#include +#include +#include "clock.h" + + +/******************************************************* + * Machine dependent functions + * + * Note: the constants __i386__ and __alpha + * are set by GCC when it calls the C preprocessor + * You can verify this for yourself using gcc -v. + *******************************************************/ + +#if defined(__i386__) +/******************************************************* + * Pentium versions of start_counter() and get_counter() + *******************************************************/ + + +/* $begin x86cyclecounter */ +/* Initialize the cycle counter */ +static unsigned cyc_hi = 0; +static unsigned cyc_lo = 0; + + +/* Set *hi and *lo to the high and low order bits of the cycle counter. + Implementation requires assembly code to use the rdtsc instruction. */ +void access_counter(unsigned *hi, unsigned *lo) +{ + asm volatile ("rdtsc; movl %%edx,%0; movl %%eax,%1" /* Read cycle counter */ + : "=r" (*hi), "=r" (*lo) /* and move results to */ + : /* No input */ /* the two outputs */ + : "%edx", "%eax"); +} + +/* Record the current value of the cycle counter. */ +void start_counter() +{ + access_counter(&cyc_hi, &cyc_lo); +} + +/* Return the number of cycles since the last call to start_counter. */ +double get_counter() +{ + unsigned ncyc_hi, ncyc_lo; + unsigned hi, lo, borrow; + double result; + + /* Get cycle counter */ + access_counter(&ncyc_hi, &ncyc_lo); + + /* Do double precision subtraction */ + lo = ncyc_lo - cyc_lo; + borrow = lo > ncyc_lo; + hi = ncyc_hi - cyc_hi - borrow; + result = (double) hi * (1 << 30) * 4 + lo; + if (result < 0) { + fprintf(stderr, "Error: counter returns neg value: %.0f\n", result); + } + return result; +} +/* $end x86cyclecounter */ + +#elif defined(__alpha) + +/**************************************************** + * Alpha versions of start_counter() and get_counter() + ***************************************************/ + +/* Initialize the cycle counter */ +static unsigned cyc_hi = 0; +static unsigned cyc_lo = 0; + + +/* Use Alpha cycle timer to compute cycles. Then use + measured clock speed to compute seconds +*/ + +/* + * counterRoutine is an array of Alpha instructions to access + * the Alpha's processor cycle counter. It uses the rpcc + * instruction to access the counter. This 64 bit register is + * divided into two parts. The lower 32 bits are the cycles + * used by the current process. The upper 32 bits are wall + * clock cycles. These instructions read the counter, and + * convert the lower 32 bits into an unsigned int - this is the + * user space counter value. + * NOTE: The counter has a very limited time span. With a + * 450MhZ clock the counter can time things for about 9 + * seconds. */ +static unsigned int counterRoutine[] = +{ + 0x601fc000u, + 0x401f0000u, + 0x6bfa8001u +}; + +/* Cast the above instructions into a function. */ +static unsigned int (*counter)(void)= (void *)counterRoutine; + + +void start_counter() +{ + /* Get cycle counter */ + cyc_hi = 0; + cyc_lo = counter(); +} + +double get_counter() +{ + unsigned ncyc_hi, ncyc_lo; + unsigned hi, lo, borrow; + double result; + ncyc_lo = counter(); + ncyc_hi = 0; + lo = ncyc_lo - cyc_lo; + borrow = lo > ncyc_lo; + hi = ncyc_hi - cyc_hi - borrow; + result = (double) hi * (1 << 30) * 4 + lo; + if (result < 0) { + fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); + } + return result; +} + +#else + +/**************************************************************** + * All the other platforms for which we haven't implemented cycle + * counter routines. Newer models of sparcs (v8plus) have cycle + * counters that can be accessed from user programs, but since there + * are still many sparc boxes out there that don't support this, we + * haven't provided a Sparc version here. + ***************************************************************/ + +void start_counter() +{ + printf("ERROR: You are trying to use a start_counter routine in clock.c\n"); + printf("that has not been implemented yet on this platform.\n"); + printf("Please choose another timing package in config.h.\n"); + exit(1); +} + +double get_counter() +{ + printf("ERROR: You are trying to use a get_counter routine in clock.c\n"); + printf("that has not been implemented yet on this platform.\n"); + printf("Please choose another timing package in config.h.\n"); + exit(1); +} +#endif + + + + +/******************************* + * Machine-independent functions + ******************************/ +double ovhd() +{ + /* Do it twice to eliminate cache effects */ + int i; + double result; + + for (i = 0; i < 2; i++) { + start_counter(); + result = get_counter(); + } + return result; +} + +/* $begin mhz */ +/* Estimate the clock rate by measuring the cycles that elapse */ +/* while sleeping for sleeptime seconds */ +double mhz_full(int verbose, int sleeptime) +{ + double rate; + + start_counter(); + sleep(sleeptime); + rate = get_counter() / (1e6*sleeptime); + if (verbose) + printf("Processor clock rate ~= %.1f MHz\n", rate); + return rate; +} +/* $end mhz */ + +/* Version using a default sleeptime */ +double mhz(int verbose) +{ + return mhz_full(verbose, 2); +} + +/** Special counters that compensate for timer interrupt overhead */ + +static double cyc_per_tick = 0.0; + +#define NEVENT 100 +#define THRESHOLD 1000 +#define RECORDTHRESH 3000 + +/* Attempt to see how much time is used by timer interrupt */ +static void callibrate(int verbose) +{ + double oldt; + struct tms t; + clock_t oldc; + int e = 0; + + times(&t); + oldc = t.tms_utime; + start_counter(); + oldt = get_counter(); + while (e = THRESHOLD) { + clock_t newc; + times(&t); + newc = t.tms_utime; + if (newc > oldc) { + double cpt = (newt-oldt)/(newc-oldc); + if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) + cyc_per_tick = cpt; + /* + if (verbose) + printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n", + newt-oldt, (int) (newc-oldc), cpt); + */ + e++; + oldc = newc; + } + oldt = newt; + } + } + if (verbose) + printf("Setting cyc_per_tick to %f\n", cyc_per_tick); +} + +static clock_t start_tick = 0; + +void start_comp_counter() +{ + struct tms t; + + if (cyc_per_tick == 0.0) + callibrate(0); + times(&t); + start_tick = t.tms_utime; + start_counter(); +} + +double get_comp_counter() +{ + double time = get_counter(); + double ctime; + struct tms t; + clock_t ticks; + + times(&t); + ticks = t.tms_utime - start_tick; + ctime = time - ticks*cyc_per_tick; + /* + printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n", + time, (int) ticks, ctime); + */ + return ctime; +} + diff --git a/clock.h b/clock.h new file mode 100644 index 0000000..54d26ac --- /dev/null +++ b/clock.h @@ -0,0 +1,22 @@ +/* Routines for using cycle counter */ + +/* Start the counter */ +void start_counter(); + +/* Get # cycles since counter started */ +double get_counter(); + +/* Measure overhead for counter */ +double ovhd(); + +/* Determine clock rate of processor (using a default sleeptime) */ +double mhz(int verbose); + +/* Determine clock rate of processor, having more control over accuracy */ +double mhz_full(int verbose, int sleeptime); + +/** Special counters that compensate for timer interrupt overhead */ + +void start_comp_counter(); + +double get_comp_counter(); diff --git a/config.h b/config.h new file mode 100644 index 0000000..2df9fb8 --- /dev/null +++ b/config.h @@ -0,0 +1,76 @@ +#ifndef __CONFIG_H_ +#define __CONFIG_H_ + +/* + * config.h - malloc lab configuration file + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + */ + +/* + * This is the default path where the driver will look for the + * default tracefiles. You can override it at runtime with the -t flag. + */ +#define TRACEDIR "/home/courses/cs3214/malloclab/traces/" + +/* + * This is the list of default tracefiles in TRACEDIR that the driver + * will use for testing. Modify this if you want to add or delete + * traces from the driver's test suite. For example, if you don't want + * your students to implement realloc, you can delete the last two + * traces. + */ +#define DEFAULT_TRACEFILES \ + "amptjp-bal.rep",\ + "cccp-bal.rep",\ + "cp-decl-bal.rep",\ + "expr-bal.rep",\ + "coalescing-bal.rep",\ + "random-bal.rep",\ + "random2-bal.rep",\ + "binary-bal.rep",\ + "binary2-bal.rep",\ + "realloc-bal.rep",\ + "realloc2-bal.rep" + +/* + * This constant gives the estimated performance of the libc malloc + * package using our traces on some reference system, typically the + * same kind of system the students use. Its purpose is to cap the + * contribution of throughput to the performance index. Once the + * students surpass the AVG_LIBC_THRUPUT, they get no further benefit + * to their score. This deters students from building extremely fast, + * but extremely stupid malloc packages. + * + * gback@cs.vt.edu: I set this to a value that is achieved by a r/b + * tree-based implementation on our rlogin cluster as of Fall 2014; + * regardless of the speed of the actual libc + */ +#define AVG_LIBC_THRUPUT 14.6E6 /* 14600 Kops/sec */ + + /* + * This constant determines the contributions of space utilization + * (UTIL_WEIGHT) and throughput (1 - UTIL_WEIGHT) to the performance + * index. + */ +#define UTIL_WEIGHT .60 + +/* + * Alignment requirement in bytes (either 4 or 8) + */ +#define ALIGNMENT 8 + +/* + * Maximum heap size in bytes + */ +#define MAX_HEAP (20*(1<<20)) /* 20 MB */ + +/***************************************************************************** + * Set exactly one of these USE_xxx constants to "1" to select a timing method + *****************************************************************************/ +#define USE_FCYC 1 /* cycle counter w/K-best scheme (x86 & Alpha only) */ +#define USE_ITIMER 0 /* interval timer (any Unix box) */ +#define USE_GETTOD 0 /* gettimeofday (any Unix box) */ + +#endif /* __CONFIG_H */ diff --git a/fcyc.c b/fcyc.c new file mode 100644 index 0000000..420b306 --- /dev/null +++ b/fcyc.c @@ -0,0 +1,251 @@ +/* + * fcyc.c - Estimate the time (in CPU cycles) used by a function f + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + * + * Uses the cycle timer routines in clock.c to estimate the + * the time in CPU cycles for a function f. + */ +#include +#include +#include + +#include "fcyc.h" +#include "clock.h" + +/* Default values */ +#define K 3 /* Value of K in K-best scheme */ +#define MAXSAMPLES 20 /* Give up after MAXSAMPLES */ +#define EPSILON 0.01 /* K samples should be EPSILON of each other*/ +#define COMPENSATE 0 /* 1-> try to compensate for clock ticks */ +#define CLEAR_CACHE 0 /* Clear cache before running test function */ +#define CACHE_BYTES (1<<19) /* Max cache size in bytes */ +#define CACHE_BLOCK 32 /* Cache block size in bytes */ + +static int kbest = K; +static int maxsamples = MAXSAMPLES; +static double epsilon = EPSILON; +static int compensate = COMPENSATE; +static int clear_cache = CLEAR_CACHE; +static int cache_bytes = CACHE_BYTES; +static int cache_block = CACHE_BLOCK; + +static int *cache_buf = NULL; + +static double *values = NULL; +static int samplecount = 0; + +/* for debugging only */ +#define KEEP_VALS 0 +#define KEEP_SAMPLES 0 + +#if KEEP_SAMPLES +static double *samples = NULL; +#endif + +/* + * init_sampler - Start new sampling process + */ +static void init_sampler() +{ + if (values) + free(values); + values = calloc(kbest, sizeof(double)); +#if KEEP_SAMPLES + if (samples) + free(samples); + /* Allocate extra for wraparound analysis */ + samples = calloc(maxsamples+kbest, sizeof(double)); +#endif + samplecount = 0; +} + +/* + * add_sample - Add new sample + */ +static void add_sample(double val) +{ + int pos = 0; + if (samplecount < kbest) { + pos = samplecount; + values[pos] = val; + } else if (val < values[kbest-1]) { + pos = kbest-1; + values[pos] = val; + } +#if KEEP_SAMPLES + samples[samplecount] = val; +#endif + samplecount++; + /* Insertion sort */ + while (pos > 0 && values[pos-1] > values[pos]) { + double temp = values[pos-1]; + values[pos-1] = values[pos]; + values[pos] = temp; + pos--; + } +} + +/* + * has_converged- Have kbest minimum measurements converged within epsilon? + */ +static int has_converged() +{ + return + (samplecount >= kbest) && + ((1 + epsilon)*values[0] >= values[kbest-1]); +} + +/* + * clear - Code to clear cache + */ +static volatile int sink = 0; + +static void clear() +{ + int x = sink; + int *cptr, *cend; + int incr = cache_block/sizeof(int); + if (!cache_buf) { + cache_buf = malloc(cache_bytes); + if (!cache_buf) { + fprintf(stderr, "Fatal error. Malloc returned null when trying to clear cache\n"); + exit(1); + } + } + cptr = (int *) cache_buf; + cend = cptr + cache_bytes/sizeof(int); + while (cptr < cend) { + x += *cptr; + cptr += incr; + } + sink = x; +} + +/* + * fcyc - Use K-best scheme to estimate the running time of function f + */ +double fcyc(test_funct f, void *argp) +{ + double result; + init_sampler(); + if (compensate) { + do { + double cyc; + if (clear_cache) + clear(); + start_comp_counter(); + f(argp); + cyc = get_comp_counter(); + add_sample(cyc); + } while (!has_converged() && samplecount < maxsamples); + } else { + do { + double cyc; + if (clear_cache) + clear(); + start_counter(); + f(argp); + cyc = get_counter(); + add_sample(cyc); + } while (!has_converged() && samplecount < maxsamples); + } +#ifdef DEBUG + { + int i; + printf(" %d smallest values: [", kbest); + for (i = 0; i < kbest; i++) + printf("%.0f%s", values[i], i==kbest-1 ? "]\n" : ", "); + } +#endif + result = values[0]; +#if !KEEP_VALS + free(values); + values = NULL; +#endif + return result; +} + + +/************************************************************* + * Set the various parameters used by the measurement routines + ************************************************************/ + +/* + * set_fcyc_clear_cache - When set, will run code to clear cache + * before each measurement. + * Default = 0 + */ +void set_fcyc_clear_cache(int clear) +{ + clear_cache = clear; +} + +/* + * set_fcyc_cache_size - Set size of cache to use when clearing cache + * Default = 1<<19 (512KB) + */ +void set_fcyc_cache_size(int bytes) +{ + if (bytes != cache_bytes) { + cache_bytes = bytes; + if (cache_buf) { + free(cache_buf); + cache_buf = NULL; + } + } +} + +/* + * set_fcyc_cache_block - Set size of cache block + * Default = 32 + */ +void set_fcyc_cache_block(int bytes) { + cache_block = bytes; +} + + +/* + * set_fcyc_compensate- When set, will attempt to compensate for + * timer interrupt overhead + * Default = 0 + */ +void set_fcyc_compensate(int compensate_arg) +{ + compensate = compensate_arg; +} + +/* + * set_fcyc_k - Value of K in K-best measurement scheme + * Default = 3 + */ +void set_fcyc_k(int k) +{ + kbest = k; +} + +/* + * set_fcyc_maxsamples - Maximum number of samples attempting to find + * K-best within some tolerance. + * When exceeded, just return best sample found. + * Default = 20 + */ +void set_fcyc_maxsamples(int maxsamples_arg) +{ + maxsamples = maxsamples_arg; +} + +/* + * set_fcyc_epsilon - Tolerance required for K-best + * Default = 0.01 + */ +void set_fcyc_epsilon(double epsilon_arg) +{ + epsilon = epsilon_arg; +} + + + + + diff --git a/fcyc.h b/fcyc.h new file mode 100644 index 0000000..d398278 --- /dev/null +++ b/fcyc.h @@ -0,0 +1,68 @@ +/* + * fcyc.h - prototypes for the routines in fcyc.c that estimate the + * time in CPU cycles used by a test function f + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + * + */ + +/* The test function takes a generic pointer as input */ +typedef void (*test_funct)(void *); + +/* Compute number of cycles used by test function f */ +double fcyc(test_funct f, void* argp); + +/********************************************************* + * Set the various parameters used by measurement routines + *********************************************************/ + +/* + * set_fcyc_clear_cache - When set, will run code to clear cache + * before each measurement. + * Default = 0 + */ +void set_fcyc_clear_cache(int clear); + +/* + * set_fcyc_cache_size - Set size of cache to use when clearing cache + * Default = 1<<19 (512KB) + */ +void set_fcyc_cache_size(int bytes); + +/* + * set_fcyc_cache_block - Set size of cache block + * Default = 32 + */ +void set_fcyc_cache_block(int bytes); + +/* + * set_fcyc_compensate- When set, will attempt to compensate for + * timer interrupt overhead + * Default = 0 + */ +void set_fcyc_compensate(int compensate_arg); + +/* + * set_fcyc_k - Value of K in K-best measurement scheme + * Default = 3 + */ +void set_fcyc_k(int k); + +/* + * set_fcyc_maxsamples - Maximum number of samples attempting to find + * K-best within some tolerance. + * When exceeded, just return best sample found. + * Default = 20 + */ +void set_fcyc_maxsamples(int maxsamples_arg); + +/* + * set_fcyc_epsilon - Tolerance required for K-best + * Default = 0.01 + */ +void set_fcyc_epsilon(double epsilon_arg); + + + + diff --git a/fsecs.c b/fsecs.c new file mode 100644 index 0000000..ae2346d --- /dev/null +++ b/fsecs.c @@ -0,0 +1,57 @@ +/**************************** + * High-level timing wrappers + ****************************/ +#include +#include "fsecs.h" +#include "fcyc.h" +#include "clock.h" +#include "ftimer.h" +#include "config.h" + +static double Mhz; /* estimated CPU clock frequency */ + +extern int verbose; /* -v option in mdriver.c */ + +/* + * init_fsecs - initialize the timing package + */ +void init_fsecs(void) +{ + Mhz = 0; /* keep gcc -Wall happy */ + +#if USE_FCYC + if (verbose) + printf("Measuring performance with a cycle counter.\n"); + + /* set key parameters for the fcyc package */ + set_fcyc_maxsamples(20); + set_fcyc_clear_cache(1); + set_fcyc_compensate(1); + set_fcyc_epsilon(0.01); + set_fcyc_k(3); + Mhz = mhz(verbose > 0); +#elif USE_ITIMER + if (verbose) + printf("Measuring performance with the interval timer.\n"); +#elif USE_GETTOD + if (verbose) + printf("Measuring performance with gettimeofday().\n"); +#endif +} + +/* + * fsecs - Return the running time of a function f (in seconds) + */ +double fsecs(fsecs_test_funct f, void *argp) +{ +#if USE_FCYC + double cycles = fcyc(f, argp); + return cycles/(Mhz*1e6); +#elif USE_ITIMER + return ftimer_itimer(f, argp, 10); +#elif USE_GETTOD + return ftimer_gettod(f, argp, 10); +#endif +} + + diff --git a/fsecs.h b/fsecs.h new file mode 100644 index 0000000..59e095d --- /dev/null +++ b/fsecs.h @@ -0,0 +1,4 @@ +typedef void (*fsecs_test_funct)(void *); + +void init_fsecs(void); +double fsecs(fsecs_test_funct f, void *argp); diff --git a/ftimer.c b/ftimer.c new file mode 100644 index 0000000..ea08560 --- /dev/null +++ b/ftimer.c @@ -0,0 +1,106 @@ +/* + * ftimer.c - Estimate the time (in seconds) used by a function f + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + * + * Function timers that estimate the running time (in seconds) of a function f. + * ftimer_itimer: version that uses the interval timer + * ftimer_gettod: version that uses gettimeofday + */ +#include +#include +#include "ftimer.h" + +/* function prototypes */ +static void init_etime(void); +static double get_etime(void); + +/* + * ftimer_itimer - Use the interval timer to estimate the running time + * of f(argp). Return the average of n runs. + */ +double ftimer_itimer(ftimer_test_funct f, void *argp, int n) +{ + double start, tmeas; + int i; + + init_etime(); + start = get_etime(); + for (i = 0; i < n; i++) + f(argp); + tmeas = get_etime() - start; + return tmeas / n; +} + +/* + * ftimer_gettod - Use gettimeofday to estimate the running time of + * f(argp). Return the average of n runs. + */ +double ftimer_gettod(ftimer_test_funct f, void *argp, int n) +{ + int i; + struct timeval stv, etv; + double diff; + + gettimeofday(&stv, NULL); + for (i = 0; i < n; i++) + f(argp); + gettimeofday(&etv,NULL); + diff = 1E3*(etv.tv_sec - stv.tv_sec) + 1E-3*(etv.tv_usec-stv.tv_usec); + diff /= n; + return (1E-3*diff); +} + + +/* + * Routines for manipulating the Unix interval timer + */ + +/* The initial value of the interval timer */ +#define MAX_ETIME 86400 + +/* static variables that hold the initial value of the interval timer */ +static struct itimerval first_u; /* user time */ +static struct itimerval first_r; /* real time */ +static struct itimerval first_p; /* prof time*/ + +/* init the timer */ +static void init_etime(void) +{ + first_u.it_interval.tv_sec = 0; + first_u.it_interval.tv_usec = 0; + first_u.it_value.tv_sec = MAX_ETIME; + first_u.it_value.tv_usec = 0; + setitimer(ITIMER_VIRTUAL, &first_u, NULL); + + first_r.it_interval.tv_sec = 0; + first_r.it_interval.tv_usec = 0; + first_r.it_value.tv_sec = MAX_ETIME; + first_r.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &first_r, NULL); + + first_p.it_interval.tv_sec = 0; + first_p.it_interval.tv_usec = 0; + first_p.it_value.tv_sec = MAX_ETIME; + first_p.it_value.tv_usec = 0; + setitimer(ITIMER_PROF, &first_p, NULL); +} + +/* return elapsed real seconds since call to init_etime */ +static double get_etime(void) { + struct itimerval v_curr; + struct itimerval r_curr; + struct itimerval p_curr; + + getitimer(ITIMER_VIRTUAL, &v_curr); + getitimer(ITIMER_REAL,&r_curr); + getitimer(ITIMER_PROF,&p_curr); + + return (double) ((first_p.it_value.tv_sec - r_curr.it_value.tv_sec) + + (first_p.it_value.tv_usec - r_curr.it_value.tv_usec)*1e-6); +} + + + + diff --git a/ftimer.h b/ftimer.h new file mode 100644 index 0000000..3400603 --- /dev/null +++ b/ftimer.h @@ -0,0 +1,14 @@ +/* + * Function timers + */ +typedef void (*ftimer_test_funct)(void *); + +/* Estimate the running time of f(argp) using the Unix interval timer. + Return the average of n runs */ +double ftimer_itimer(ftimer_test_funct f, void *argp, int n); + + +/* Estimate the running time of f(argp) using gettimeofday + Return the average of n runs */ +double ftimer_gettod(ftimer_test_funct f, void *argp, int n); + diff --git a/list.c b/list.c new file mode 100644 index 0000000..7f5f7bf --- /dev/null +++ b/list.c @@ -0,0 +1,532 @@ +#include "list.h" +#include + +/* Our doubly linked lists have two header elements: the "head" + just before the first element and the "tail" just after the + last element. The `prev' link of the front header is null, as + is the `next' link of the back header. Their other two links + point toward each other via the interior elements of the list. + + An empty list looks like this: + + +------+ +------+ + <---| head |<--->| tail |---> + +------+ +------+ + + A list with two elements in it looks like this: + + +------+ +-------+ +-------+ +------+ + <---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> + +------+ +-------+ +-------+ +------+ + + The symmetry of this arrangement eliminates lots of special + cases in list processing. For example, take a look at + list_remove(): it takes only two pointer assignments and no + conditionals. That's a lot simpler than the code would be + without header elements. + + (Because only one of the pointers in each header element is used, + we could in fact combine them into a single header element + without sacrificing this simplicity. But using two separate + elements allows us to do a little bit of checking on some + operations, which can be valuable.) */ + +static bool is_sorted (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux); + +/* Returns true if ELEM is a head, false otherwise. */ +static inline bool +is_head (struct list_elem *elem) +{ + return elem != NULL && elem->prev == NULL && elem->next != NULL; +} + +/* Returns true if ELEM is an interior element, + false otherwise. */ +static inline bool +is_interior (struct list_elem *elem) +{ + return elem != NULL && elem->prev != NULL && elem->next != NULL; +} + +/* Returns true if ELEM is a tail, false otherwise. */ +static inline bool +is_tail (struct list_elem *elem) +{ + return elem != NULL && elem->prev != NULL && elem->next == NULL; +} + +/* Initializes LIST as an empty list. */ +void +list_init (struct list *list) +{ + assert (list != NULL); + list->head.prev = NULL; + list->head.next = &list->tail; + list->tail.prev = &list->head; + list->tail.next = NULL; +} + +/* Returns the beginning of LIST. */ +struct list_elem * +list_begin (struct list *list) +{ + assert (list != NULL); + return list->head.next; +} + +/* Returns the element after ELEM in its list. If ELEM is the + last element in its list, returns the list tail. Results are + undefined if ELEM is itself a list tail. */ +struct list_elem * +list_next (struct list_elem *elem) +{ + assert (is_head (elem) || is_interior (elem)); + return elem->next; +} + +/* Returns LIST's tail. + + list_end() is often used in iterating through a list from + front to back. See the big comment at the top of list.h for + an example. */ +struct list_elem * +list_end (struct list *list) +{ + assert (list != NULL); + return &list->tail; +} + +/* Returns the LIST's reverse beginning, for iterating through + LIST in reverse order, from back to front. */ +struct list_elem * +list_rbegin (struct list *list) +{ + assert (list != NULL); + return list->tail.prev; +} + +/* Returns the element before ELEM in its list. If ELEM is the + first element in its list, returns the list head. Results are + undefined if ELEM is itself a list head. */ +struct list_elem * +list_prev (struct list_elem *elem) +{ + assert (is_interior (elem) || is_tail (elem)); + return elem->prev; +} + +/* Returns LIST's head. + + list_rend() is often used in iterating through a list in + reverse order, from back to front. Here's typical usage, + following the example from the top of list.h: + + for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); + e = list_prev (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } +*/ +struct list_elem * +list_rend (struct list *list) +{ + assert (list != NULL); + return &list->head; +} + +/* Return's LIST's head. + + list_head() can be used for an alternate style of iterating + through a list, e.g.: + + e = list_head (&list); + while ((e = list_next (e)) != list_end (&list)) + { + ... + } +*/ +struct list_elem * +list_head (struct list *list) +{ + assert (list != NULL); + return &list->head; +} + +/* Return's LIST's tail. */ +struct list_elem * +list_tail (struct list *list) +{ + assert (list != NULL); + return &list->tail; +} + +/* Inserts ELEM just before BEFORE, which may be either an + interior element or a tail. The latter case is equivalent to + list_push_back(). */ +void +list_insert (struct list_elem *before, struct list_elem *elem) +{ + assert (is_interior (before) || is_tail (before)); + assert (elem != NULL); + + elem->prev = before->prev; + elem->next = before; + before->prev->next = elem; + before->prev = elem; +} + +/* Removes elements FIRST though LAST (exclusive) from their + current list, then inserts them just before BEFORE, which may + be either an interior element or a tail. */ +void +list_splice (struct list_elem *before, + struct list_elem *first, struct list_elem *last) +{ + assert (is_interior (before) || is_tail (before)); + if (first == last) + return; + last = list_prev (last); + + assert (is_interior (first)); + assert (is_interior (last)); + + /* Cleanly remove FIRST...LAST from its current list. */ + first->prev->next = last->next; + last->next->prev = first->prev; + + /* Splice FIRST...LAST into new list. */ + first->prev = before->prev; + last->next = before; + before->prev->next = first; + before->prev = last; +} + +/* Inserts ELEM at the beginning of LIST, so that it becomes the + front in LIST. */ +void +list_push_front (struct list *list, struct list_elem *elem) +{ + list_insert (list_begin (list), elem); +} + +/* Inserts ELEM at the end of LIST, so that it becomes the + back in LIST. */ +void +list_push_back (struct list *list, struct list_elem *elem) +{ + list_insert (list_end (list), elem); +} + +/* Removes ELEM from its list and returns the element that + followed it. Undefined behavior if ELEM is not in a list. + + It's not safe to treat ELEM as an element in a list after + removing it. In particular, using list_next() or list_prev() + on ELEM after removal yields undefined behavior. This means + that a naive loop to remove the elements in a list will fail: + + ** DON'T DO THIS ** + for (e = list_begin (&list); e != list_end (&list); e = list_next (e)) + { + ...do something with e... + list_remove (e); + } + ** DON'T DO THIS ** + + Here is one correct way to iterate and remove elements from a + list: + + for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) + { + ...do something with e... + } + + If you need to free() elements of the list then you need to be + more conservative. Here's an alternate strategy that works + even in that case: + + while (!list_empty (&list)) + { + struct list_elem *e = list_pop_front (&list); + ...do something with e... + } +*/ +struct list_elem * +list_remove (struct list_elem *elem) +{ + assert (is_interior (elem)); + elem->prev->next = elem->next; + elem->next->prev = elem->prev; + return elem->next; +} + +/* Removes the front element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem * +list_pop_front (struct list *list) +{ + struct list_elem *front = list_front (list); + list_remove (front); + return front; +} + +/* Removes the back element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem * +list_pop_back (struct list *list) +{ + struct list_elem *back = list_back (list); + list_remove (back); + return back; +} + +/* Returns the front element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem * +list_front (struct list *list) +{ + assert (!list_empty (list)); + return list->head.next; +} + +/* Returns the back element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem * +list_back (struct list *list) +{ + assert (!list_empty (list)); + return list->tail.prev; +} + +/* Returns the number of elements in LIST. + Runs in O(n) in the number of elements. */ +size_t +list_size (struct list *list) +{ + struct list_elem *e; + size_t cnt = 0; + + for (e = list_begin (list); e != list_end (list); e = list_next (e)) + cnt++; + return cnt; +} + +/* Returns true if LIST is empty, false otherwise. */ +bool +list_empty (struct list *list) +{ + return list_begin (list) == list_end (list); +} + +/* Swaps the `struct list_elem *'s that A and B point to. */ +static void +swap (struct list_elem **a, struct list_elem **b) +{ + struct list_elem *t = *a; + *a = *b; + *b = t; +} + +/* Reverses the order of LIST. */ +void +list_reverse (struct list *list) +{ + if (!list_empty (list)) + { + struct list_elem *e; + + for (e = list_begin (list); e != list_end (list); e = e->prev) + swap (&e->prev, &e->next); + swap (&list->head.next, &list->tail.prev); + swap (&list->head.next->prev, &list->tail.prev->next); + } +} + +/* Returns true only if the list elements A through B (exclusive) + are in order according to LESS given auxiliary data AUX. */ +static bool +is_sorted (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux) +{ + if (a != b) + while ((a = list_next (a)) != b) + if (less (a, list_prev (a), aux)) + return false; + return true; +} + +/* Finds a run, starting at A and ending not after B, of list + elements that are in nondecreasing order according to LESS + given auxiliary data AUX. Returns the (exclusive) end of the + run. + A through B (exclusive) must form a non-empty range. */ +static struct list_elem * +find_end_of_run (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux) +{ + assert (a != NULL); + assert (b != NULL); + assert (less != NULL); + assert (a != b); + + do + { + a = list_next (a); + } + while (a != b && !less (a, list_prev (a), aux)); + return a; +} + +/* Merges A0 through A1B0 (exclusive) with A1B0 through B1 + (exclusive) to form a combined range also ending at B1 + (exclusive). Both input ranges must be nonempty and sorted in + nondecreasing order according to LESS given auxiliary data + AUX. The output range will be sorted the same way. */ +static void +inplace_merge (struct list_elem *a0, struct list_elem *a1b0, + struct list_elem *b1, + list_less_func *less, void *aux) +{ + assert (a0 != NULL); + assert (a1b0 != NULL); + assert (b1 != NULL); + assert (less != NULL); + assert (is_sorted (a0, a1b0, less, aux)); + assert (is_sorted (a1b0, b1, less, aux)); + + while (a0 != a1b0 && a1b0 != b1) + if (!less (a1b0, a0, aux)) + a0 = list_next (a0); + else + { + a1b0 = list_next (a1b0); + list_splice (a0, list_prev (a1b0), a1b0); + } +} + +/* Sorts LIST according to LESS given auxiliary data AUX, using a + natural iterative merge sort that runs in O(n lg n) time and + O(1) space in the number of elements in LIST. */ +void +list_sort (struct list *list, list_less_func *less, void *aux) +{ + size_t output_run_cnt; /* Number of runs output in current pass. */ + + assert (list != NULL); + assert (less != NULL); + + /* Pass over the list repeatedly, merging adjacent runs of + nondecreasing elements, until only one run is left. */ + do + { + struct list_elem *a0; /* Start of first run. */ + struct list_elem *a1b0; /* End of first run, start of second. */ + struct list_elem *b1; /* End of second run. */ + + output_run_cnt = 0; + for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) + { + /* Each iteration produces one output run. */ + output_run_cnt++; + + /* Locate two adjacent runs of nondecreasing elements + A0...A1B0 and A1B0...B1. */ + a1b0 = find_end_of_run (a0, list_end (list), less, aux); + if (a1b0 == list_end (list)) + break; + b1 = find_end_of_run (a1b0, list_end (list), less, aux); + + /* Merge the runs. */ + inplace_merge (a0, a1b0, b1, less, aux); + } + } + while (output_run_cnt > 1); + + assert (is_sorted (list_begin (list), list_end (list), less, aux)); +} + +/* Inserts ELEM in the proper position in LIST, which must be + sorted according to LESS given auxiliary data AUX. + Runs in O(n) average case in the number of elements in LIST. */ +void +list_insert_ordered (struct list *list, struct list_elem *elem, + list_less_func *less, void *aux) +{ + struct list_elem *e; + + assert (list != NULL); + assert (elem != NULL); + assert (less != NULL); + + for (e = list_begin (list); e != list_end (list); e = list_next (e)) + if (less (elem, e, aux)) + break; + return list_insert (e, elem); +} + +/* Iterates through LIST and removes all but the first in each + set of adjacent elements that are equal according to LESS + given auxiliary data AUX. If DUPLICATES is non-null, then the + elements from LIST are appended to DUPLICATES. */ +void +list_unique (struct list *list, struct list *duplicates, + list_less_func *less, void *aux) +{ + struct list_elem *elem, *next; + + assert (list != NULL); + assert (less != NULL); + if (list_empty (list)) + return; + + elem = list_begin (list); + while ((next = list_next (elem)) != list_end (list)) + if (!less (elem, next, aux) && !less (next, elem, aux)) + { + list_remove (next); + if (duplicates != NULL) + list_push_back (duplicates, next); + } + else + elem = next; +} + +/* Returns the element in LIST with the largest value according + to LESS given auxiliary data AUX. If there is more than one + maximum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem * +list_max (struct list *list, list_less_func *less, void *aux) +{ + struct list_elem *max = list_begin (list); + if (max != list_end (list)) + { + struct list_elem *e; + + for (e = list_next (max); e != list_end (list); e = list_next (e)) + if (less (max, e, aux)) + max = e; + } + return max; +} + +/* Returns the element in LIST with the smallest value according + to LESS given auxiliary data AUX. If there is more than one + minimum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem * +list_min (struct list *list, list_less_func *less, void *aux) +{ + struct list_elem *min = list_begin (list); + if (min != list_end (list)) + { + struct list_elem *e; + + for (e = list_next (min); e != list_end (list); e = list_next (e)) + if (less (e, min, aux)) + min = e; + } + return min; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..bded333 --- /dev/null +++ b/list.h @@ -0,0 +1,170 @@ +#ifndef __LIST_H +#define __LIST_H +/* This code is taken from the Pintos education OS. + * For copyright information, see www.pintos-os.org */ + +/* Doubly linked list. + + This implementation of a doubly linked list does not require + use of dynamically allocated memory. Instead, each structure + that is a potential list element must embed a struct list_elem + member. All of the list functions operate on these `struct + list_elem's. The list_entry macro allows conversion from a + struct list_elem back to a structure object that contains it. + + For example, suppose there is a needed for a list of `struct + foo'. `struct foo' should contain a `struct list_elem' + member, like so: + + struct foo + { + struct list_elem elem; + int bar; + ...other members... + }; + + Then a list of `struct foo' can be be declared and initialized + like so: + + struct list foo_list; + + list_init (&foo_list); + + Iteration is a typical situation where it is necessary to + convert from a struct list_elem back to its enclosing + structure. Here's an example using foo_list: + + struct list_elem *e; + + for (e = list_begin (&foo_list); e != list_end (&foo_list); + e = list_next (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } + + You can find real examples of list usage throughout the + source; for example, malloc.c, palloc.c, and thread.c in the + threads directory all use lists. + + The interface for this list is inspired by the list<> template + in the C++ STL. If you're familiar with list<>, you should + find this easy to use. However, it should be emphasized that + these lists do *no* type checking and can't do much other + correctness checking. If you screw up, it will bite you. + + Glossary of list terms: + + - "front": The first element in a list. Undefined in an + empty list. Returned by list_front(). + + - "back": The last element in a list. Undefined in an empty + list. Returned by list_back(). + + - "tail": The element figuratively just after the last + element of a list. Well defined even in an empty list. + Returned by list_end(). Used as the end sentinel for an + iteration from front to back. + + - "beginning": In a non-empty list, the front. In an empty + list, the tail. Returned by list_begin(). Used as the + starting point for an iteration from front to back. + + - "head": The element figuratively just before the first + element of a list. Well defined even in an empty list. + Returned by list_rend(). Used as the end sentinel for an + iteration from back to front. + + - "reverse beginning": In a non-empty list, the back. In an + empty list, the head. Returned by list_rbegin(). Used as + the starting point for an iteration from back to front. + + - "interior element": An element that is not the head or + tail, that is, a real list element. An empty list does + not have any interior elements. +*/ + +#include +#include +#include + +/* List element. */ +struct list_elem + { + struct list_elem *prev; /* Previous list element. */ + struct list_elem *next; /* Next list element. */ + }; + +/* List. */ +struct list + { + struct list_elem head; /* List head. */ + struct list_elem tail; /* List tail. */ + }; + +/* Converts pointer to list element LIST_ELEM into a pointer to + the structure that LIST_ELEM is embedded inside. Supply the + name of the outer structure STRUCT and the member name MEMBER + of the list element. See the big comment at the top of the + file for an example. */ +#define list_entry(LIST_ELEM, STRUCT, MEMBER) \ + ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \ + - offsetof (STRUCT, MEMBER.next))) + +void list_init (struct list *); + +/* List traversal. */ +struct list_elem *list_begin (struct list *); +struct list_elem *list_next (struct list_elem *); +struct list_elem *list_end (struct list *); + +struct list_elem *list_rbegin (struct list *); +struct list_elem *list_prev (struct list_elem *); +struct list_elem *list_rend (struct list *); + +struct list_elem *list_head (struct list *); +struct list_elem *list_tail (struct list *); + +/* List insertion. */ +void list_insert (struct list_elem *, struct list_elem *); +void list_splice (struct list_elem *before, + struct list_elem *first, struct list_elem *last); +void list_push_front (struct list *, struct list_elem *); +void list_push_back (struct list *, struct list_elem *); + +/* List removal. */ +struct list_elem *list_remove (struct list_elem *); +struct list_elem *list_pop_front (struct list *); +struct list_elem *list_pop_back (struct list *); + +/* List elements. */ +struct list_elem *list_front (struct list *); +struct list_elem *list_back (struct list *); + +/* List properties. */ +size_t list_size (struct list *); +bool list_empty (struct list *); + +/* Miscellaneous. */ +void list_reverse (struct list *); + +/* Compares the value of two list elements A and B, given + auxiliary data AUX. Returns true if A is less than B, or + false if A is greater than or equal to B. */ +typedef bool list_less_func (const struct list_elem *a, + const struct list_elem *b, + void *aux); + +/* Operations on lists with ordered elements. */ +void list_sort (struct list *, + list_less_func *, void *aux); +void list_insert_ordered (struct list *, struct list_elem *, + list_less_func *, void *aux); +void list_unique (struct list *, struct list *duplicates, + list_less_func *, void *aux); + +/* Max and min. */ +struct list_elem *list_max (struct list *, list_less_func *, void *aux); +struct list_elem *list_min (struct list *, list_less_func *, void *aux); + +#endif /* list.h */ diff --git a/mdriver.c b/mdriver.c new file mode 100644 index 0000000..effe1f2 --- /dev/null +++ b/mdriver.c @@ -0,0 +1,1075 @@ +/* + * mdriver.c - CS:APP Malloc Lab Driver + * + * Uses a collection of trace files to tests a malloc/free/realloc + * implementation in mm.c. + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mm.h" +#include "memlib.h" +#include "fsecs.h" +#include "config.h" + +/********************** + * Constants and macros + **********************/ + +/* Misc */ +#define MAXLINE 1024 /* max string size */ +#define HDRLINES 4 /* number of header lines in a trace file */ +#define LINENUM(i) (i+5) /* cnvt trace request nums to linenums (origin 1) */ + +/* Returns true if p is ALIGNMENT-byte aligned */ +#define IS_ALIGNED(p) ((((unsigned int)(p)) % ALIGNMENT) == 0) + +/****************************** + * The key compound data types + *****************************/ + +/* Records the extent of each block's payload */ +typedef struct range_t { + char *lo; /* low payload address */ + char *hi; /* high payload address */ + struct range_t *next; /* next list element */ +} range_t; + +/* Characterizes a single trace operation (allocator request) */ +typedef struct { + enum {ALLOC, FREE, REALLOC} type; /* type of request */ + int index; /* index for free() to use later */ + int size; /* byte size of alloc/realloc request */ +} traceop_t; + +/* Holds the information for one trace file*/ +typedef struct { + int sugg_heapsize; /* suggested heap size (unused) */ + int num_ids; /* number of alloc/realloc ids */ + int num_ops; /* number of distinct requests */ + int weight; /* weight for this trace (unused) */ + traceop_t *ops; /* array of requests */ + char **blocks; /* array of ptrs returned by malloc/realloc... */ + size_t *block_sizes; /* ... and a corresponding array of payload sizes */ +} trace_t; + +/* + * Holds the params to the xxx_speed functions, which are timed by fcyc. + * This struct is necessary because fcyc accepts only a pointer array + * as input. + */ +typedef struct { + trace_t *trace; + range_t *ranges; +} speed_t; + +/* Summarizes the important stats for some malloc function on some trace */ +typedef struct { + /* defined for both libc malloc and student malloc package (mm.c) */ + double ops; /* number of ops (malloc/free/realloc) in the trace */ + int valid; /* was the trace processed correctly by the allocator? */ + double secs; /* number of secs needed to run the trace */ + + /* defined only for the student malloc package */ + double util; /* space utilization for this trace (always 0 for libc) */ + + /* Note: secs and util are only defined if valid is true */ +} stats_t; + +/******************** + * Global variables + *******************/ +int verbose = 0; /* global flag for verbose output */ +static int errors = 0; /* number of errs found when running student malloc */ +char msg[MAXLINE]; /* for whenever we need to compose an error message */ + +/* Directory where default tracefiles are found */ +static char tracedir[MAXLINE] = TRACEDIR; + +/* The filenames of the default tracefiles */ +static char *default_tracefiles[] = { + DEFAULT_TRACEFILES, NULL +}; + + +/********************* + * Function prototypes + *********************/ + +/* these functions manipulate range lists */ +static int add_range(range_t **ranges, char *lo, int size, + int tracenum, int opnum); +static void remove_range(range_t **ranges, char *lo); +static void clear_ranges(range_t **ranges); + +/* These functions read, allocate, and free storage for traces */ +static trace_t *read_trace(char *tracedir, char *filename); +static void free_trace(trace_t *trace); + +/* Routines for evaluating the correctness and speed of libc malloc */ +static int eval_libc_valid(trace_t *trace, int tracenum); +static void eval_libc_speed(void *ptr); + +/* Routines for evaluating correctnes, space utilization, and speed + of the student's malloc package in mm.c */ +static int eval_mm_valid(trace_t *trace, int tracenum, range_t **ranges); +static double eval_mm_util(trace_t *trace, int tracenum, range_t **ranges); +static void eval_mm_speed(void *ptr); + +/* Various helper routines */ +static void printresults(int n, char ** tracefiles, stats_t *stats); +static void printresults_as_json(FILE *json, int n, char ** tracefiles, stats_t *stats); +static void usage(void); +static void unix_error(char *msg); +static void malloc_error(int tracenum, int opnum, char *msg); +static void app_error(char *msg); + +/************** + * Main routine + **************/ +int main(int argc, char **argv) +{ + int i; + char c; + char **tracefiles = NULL; /* null-terminated array of trace file names */ + int num_tracefiles = 0; /* the number of traces in that array */ + trace_t *trace = NULL; /* stores a single trace file in memory */ + range_t *ranges = NULL; /* keeps track of block extents for one trace */ + stats_t *libc_stats = NULL;/* libc stats for each trace */ + stats_t *mm_stats = NULL; /* mm (i.e. student) stats for each trace */ + speed_t speed_params; /* input parameters to the xx_speed routines */ + + int team_check = 1; /* If set, check team structure (reset by -a) */ + int run_libc = 0; /* If set, run libc malloc (set by -l) */ + int autograder = 0; /* If set, emit summary info for autograder (-g) */ + int use_mmap = 0; /* If set, have memlib use mmap() instead malloc() */ + + /* temporaries used to compute the performance index */ + double secs, ops, util, avg_mm_util, avg_mm_throughput = 0, p1, p2, perfindex; + int numcorrect; + + /* + * Read and interpret the command line arguments + */ + while ((c = getopt(argc, argv, "nf:t:hvVgal")) != EOF) { + switch (c) { + case 'n': + use_mmap = 1; + break; + case 'g': /* Generate summary info for the autograder */ + autograder = 1; + break; + case 'f': /* Use one specific trace file only (relative to curr dir) */ + num_tracefiles = 1; + if ((tracefiles = realloc(tracefiles, 2*sizeof(char *))) == NULL) + unix_error("ERROR: realloc failed in main"); + strcpy(tracedir, "./"); + tracefiles[0] = strdup(optarg); + tracefiles[1] = NULL; + break; + case 't': /* Directory where the traces are located */ + if (num_tracefiles == 1) /* ignore if -f already encountered */ + break; + strcpy(tracedir, optarg); + if (tracedir[strlen(tracedir)-1] != '/') + strcat(tracedir, "/"); /* path always ends with "/" */ + break; + case 'a': /* Don't check team structure */ + team_check = 0; + break; + case 'l': /* Run libc malloc */ + run_libc = 1; + break; + case 'v': /* Print per-trace performance breakdown */ + verbose = 1; + break; + case 'V': /* Be more verbose than -v */ + verbose = 2; + break; + case 'h': /* Print this message */ + usage(); + exit(0); + default: + usage(); + exit(1); + } + } + + /* + * Check and print team info + */ + if (team_check) { + /* Students must fill in their team information */ + if (!strcmp(team.teamname, "")) { + printf("ERROR: Please provide the information about your team in mm.c.\n"); + exit(1); + } else + printf("Team Name:%s\n", team.teamname); + if ((*team.name1 == '\0') || (*team.id1 == '\0')) { + printf("ERROR. You must fill in all team member 1 fields!\n"); + exit(1); + } + else + printf("Member 1 :%s:%s\n", team.name1, team.id1); + + if (((*team.name2 != '\0') && (*team.id2 == '\0')) || + ((*team.name2 == '\0') && (*team.id2 != '\0'))) { + printf("ERROR. You must fill in all or none of the team member 2 ID fields!\n"); + exit(1); + } + else if (*team.name2 != '\0') + printf("Member 2 :%s:%s\n", team.name2, team.id2); + } + + /* + * If no -f command line arg, then use the entire set of tracefiles + * defined in default_traces[] + */ + if (tracefiles == NULL) { + tracefiles = default_tracefiles; + num_tracefiles = sizeof(default_tracefiles) / sizeof(char *) - 1; + printf("Using default tracefiles in %s\n", tracedir); + } + + /* Initialize the timing package */ + init_fsecs(); + + /* + * Optionally run and evaluate the libc malloc package + */ + if (run_libc) { + if (verbose > 1) + printf("\nTesting libc malloc\n"); + + /* Allocate libc stats array, with one stats_t struct per tracefile */ + libc_stats = (stats_t *)calloc(num_tracefiles, sizeof(stats_t)); + if (libc_stats == NULL) + unix_error("libc_stats calloc in main failed"); + + /* Evaluate the libc malloc package using the K-best scheme */ + for (i=0; i < num_tracefiles; i++) { + trace = read_trace(tracedir, tracefiles[i]); + libc_stats[i].ops = trace->num_ops; + if (verbose > 1) + printf("Checking libc malloc for correctness, "); + libc_stats[i].valid = eval_libc_valid(trace, i); + if (libc_stats[i].valid) { + speed_params.trace = trace; + if (verbose > 1) + printf("and performance.\n"); + libc_stats[i].secs = fsecs(eval_libc_speed, &speed_params); + } + free_trace(trace); + } + + /* Display the libc results in a compact table */ + if (verbose) { + printf("\nResults for libc malloc:\n"); + printresults(num_tracefiles, tracefiles, libc_stats); + } + } + + /* + * Always run and evaluate the student's mm package + */ + if (verbose > 1) + printf("\nTesting mm malloc\n"); + + /* Allocate the mm stats array, with one stats_t struct per tracefile */ + mm_stats = (stats_t *)calloc(num_tracefiles, sizeof(stats_t)); + if (mm_stats == NULL) + unix_error("mm_stats calloc in main failed"); + + /* Initialize the simulated memory system in memlib.c */ + mem_init(use_mmap); + + /* Evaluate student's mm malloc package using the K-best scheme */ + for (i=0; i < num_tracefiles; i++) { + trace = read_trace(tracedir, tracefiles[i]); + mm_stats[i].ops = trace->num_ops; + if (verbose > 1) + printf("Checking mm_malloc for correctness, "); + mm_stats[i].valid = eval_mm_valid(trace, i, &ranges); + if (mm_stats[i].valid) { + if (verbose > 1) + printf("efficiency, "); + mm_stats[i].util = eval_mm_util(trace, i, &ranges); + speed_params.trace = trace; + speed_params.ranges = ranges; + if (verbose > 1) + printf("and performance.\n"); + mm_stats[i].secs = fsecs(eval_mm_speed, &speed_params); + } + free_trace(trace); + } + + /* Display the mm results in a compact table */ + if (verbose) { + printf("\nResults for mm malloc:\n"); + printresults(num_tracefiles, tracefiles, mm_stats); + printf("\n"); + } + + /* + * Accumulate the aggregate statistics for the student's mm package + */ + secs = 0; + ops = 0; + util = 0; + numcorrect = 0; + for (i=0; i < num_tracefiles; i++) { + secs += mm_stats[i].secs; + ops += mm_stats[i].ops; + util += mm_stats[i].util; + if (mm_stats[i].valid) + numcorrect++; + } + avg_mm_util = util/num_tracefiles; + + /* + * Compute and print the performance index + */ + if (errors == 0) { + avg_mm_throughput = ops/secs; + + p1 = UTIL_WEIGHT * avg_mm_util; + if (avg_mm_throughput > AVG_LIBC_THRUPUT) { + p2 = (double)(1.0 - UTIL_WEIGHT); + } + else { + p2 = ((double) (1.0 - UTIL_WEIGHT)) * + (avg_mm_throughput/AVG_LIBC_THRUPUT); + } + + perfindex = (p1 + p2)*100.0; + printf("Perf index = %.0f (util) + %.0f (thru) = %.0f/100\n", + p1*100, + p2*100, + perfindex); + + } + else { /* There were errors */ + perfindex = 0.0; + printf("Terminated with %d errors\n", errors); + } + + if (autograder) { + printf("correct:%d\n", numcorrect); + printf("perfidx:%.0f\n", perfindex); + } + + /* Write results to JSON file for submission */ + char jsonfilename[80]; + snprintf(jsonfilename, sizeof jsonfilename, "results.%d.json", getpid()); + printf("Writing results to %s for submission to the scoreboard\n", jsonfilename); + FILE *json = fopen(jsonfilename, "w"); + fprintf(json, "{"); + fprintf(json, " \"results\": "); + printresults_as_json(json, num_tracefiles, tracefiles, mm_stats); + if (errors == 0) { + fprintf(json, ", \"perfindex\" : " + "{ \"avg_mm_util\": %f, \"avg_mm_throughput\" : %f, \"perfindex\": %f, \"AVG_LIBC_THRUPUT\": %f }\n", + avg_mm_util, avg_mm_throughput, perfindex, AVG_LIBC_THRUPUT); + } + fprintf(json, "}"); + fclose(json); + + exit(0); +} + + +/***************************************************************** + * The following routines manipulate the range list, which keeps + * track of the extent of every allocated block payload. We use the + * range list to detect any overlapping allocated blocks. + ****************************************************************/ + +/* + * add_range - As directed by request opnum in trace tracenum, + * we've just called the student's mm_malloc to allocate a block of + * size bytes at addr lo. After checking the block for correctness, + * we create a range struct for this block and add it to the range list. + */ +static int add_range(range_t **ranges, char *lo, int size, + int tracenum, int opnum) +{ + char *hi = lo + size - 1; + range_t *p; + char msg[MAXLINE]; + + assert(size > 0); + + /* Payload addresses must be ALIGNMENT-byte aligned */ + if (!IS_ALIGNED(lo)) { + sprintf(msg, "Payload address (%p) not aligned to %d bytes", + lo, ALIGNMENT); + malloc_error(tracenum, opnum, msg); + return 0; + } + + /* The payload must lie within the extent of the heap */ + if ((lo < (char *)mem_heap_lo()) || (lo > (char *)mem_heap_hi()) || + (hi < (char *)mem_heap_lo()) || (hi > (char *)mem_heap_hi())) { + sprintf(msg, "Payload (%p:%p) lies outside heap (%p:%p)", + lo, hi, mem_heap_lo(), mem_heap_hi()); + malloc_error(tracenum, opnum, msg); + return 0; + } + + /* The payload must not overlap any other payloads */ + for (p = *ranges; p != NULL; p = p->next) { + if ((lo >= p->lo && lo <= p-> hi) || + (hi >= p->lo && hi <= p->hi)) { + sprintf(msg, "Payload (%p:%p) overlaps another payload (%p:%p)\n", + lo, hi, p->lo, p->hi); + malloc_error(tracenum, opnum, msg); + return 0; + } + } + + /* + * Everything looks OK, so remember the extent of this block + * by creating a range struct and adding it the range list. + */ + if ((p = (range_t *)malloc(sizeof(range_t))) == NULL) + unix_error("malloc error in add_range"); + p->next = *ranges; + p->lo = lo; + p->hi = hi; + *ranges = p; + return 1; +} + +/* + * remove_range - Free the range record of block whose payload starts at lo + */ +static void remove_range(range_t **ranges, char *lo) +{ + range_t *p; + range_t **prevpp = ranges; + int size; + + for (p = *ranges; p != NULL; p = p->next) { + if (p->lo == lo) { + *prevpp = p->next; + size = p->hi - p->lo + 1; + free(p); + break; + } + prevpp = &(p->next); + } +} + +/* + * clear_ranges - free all of the range records for a trace + */ +static void clear_ranges(range_t **ranges) +{ + range_t *p; + range_t *pnext; + + for (p = *ranges; p != NULL; p = pnext) { + pnext = p->next; + free(p); + } + *ranges = NULL; +} + + +/********************************************** + * The following routines manipulate tracefiles + *********************************************/ + +/* + * read_trace - read a trace file and store it in memory + */ +static trace_t *read_trace(char *tracedir, char *filename) +{ + FILE *tracefile; + trace_t *trace; + char type[MAXLINE]; + char path[MAXLINE]; + unsigned index, size; + unsigned max_index = 0; + unsigned op_index; + + if (verbose > 1) + printf("Reading tracefile: %s\n", filename); + + /* Allocate the trace record */ + if ((trace = (trace_t *) malloc(sizeof(trace_t))) == NULL) + unix_error("malloc 1 failed in read_trance"); + + /* Read the trace file header */ + strcpy(path, tracedir); + strcat(path, filename); + if ((tracefile = fopen(path, "r")) == NULL) { + sprintf(msg, "Could not open %s in read_trace", path); + unix_error(msg); + } + fscanf(tracefile, "%d", &(trace->sugg_heapsize)); /* not used */ + fscanf(tracefile, "%d", &(trace->num_ids)); + fscanf(tracefile, "%d", &(trace->num_ops)); + fscanf(tracefile, "%d", &(trace->weight)); /* not used */ + + /* We'll store each request line in the trace in this array */ + if ((trace->ops = + (traceop_t *)malloc(trace->num_ops * sizeof(traceop_t))) == NULL) + unix_error("malloc 2 failed in read_trace"); + + /* We'll keep an array of pointers to the allocated blocks here... */ + if ((trace->blocks = + (char **)malloc(trace->num_ids * sizeof(char *))) == NULL) + unix_error("malloc 3 failed in read_trace"); + + /* ... along with the corresponding byte sizes of each block */ + if ((trace->block_sizes = + (size_t *)malloc(trace->num_ids * sizeof(size_t))) == NULL) + unix_error("malloc 4 failed in read_trace"); + + /* read every request line in the trace file */ + index = 0; + op_index = 0; + while (fscanf(tracefile, "%s", type) != EOF) { + switch(type[0]) { + case 'a': + fscanf(tracefile, "%u %u", &index, &size); + trace->ops[op_index].type = ALLOC; + trace->ops[op_index].index = index; + trace->ops[op_index].size = size; + max_index = (index > max_index) ? index : max_index; + break; + case 'r': + fscanf(tracefile, "%u %u", &index, &size); + trace->ops[op_index].type = REALLOC; + trace->ops[op_index].index = index; + trace->ops[op_index].size = size; + max_index = (index > max_index) ? index : max_index; + break; + case 'f': + fscanf(tracefile, "%ud", &index); + trace->ops[op_index].type = FREE; + trace->ops[op_index].index = index; + break; + default: + printf("Bogus type character (%c) in tracefile %s\n", + type[0], path); + exit(1); + } + op_index++; + + } + fclose(tracefile); + assert(max_index == trace->num_ids - 1); + assert(trace->num_ops == op_index); + + return trace; +} + +/* + * free_trace - Free the trace record and the three arrays it points + * to, all of which were allocated in read_trace(). + */ +void free_trace(trace_t *trace) +{ + free(trace->ops); /* free the three arrays... */ + free(trace->blocks); + free(trace->block_sizes); + free(trace); /* and the trace record itself... */ +} + +/********************************************************************** + * The following functions evaluate the correctness, space utilization, + * and throughput of the libc and mm malloc packages. + **********************************************************************/ + +/* + * eval_mm_valid - Check the mm malloc package for correctness + */ +static int eval_mm_valid(trace_t *trace, int tracenum, range_t **ranges) +{ + int i, j; + int index; + int size; + int oldsize; + char *newp; + char *oldp; + char *p; + + /* Reset the heap and free any records in the range list */ + mem_reset_brk(); + clear_ranges(ranges); + + /* Call the mm package's init function */ + if (mm_init() < 0) { + malloc_error(tracenum, 0, "mm_init failed."); + return 0; + } + + /* Interpret each operation in the trace in order */ + for (i = 0; i < trace->num_ops; i++) { + index = trace->ops[i].index; + size = trace->ops[i].size; + + switch (trace->ops[i].type) { + + case ALLOC: /* mm_malloc */ + + /* Call the student's malloc */ + if ((p = mm_malloc(size)) == NULL) { + malloc_error(tracenum, i, "mm_malloc failed."); + return 0; + } + + /* + * Test the range of the new block for correctness and add it + * to the range list if OK. The block must be be aligned properly, + * and must not overlap any currently allocated block. + */ + if (add_range(ranges, p, size, tracenum, i) == 0) + return 0; + + /* ADDED: cgw + * fill range with low byte of index. This will be used later + * if we realloc the block and wish to make sure that the old + * data was copied to the new block + */ + memset(p, index & 0xFF, size); + + /* Remember region */ + trace->blocks[index] = p; + trace->block_sizes[index] = size; + break; + + case REALLOC: /* mm_realloc */ + + /* Call the student's realloc */ + oldp = trace->blocks[index]; + if ((newp = mm_realloc(oldp, size)) == NULL) { + malloc_error(tracenum, i, "mm_realloc failed."); + return 0; + } + + /* Remove the old region from the range list */ + remove_range(ranges, oldp); + + /* Check new block for correctness and add it to range list */ + if (add_range(ranges, newp, size, tracenum, i) == 0) + return 0; + + /* ADDED: cgw + * Make sure that the new block contains the data from the old + * block and then fill in the new block with the low order byte + * of the new index + */ + oldsize = trace->block_sizes[index]; + if (size < oldsize) oldsize = size; + for (j = 0; j < oldsize; j++) { + if (newp[j] != (index & 0xFF)) { + malloc_error(tracenum, i, "mm_realloc did not preserve the " + "data from old block"); + return 0; + } + } + memset(newp, index & 0xFF, size); + + /* Remember region */ + trace->blocks[index] = newp; + trace->block_sizes[index] = size; + break; + + case FREE: /* mm_free */ + + /* Remove region from list and call student's free function */ + p = trace->blocks[index]; + remove_range(ranges, p); + mm_free(p); + break; + + default: + app_error("Nonexistent request type in eval_mm_valid"); + } + + } + + /* As far as we know, this is a valid malloc package */ + return 1; +} + +/* + * eval_mm_util - Evaluate the space utilization of the student's package + * The idea is to remember the high water mark "hwm" of the heap for + * an optimal allocator, i.e., no gaps and no internal fragmentation. + * Utilization is the ratio hwm/heapsize, where heapsize is the + * size of the heap in bytes after running the student's malloc + * package on the trace. Note that our implementation of mem_sbrk() + * doesn't allow the students to decrement the brk pointer, so brk + * is always the high water mark of the heap. + * + */ +static double eval_mm_util(trace_t *trace, int tracenum, range_t **ranges) +{ + int i; + int index; + int size, newsize, oldsize; + int max_total_size = 0; + int total_size = 0; + char *p; + char *newp, *oldp; + + /* initialize the heap and the mm malloc package */ + mem_reset_brk(); + if (mm_init() < 0) + app_error("mm_init failed in eval_mm_util"); + + for (i = 0; i < trace->num_ops; i++) { + switch (trace->ops[i].type) { + + case ALLOC: /* mm_alloc */ + index = trace->ops[i].index; + size = trace->ops[i].size; + + if ((p = mm_malloc(size)) == NULL) + app_error("mm_malloc failed in eval_mm_util"); + + /* Remember region and size */ + trace->blocks[index] = p; + trace->block_sizes[index] = size; + + /* Keep track of current total size + * of all allocated blocks */ + total_size += size; + + /* Update statistics */ + max_total_size = (total_size > max_total_size) ? + total_size : max_total_size; + break; + + case REALLOC: /* mm_realloc */ + index = trace->ops[i].index; + newsize = trace->ops[i].size; + oldsize = trace->block_sizes[index]; + + oldp = trace->blocks[index]; + if ((newp = mm_realloc(oldp,newsize)) == NULL) + app_error("mm_realloc failed in eval_mm_util"); + + /* Remember region and size */ + trace->blocks[index] = newp; + trace->block_sizes[index] = newsize; + + /* Keep track of current total size + * of all allocated blocks */ + total_size += (newsize - oldsize); + + /* Update statistics */ + max_total_size = (total_size > max_total_size) ? + total_size : max_total_size; + break; + + case FREE: /* mm_free */ + index = trace->ops[i].index; + size = trace->block_sizes[index]; + p = trace->blocks[index]; + + mm_free(p); + + /* Keep track of current total size + * of all allocated blocks */ + total_size -= size; + + break; + + default: + app_error("Nonexistent request type in eval_mm_util"); + + } + } + + return ((double)max_total_size / (double)mem_heapsize()); +} + + +/* + * eval_mm_speed - This is the function that is used by fcyc() + * to measure the running time of the mm malloc package. + */ +static void eval_mm_speed(void *ptr) +{ + int i, index, size, newsize; + char *p, *newp, *oldp, *block; + trace_t *trace = ((speed_t *)ptr)->trace; + + /* Reset the heap and initialize the mm package */ + mem_reset_brk(); + if (mm_init() < 0) + app_error("mm_init failed in eval_mm_speed"); + + /* Interpret each trace request */ + for (i = 0; i < trace->num_ops; i++) + switch (trace->ops[i].type) { + + case ALLOC: /* mm_malloc */ + index = trace->ops[i].index; + size = trace->ops[i].size; + if ((p = mm_malloc(size)) == NULL) + app_error("mm_malloc error in eval_mm_speed"); + trace->blocks[index] = p; + break; + + case REALLOC: /* mm_realloc */ + index = trace->ops[i].index; + newsize = trace->ops[i].size; + oldp = trace->blocks[index]; + if ((newp = mm_realloc(oldp,newsize)) == NULL) + app_error("mm_realloc error in eval_mm_speed"); + trace->blocks[index] = newp; + break; + + case FREE: /* mm_free */ + index = trace->ops[i].index; + block = trace->blocks[index]; + mm_free(block); + break; + + default: + app_error("Nonexistent request type in eval_mm_valid"); + } +} + +/* + * eval_libc_valid - We run this function to make sure that the + * libc malloc can run to completion on the set of traces. + * We'll be conservative and terminate if any libc malloc call fails. + * + */ +static int eval_libc_valid(trace_t *trace, int tracenum) +{ + int i, newsize; + char *p, *newp, *oldp; + + for (i = 0; i < trace->num_ops; i++) { + switch (trace->ops[i].type) { + + case ALLOC: /* malloc */ + if ((p = malloc(trace->ops[i].size)) == NULL) { + malloc_error(tracenum, i, "libc malloc failed"); + unix_error("System message"); + } + trace->blocks[trace->ops[i].index] = p; + break; + + case REALLOC: /* realloc */ + newsize = trace->ops[i].size; + oldp = trace->blocks[trace->ops[i].index]; + if ((newp = realloc(oldp, newsize)) == NULL) { + malloc_error(tracenum, i, "libc realloc failed"); + unix_error("System message"); + } + trace->blocks[trace->ops[i].index] = newp; + break; + + case FREE: /* free */ + free(trace->blocks[trace->ops[i].index]); + break; + + default: + app_error("invalid operation type in eval_libc_valid"); + } + } + + return 1; +} + +/* + * eval_libc_speed - This is the function that is used by fcyc() to + * measure the running time of the libc malloc package on the set + * of traces. + */ +static void eval_libc_speed(void *ptr) +{ + int i; + int index, size, newsize; + char *p, *newp, *oldp, *block; + trace_t *trace = ((speed_t *)ptr)->trace; + + for (i = 0; i < trace->num_ops; i++) { + switch (trace->ops[i].type) { + case ALLOC: /* malloc */ + index = trace->ops[i].index; + size = trace->ops[i].size; + if ((p = malloc(size)) == NULL) + unix_error("malloc failed in eval_libc_speed"); + trace->blocks[index] = p; + break; + + case REALLOC: /* realloc */ + index = trace->ops[i].index; + newsize = trace->ops[i].size; + oldp = trace->blocks[index]; + if ((newp = realloc(oldp, newsize)) == NULL) + unix_error("realloc failed in eval_libc_speed\n"); + + trace->blocks[index] = newp; + break; + + case FREE: /* free */ + index = trace->ops[i].index; + block = trace->blocks[index]; + free(block); + break; + } + } +} + +/************************************* + * Some miscellaneous helper routines + ************************************/ + + +/* + * printresults - prints a performance summary for some malloc package + */ +static void printresults(int n, char ** tracefiles, stats_t *stats) +{ + int i; + double secs = 0; + double ops = 0; + double util = 0; + + /* Print the individual results for each trace */ + printf("%5s%22s%5s%5s%8s%10s%6s\n", + "trace", " name", " valid", "util", "ops", "secs", "Kops"); + for (i=0; i < n; i++) { + if (stats[i].valid) { + printf("%2d%25s%5s%5.0f%%%8.0f%10.6f%6.0f\n", + i, + tracefiles[i], + "yes", + stats[i].util*100.0, + stats[i].ops, + stats[i].secs, + (stats[i].ops/1e3)/stats[i].secs); + secs += stats[i].secs; + ops += stats[i].ops; + util += stats[i].util; + } + else { + printf("%2d%25s%5s%6s%8s%10s%6s\n", + i, + tracefiles[i], + "no", + "-", + "-", + "-", + "-"); + } + } + + /* Print the aggregate results for the set of traces */ + if (errors == 0) { + printf("%12s %5.0f%%%8.0f%10.6f%6.0f\n", + "Total ", + (util/n)*100.0, + ops, + secs, + (ops/1e3)/secs); + } + else { + printf("%12s %6s%8s%10s%6s\n", + "Total ", + "-", + "-", + "-", + "-"); + } + +} + +static void printresults_as_json(FILE *json, int n, char **tracefiles, stats_t *stats) +{ + int i; + double secs = 0; + double ops = 0; + double util = 0; + + /* Print the individual results for each trace */ + fprintf(json, "[\n"); + for (i=0; i < n; i++) { + if (stats[i].valid) { + fprintf(json, "{ \"%s\": \"%s\"\n", "trace", tracefiles[i]); + fprintf(json, ", \"valid\": true\n"); + fprintf(json, ", \"%s\": %f\n", "util", stats[i].util*100.0); + fprintf(json, ", \"%s\": %f\n", "ops", stats[i].ops); + fprintf(json, ", \"%s\": %f\n", "secs", stats[i].secs); + fprintf(json, ", \"%s\": %f\n", "Kops", (stats[i].ops/1e3)/stats[i].secs); + fprintf(json, "}"); + + secs += stats[i].secs; + ops += stats[i].ops; + util += stats[i].util; + } + else { + fprintf(json, "{ \"%s\": \"%s\"\n", "trace", tracefiles[i]); + fprintf(json, ", \"valid\": false\n"); + fprintf(json, "}"); + } + if (i < n-1) + fprintf(json, ",\n"); + } + fprintf(json, "]\n"); +} + +/* + * app_error - Report an arbitrary application error + */ +void app_error(char *msg) +{ + printf("%s\n", msg); + exit(1); +} + +/* + * unix_error - Report a Unix-style error + */ +void unix_error(char *msg) +{ + printf("%s: %s\n", msg, strerror(errno)); + exit(1); +} + +/* + * malloc_error - Report an error returned by the mm_malloc package + */ +void malloc_error(int tracenum, int opnum, char *msg) +{ + errors++; + printf("ERROR [trace %d, line %d]: %s\n", tracenum, LINENUM(opnum), msg); +} + +/* + * usage - Explain the command line arguments + */ +static void usage(void) +{ + fprintf(stderr, "Usage: mdriver [-hvVal] [-f ] [-t ]\n"); + fprintf(stderr, "Options\n"); + fprintf(stderr, "\t-a Don't check the team structure.\n"); + fprintf(stderr, "\t-f Use as the trace file.\n"); + fprintf(stderr, "\t-g Generate summary info for autograder.\n"); + fprintf(stderr, "\t-h Print this message.\n"); + fprintf(stderr, "\t-l Run libc malloc as well.\n"); + fprintf(stderr, "\t-t Directory to find default traces.\n"); + fprintf(stderr, "\t-v Print per-trace performance breakdowns.\n"); + fprintf(stderr, "\t-V Print additional debug info.\n"); + fprintf(stderr, "\t-n Don't randomize addresses.\n"); +} diff --git a/memlib.c b/memlib.c new file mode 100644 index 0000000..28f75ea --- /dev/null +++ b/memlib.c @@ -0,0 +1,126 @@ +/* + * memlib.c - a module that simulates the memory system. Needed because it + * allows us to interleave calls from the student's malloc package + * with the system's malloc package in libc. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "memlib.h" +#include "config.h" + +/* private variables */ +static char *mem_start_brk; /* points to first byte of heap */ +static char *mem_brk; /* points to last byte of heap */ +static char *mem_max_addr; /* largest legal heap address */ +static int use_mmap; /* Use mmap instead of malloc */ +static void * mmap_addr = (void *)0x58000000; + +/* + * mem_init - initialize the memory system model + */ +void mem_init(int _use_mmap) +{ + use_mmap = _use_mmap; + + /* allocate the storage we will use to model the available VM */ + if (use_mmap) { + mem_start_brk = (char *)mmap(mmap_addr, MAX_HEAP, PROT_READ|PROT_WRITE, + MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + if (mem_start_brk == MAP_FAILED) { + perror("mem_init_vm: mmap error:"); + exit(1); + } + if (mem_start_brk != mmap_addr) { + perror("mmap"); + fprintf(stderr, + "mem_init_vm: could not obtain memory at address %p\n", + mmap_addr); + exit(1); + } + } else { + if ((mem_start_brk = (char *)malloc(MAX_HEAP)) == NULL) { + fprintf(stderr, "mem_init_vm: malloc error\n"); + exit(1); + } + } + + mem_max_addr = mem_start_brk + MAX_HEAP; /* max legal heap address */ + mem_brk = mem_start_brk; /* heap is empty initially */ +} + +/* + * mem_deinit - free the storage used by the memory system model + */ +void mem_deinit(void) +{ + if (use_mmap) { + if (munmap(mem_start_brk, MAX_HEAP)) + perror("munmap"); + } else { + free(mem_start_brk); + } +} + +/* + * mem_reset_brk - reset the simulated brk pointer to make an empty heap + */ +void mem_reset_brk() +{ + mem_brk = mem_start_brk; +} + +/* + * mem_sbrk - simple model of the sbrk function. Extends the heap + * by incr bytes and returns the start address of the new area. In + * this model, the heap cannot be shrunk. + */ +void *mem_sbrk(int incr) +{ + char *old_brk = mem_brk; + + if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) { + errno = ENOMEM; + fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n"); + return NULL; + } + mem_brk += incr; + return (void *)old_brk; +} + +/* + * mem_heap_lo - return address of the first heap byte + */ +void *mem_heap_lo() +{ + return (void *)mem_start_brk; +} + +/* + * mem_heap_hi - return address of last heap byte + */ +void *mem_heap_hi() +{ + return (void *)(mem_brk - 1); +} + +/* + * mem_heapsize() - returns the heap size in bytes + */ +size_t mem_heapsize() +{ + return (size_t)(mem_brk - mem_start_brk); +} + +/* + * mem_pagesize() - returns the page size of the system + */ +size_t mem_pagesize() +{ + return (size_t)getpagesize(); +} diff --git a/memlib.h b/memlib.h new file mode 100644 index 0000000..4b6acd8 --- /dev/null +++ b/memlib.h @@ -0,0 +1,11 @@ +#include + +void mem_init(int use_mmap); +void mem_deinit(void); +void *mem_sbrk(int incr); +void mem_reset_brk(void); +void *mem_heap_lo(void); +void *mem_heap_hi(void); +size_t mem_heapsize(void); +size_t mem_pagesize(void); + diff --git a/mm-book-implicit.c b/mm-book-implicit.c new file mode 100644 index 0000000..5def0e6 --- /dev/null +++ b/mm-book-implicit.c @@ -0,0 +1,400 @@ +/* + * Simple, 32-bit and 64-bit clean allocator based on implicit free + * lists, first fit placement, and boundary tag coalescing, as described + * in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte) + * boundaries. Minimum block size is 16 bytes. + */ +#include +#include +#include + +#include "mm.h" +#include "memlib.h" + +/* + * If NEXT_FIT defined use next fit search, else use first fit search + */ +#define NEXT_FITx + +/* $begin mallocmacros */ +/* Basic constants and macros */ +#define WSIZE 4 /* Word and header/footer size (bytes) */ //line:vm:mm:beginconst +#define DSIZE 8 /* Doubleword size (bytes) */ +#define CHUNKSIZE (1<<12) /* Extend heap by this amount (bytes) */ //line:vm:mm:endconst + +#define MAX(x, y) ((x) > (y)? (x) : (y)) + +/* Pack a size and allocated bit into a word */ +#define PACK(size, alloc) ((size) | (alloc)) //line:vm:mm:pack + +/* Read and write a word at address p */ +#define GET(p) (*(unsigned int *)(p)) //line:vm:mm:get +#define PUT(p, val) (*(unsigned int *)(p) = (val)) //line:vm:mm:put + +/* Read the size and allocated fields from address p */ +#define GET_SIZE(p) (GET(p) & ~0x7) //line:vm:mm:getsize +#define GET_ALLOC(p) (GET(p) & 0x1) //line:vm:mm:getalloc + +/* Given block ptr bp, compute address of its header and footer */ +#define HDRP(bp) ((char *)(bp) - WSIZE) //line:vm:mm:hdrp +#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) //line:vm:mm:ftrp + +/* Given block ptr bp, compute address of next and previous blocks */ +#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) //line:vm:mm:nextblkp +#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) //line:vm:mm:prevblkp +/* $end mallocmacros */ + +/* Global variables */ +static char *heap_listp = 0; /* Pointer to first block */ +#ifdef NEXT_FIT +static char *rover; /* Next fit rover */ +#endif + +/* Function prototypes for internal helper routines */ +static void *extend_heap(size_t words); +static void place(void *bp, size_t asize); +static void *find_fit(size_t asize); +static void *coalesce(void *bp); +static void printblock(void *bp); +static void checkheap(int verbose); +static void checkblock(void *bp); + +/* + * mm_init - Initialize the memory manager + */ +/* $begin mminit */ +int mm_init(void) +{ + /* Create the initial empty heap */ + if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) //line:vm:mm:begininit + return -1; + PUT(heap_listp, 0); /* Alignment padding */ + PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); /* Prologue header */ + PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); /* Prologue footer */ + PUT(heap_listp + (3*WSIZE), PACK(0, 1)); /* Epilogue header */ + heap_listp += (2*WSIZE); //line:vm:mm:endinit +/* $end mminit */ + +#ifdef NEXT_FIT + rover = heap_listp; +#endif +/* $begin mminit */ + + /* Extend the empty heap with a free block of CHUNKSIZE bytes */ + if (extend_heap(CHUNKSIZE/WSIZE) == NULL) + return -1; + return 0; +} +/* $end mminit */ + +/* + * mm_malloc - Allocate a block with at least size bytes of payload + */ +/* $begin mmmalloc */ +void *mm_malloc(size_t size) +{ + size_t asize; /* Adjusted block size */ + size_t extendsize; /* Amount to extend heap if no fit */ + char *bp; + +/* $end mmmalloc */ + if (heap_listp == 0){ + mm_init(); + } +/* $begin mmmalloc */ + /* Ignore spurious requests */ + if (size == 0) + return NULL; + + /* Adjust block size to include overhead and alignment reqs. */ + if (size <= DSIZE) //line:vm:mm:sizeadjust1 + asize = 2*DSIZE; //line:vm:mm:sizeadjust2 + else + asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); //line:vm:mm:sizeadjust3 + + /* Search the free list for a fit */ + if ((bp = find_fit(asize)) != NULL) { //line:vm:mm:findfitcall + place(bp, asize); //line:vm:mm:findfitplace + return bp; + } + + /* No fit found. Get more memory and place the block */ + extendsize = MAX(asize,CHUNKSIZE); //line:vm:mm:growheap1 + if ((bp = extend_heap(extendsize/WSIZE)) == NULL) + return NULL; //line:vm:mm:growheap2 + place(bp, asize); //line:vm:mm:growheap3 + return bp; +} +/* $end mmmalloc */ + +/* + * mm_free - Free a block + */ +/* $begin mmfree */ +void mm_free(void *bp) +{ +/* $end mmfree */ + if(bp == 0) + return; + +/* $begin mmfree */ + size_t size = GET_SIZE(HDRP(bp)); +/* $end mmfree */ + if (heap_listp == 0){ + mm_init(); + } +/* $begin mmfree */ + + PUT(HDRP(bp), PACK(size, 0)); + PUT(FTRP(bp), PACK(size, 0)); + coalesce(bp); +} + +/* $end mmfree */ +/* + * coalesce - Boundary tag coalescing. Return ptr to coalesced block + */ +/* $begin mmfree */ +static void *coalesce(void *bp) +{ + size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); + size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); + size_t size = GET_SIZE(HDRP(bp)); + + if (prev_alloc && next_alloc) { /* Case 1 */ + return bp; + } + + else if (prev_alloc && !next_alloc) { /* Case 2 */ + size += GET_SIZE(HDRP(NEXT_BLKP(bp))); + PUT(HDRP(bp), PACK(size, 0)); + PUT(FTRP(bp), PACK(size,0)); + } + + else if (!prev_alloc && next_alloc) { /* Case 3 */ + size += GET_SIZE(HDRP(PREV_BLKP(bp))); + PUT(FTRP(bp), PACK(size, 0)); + PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); + bp = PREV_BLKP(bp); + } + + else { /* Case 4 */ + size += GET_SIZE(HDRP(PREV_BLKP(bp))) + + GET_SIZE(FTRP(NEXT_BLKP(bp))); + PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); + PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0)); + bp = PREV_BLKP(bp); + } +/* $end mmfree */ +#ifdef NEXT_FIT + /* Make sure the rover isn't pointing into the free block */ + /* that we just coalesced */ + if ((rover > (char *)bp) && (rover < NEXT_BLKP(bp))) + rover = bp; +#endif +/* $begin mmfree */ + return bp; +} +/* $end mmfree */ + +/* + * mm_realloc - Naive implementation of realloc + */ +void *mm_realloc(void *ptr, size_t size) +{ + size_t oldsize; + void *newptr; + + /* If size == 0 then this is just free, and we return NULL. */ + if(size == 0) { + mm_free(ptr); + return 0; + } + + /* If oldptr is NULL, then this is just malloc. */ + if(ptr == NULL) { + return mm_malloc(size); + } + + newptr = mm_malloc(size); + + /* If realloc() fails the original block is left untouched */ + if(!newptr) { + return 0; + } + + /* Copy the old data. */ + oldsize = GET_SIZE(HDRP(ptr)); + if(size < oldsize) oldsize = size; + memcpy(newptr, ptr, oldsize); + + /* Free the old block. */ + mm_free(ptr); + + return newptr; +} + +/* + * checkheap - We don't check anything right now. + */ +void mm_checkheap(int verbose) +{ +} + +/* + * The remaining routines are internal helper routines + */ + +/* + * extend_heap - Extend heap with free block and return its block pointer + */ +/* $begin mmextendheap */ +static void *extend_heap(size_t words) +{ + char *bp; + size_t size; + + /* Allocate an even number of words to maintain alignment */ + size = (words % 2) ? (words+1) * WSIZE : words * WSIZE; //line:vm:mm:beginextend + if ((long)(bp = mem_sbrk(size)) == -1) + return NULL; //line:vm:mm:endextend + + /* Initialize free block header/footer and the epilogue header */ + PUT(HDRP(bp), PACK(size, 0)); /* Free block header */ //line:vm:mm:freeblockhdr + PUT(FTRP(bp), PACK(size, 0)); /* Free block footer */ //line:vm:mm:freeblockftr + PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* New epilogue header */ //line:vm:mm:newepihdr + + /* Coalesce if the previous block was free */ + return coalesce(bp); //line:vm:mm:returnblock +} +/* $end mmextendheap */ + +/* + * place - Place block of asize bytes at start of free block bp + * and split if remainder would be at least minimum block size + */ +/* $begin mmplace */ +/* $begin mmplace-proto */ +static void place(void *bp, size_t asize) + /* $end mmplace-proto */ +{ + size_t csize = GET_SIZE(HDRP(bp)); + + if ((csize - asize) >= (2*DSIZE)) { + PUT(HDRP(bp), PACK(asize, 1)); + PUT(FTRP(bp), PACK(asize, 1)); + bp = NEXT_BLKP(bp); + PUT(HDRP(bp), PACK(csize-asize, 0)); + PUT(FTRP(bp), PACK(csize-asize, 0)); + } + else { + PUT(HDRP(bp), PACK(csize, 1)); + PUT(FTRP(bp), PACK(csize, 1)); + } +} +/* $end mmplace */ + +/* + * find_fit - Find a fit for a block with asize bytes + */ +/* $begin mmfirstfit */ +/* $begin mmfirstfit-proto */ +static void *find_fit(size_t asize) +/* $end mmfirstfit-proto */ +{ +/* $end mmfirstfit */ + +#ifdef NEXT_FIT + /* Next fit search */ + char *oldrover = rover; + + /* Search from the rover to the end of list */ + for ( ; GET_SIZE(HDRP(rover)) > 0; rover = NEXT_BLKP(rover)) + if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover)))) + return rover; + + /* search from start of list to old rover */ + for (rover = heap_listp; rover < oldrover; rover = NEXT_BLKP(rover)) + if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover)))) + return rover; + + return NULL; /* no fit found */ +#else +/* $begin mmfirstfit */ + /* First fit search */ + void *bp; + + for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { + if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp)))) { + return bp; + } + } + return NULL; /* No fit */ +/* $end mmfirstfit */ +#endif +} + +static void printblock(void *bp) +{ + size_t hsize, halloc, fsize, falloc; + + checkheap(0); + hsize = GET_SIZE(HDRP(bp)); + halloc = GET_ALLOC(HDRP(bp)); + fsize = GET_SIZE(FTRP(bp)); + falloc = GET_ALLOC(FTRP(bp)); + + if (hsize == 0) { + printf("%p: EOL\n", bp); + return; + } + + /* printf("%p: header: [%p:%c] footer: [%p:%c]\n", bp, + hsize, (halloc ? 'a' : 'f'), + fsize, (falloc ? 'a' : 'f')); */ +} + +static void checkblock(void *bp) +{ + if ((size_t)bp % 8) + printf("Error: %p is not doubleword aligned\n", bp); + if (GET(HDRP(bp)) != GET(FTRP(bp))) + printf("Error: header does not match footer\n"); +} + +/* + * checkheap - Minimal check of the heap for consistency + */ +void checkheap(int verbose) +{ + char *bp = heap_listp; + + if (verbose) + printf("Heap (%p):\n", heap_listp); + + if ((GET_SIZE(HDRP(heap_listp)) != DSIZE) || !GET_ALLOC(HDRP(heap_listp))) + printf("Bad prologue header\n"); + checkblock(heap_listp); + + for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { + if (verbose) + printblock(bp); + checkblock(bp); + } + + if (verbose) + printblock(bp); + if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp)))) + printf("Bad epilogue header\n"); +} + +team_t team = { + /* Team name */ + "CSApp Authors", + /* First member's full name */ + "Randy Bryant", + "randy@cs.cmu.edu", + /* Second member's full name (leave blank if none) */ + "David O'Hallaron", + "dave@cs.cmu.edu", +}; diff --git a/mm-gback-implicit.c b/mm-gback-implicit.c new file mode 100644 index 0000000..7a31113 --- /dev/null +++ b/mm-gback-implicit.c @@ -0,0 +1,374 @@ +/* + * Simple, 32-bit and 64-bit clean allocator based on implicit free + * lists, first fit placement, and boundary tag coalescing, as described + * in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte) + * boundaries. Minimum block size is 16 bytes. + * + * This version is loosely based on + * http://csapp.cs.cmu.edu/public/ics2/code/vm/malloc/mm.c + * but unlike the book's version, it does not use C preprocessor + * macros or explicit bit operations. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "mm.h" +#include "memlib.h" + +struct boundary_tag { + int inuse:1; // inuse bit + int size:31; // size of block, in words +}; + +/* FENCE is used for heap prologue/epilogue. */ +const struct boundary_tag FENCE = { .inuse = 1, .size = 0 }; + +/* A C struct describing the beginning of each block. + * For implicit lists, used and free blocks have the same + * structure, so one struct will suffice for this example. + * If each block is aligned at 4 mod 8, each payload will + * be aligned at 0 mod 8. + */ +struct block { + struct boundary_tag header; /* offset 0, at address 4 mod 8 */ + char payload[0]; /* offset 4, at address 0 mod 8 */ +}; + +/* + * If NEXT_FIT defined use next fit search, else use first fit search + */ +#define NEXT_FITx + +/* Basic constants and macros */ +#define WSIZE 4 /* Word and header/footer size (bytes) */ +#define DSIZE 8 /* Doubleword size (bytes) */ +#define MIN_BLOCK_SIZE_WORDS 4 /* Minimum block size in words */ +#define CHUNKSIZE (1<<10) /* Extend heap by this amount (words) */ + +#define MAX(x, y) ((x) > (y)? (x) : (y)) + +/* Global variables */ +static struct block *heap_listp = 0; /* Pointer to first block */ +#ifdef NEXT_FIT +static struct block *rover; /* Next fit rover */ +#endif + +/* Function prototypes for internal helper routines */ +static struct block *extend_heap(size_t words); +static void place(struct block *bp, size_t asize); +static struct block *find_fit(size_t asize); +static struct block *coalesce(struct block *bp); + +/* Given a block, obtain previous's block footer. + Works for left-most block also. */ +static struct boundary_tag * prev_blk_footer(struct block *blk) { + return &blk->header - 1; +} + +/* Return if block is free */ +static bool blk_free(struct block *blk) { + return !blk->header.inuse; +} + +/* Return size of block is free */ +static size_t blk_size(struct block *blk) { + return blk->header.size; +} + +/* Given a block, obtain pointer to previous block. + Not meaningful for left-most block. */ +static struct block *prev_blk(struct block *blk) { + struct boundary_tag *prevfooter = prev_blk_footer(blk); + assert(prevfooter->size != 0); + return (struct block *)((size_t *)blk - prevfooter->size); +} + +/* Given a block, obtain pointer to next block. + Not meaningful for right-most block. */ +static struct block *next_blk(struct block *blk) { + assert(blk_size(blk) != 0); + return (struct block *)((size_t *)blk + blk->header.size); +} + +/* Given a block, obtain its footer boundary tag */ +static struct boundary_tag * get_footer(struct block *blk) { + return (void *)((size_t *)blk + blk->header.size) + - sizeof(struct boundary_tag); +} + +/* Set a block's size and inuse bit in header and footer */ +static void set_header_and_footer(struct block *blk, int size, int inuse) { + blk->header.inuse = inuse; + blk->header.size = size; + * get_footer(blk) = blk->header; /* Copy header to footer */ +} + +/* Mark a block as used and set its size. */ +static void mark_block_used(struct block *blk, int size) { + set_header_and_footer(blk, size, 1); +} + +/* Mark a block as free and set its size. */ +static void mark_block_free(struct block *blk, int size) { + set_header_and_footer(blk, size, 0); +} + +/* + * mm_init - Initialize the memory manager + */ +int mm_init(void) +{ + /* Create the initial empty heap */ + struct boundary_tag * initial = mem_sbrk(2 * sizeof(struct boundary_tag)); + if (initial == (void *)-1) + return -1; + + /* We use a slightly different strategy than suggested in the book. + * Rather than placing a min-sized prologue block at the beginning + * of the heap, we simply place two fences. + * The consequence is that coalesce() must call prev_blk_footer() + * and not prev_blk() - prev_blk() cannot be called on the left-most + * block. + */ + initial[0] = FENCE; /* Prologue footer */ + heap_listp = (struct block *)&initial[1]; + initial[1] = FENCE; /* Epilogue header */ + +#ifdef NEXT_FIT + rover = heap_listp; +#endif + + /* Extend the empty heap with a free block of CHUNKSIZE bytes */ + if (extend_heap(CHUNKSIZE) == NULL) + return -1; + return 0; +} + +/* + * mm_malloc - Allocate a block with at least size bytes of payload + */ +void *mm_malloc(size_t size) +{ + size_t awords; /* Adjusted block size in words */ + size_t extendwords; /* Amount to extend heap if no fit */ + struct block *bp; + + if (heap_listp == 0){ + mm_init(); + } + /* Ignore spurious requests */ + if (size == 0) + return NULL; + + /* Adjust block size to include overhead and alignment reqs. */ + size += 2 * sizeof(struct boundary_tag); /* account for tags */ + size = (size + DSIZE - 1) & ~(DSIZE - 1); /* align to double word */ + awords = MAX(MIN_BLOCK_SIZE_WORDS, size/WSIZE); + /* respect minimum size */ + + /* Search the free list for a fit */ + if ((bp = find_fit(awords)) != NULL) { + place(bp, awords); + return bp->payload; + } + + /* No fit found. Get more memory and place the block */ + extendwords = MAX(awords,CHUNKSIZE); + if ((bp = extend_heap(extendwords)) == NULL) + return NULL; + place(bp, awords); + return bp->payload; +} + +/* + * mm_free - Free a block + */ +void mm_free(void *bp) +{ + if (bp == 0) + return; + + /* Find block from user pointer */ + struct block *blk = bp - offsetof(struct block, payload); + if (heap_listp == 0) { + mm_init(); + } + + mark_block_free(blk, blk_size(blk)); + coalesce(blk); +} + +/* + * coalesce - Boundary tag coalescing. Return ptr to coalesced block + */ +static struct block *coalesce(struct block *bp) +{ + bool prev_alloc = prev_blk_footer(bp)->inuse; + bool next_alloc = ! blk_free(next_blk(bp)); + size_t size = blk_size(bp); + + if (prev_alloc && next_alloc) { /* Case 1 */ + return bp; + } + + else if (prev_alloc && !next_alloc) { /* Case 2 */ + mark_block_free(bp, size + blk_size(next_blk(bp))); + } + + else if (!prev_alloc && next_alloc) { /* Case 3 */ + bp = prev_blk(bp); + mark_block_free(bp, size + blk_size(bp)); + } + + else { /* Case 4 */ + mark_block_free(prev_blk(bp), + size + blk_size(next_blk(bp)) + blk_size(prev_blk(bp))); + bp = prev_blk(bp); + } +#ifdef NEXT_FIT + /* Make sure the rover isn't pointing into the free block */ + /* that we just coalesced */ + if ((rover > bp) && (rover < next_blk(bp))) + rover = bp; +#endif + return bp; +} + +/* + * mm_realloc - Naive implementation of realloc + */ +void *mm_realloc(void *ptr, size_t size) +{ + size_t oldsize; + void *newptr; + + /* If size == 0 then this is just free, and we return NULL. */ + if(size == 0) { + mm_free(ptr); + return 0; + } + + /* If oldptr is NULL, then this is just malloc. */ + if(ptr == NULL) { + return mm_malloc(size); + } + + newptr = mm_malloc(size); + + /* If realloc() fails the original block is left untouched */ + if(!newptr) { + return 0; + } + + /* Copy the old data. */ + struct block *oldblock = ptr - offsetof(struct block, payload); + oldsize = blk_size(oldblock) * WSIZE; + if(size < oldsize) oldsize = size; + memcpy(newptr, ptr, oldsize); + + /* Free the old block. */ + mm_free(ptr); + + return newptr; +} + +/* + * checkheap - We don't check anything right now. + */ +void mm_checkheap(int verbose) +{ +} + +/* + * The remaining routines are internal helper routines + */ + +/* + * extend_heap - Extend heap with free block and return its block pointer + */ +static struct block *extend_heap(size_t words) +{ + void *bp; + + /* Allocate an even number of words to maintain alignment */ + words = (words + 1) & ~1; + if ((long)(bp = mem_sbrk(words * WSIZE)) == -1) + return NULL; + + /* Initialize free block header/footer and the epilogue header. + * Note that we scoop up the previous epilogue here. */ + struct block * blk = bp - sizeof(FENCE); + mark_block_free(blk, words); + next_blk(blk)->header = FENCE; + + /* Coalesce if the previous block was free */ + return coalesce(blk); +} + +/* + * place - Place block of asize words at start of free block bp + * and split if remainder would be at least minimum block size + */ +static void place(struct block *bp, size_t asize) +{ + size_t csize = blk_size(bp); + + if ((csize - asize) >= MIN_BLOCK_SIZE_WORDS) { + mark_block_used(bp, asize); + bp = next_blk(bp); + mark_block_free(bp, csize-asize); + } + else { + mark_block_used(bp, csize); + } +} + +/* + * find_fit - Find a fit for a block with asize words + */ +static struct block *find_fit(size_t asize) +{ + +#ifdef NEXT_FIT + /* Next fit search */ + struct block *oldrover = rover; + + /* Search from the rover to the end of list */ + for ( ; blk_size(rover) > 0; rover = next_blk(rover)) + if (blk_free(rover) && (asize <= blk_size(rover))) + return rover; + + /* search from start of list to old rover */ + for (rover = heap_listp; rover < oldrover; rover = next_blk(rover)) + if (blk_free(rover) && (asize <= blk_size(rover))) + return rover; + + return NULL; /* no fit found */ +#else + /* First fit search */ + struct block *bp; + + for (bp = heap_listp; blk_size(bp) > 0; bp = next_blk(bp)) { + if (blk_free(bp) && asize <= blk_size(bp)) { + return bp; + } + } + return NULL; /* No fit */ +#endif +} + +team_t team = { + /* Team name */ + "Sample allocator using implicit lists", + /* First member's full name */ + "Godmar Back", + "gback@cs.vt.edu", + /* Second member's full name (leave blank if none) */ + "", + "", +}; diff --git a/mm.h b/mm.h new file mode 100644 index 0000000..d41eef7 --- /dev/null +++ b/mm.h @@ -0,0 +1,23 @@ +#include + +extern int mm_init (void); +extern void *mm_malloc (size_t size); +extern void mm_free (void *ptr); +extern void *mm_realloc(void *ptr, size_t size); + + +/* + * Students work in teams of one or two. Teams enter their team name, + * personal names and login IDs in a struct of this + * type in their bits.c file. + */ +typedef struct { + char *teamname; /* ID1+ID2 or ID1 */ + char *name1; /* full name of first member */ + char *id1; /* login ID of first member */ + char *name2; /* full name of second member (if any) */ + char *id2; /* login ID of second member */ +} team_t; + +extern team_t team; + diff --git a/short1-bal.rep b/short1-bal.rep new file mode 100644 index 0000000..6ec0c4c --- /dev/null +++ b/short1-bal.rep @@ -0,0 +1,16 @@ +20000 +6 +12 +1 +a 0 2040 +a 1 2040 +f 1 +a 2 48 +a 3 4072 +f 3 +a 4 4072 +f 0 +f 2 +a 5 4072 +f 4 +f 5 diff --git a/short2-bal.rep b/short2-bal.rep new file mode 100644 index 0000000..4ff7c3c --- /dev/null +++ b/short2-bal.rep @@ -0,0 +1,16 @@ +20000 +6 +12 +1 +a 0 2040 +a 1 4010 +a 2 48 +a 3 4072 +a 4 4072 +a 5 4072 +f 0 +f 1 +f 2 +f 3 +f 4 +f 5