From 92fa2c791ac4d2a27497c5d6bd0fb33430bb5003 Mon Sep 17 00:00:00 2001 From: Benjamin Scott Pruett Date: Wed, 25 Mar 2015 14:04:21 -0400 Subject: [PATCH] initial import of base files --- Makefile | 40 ++ clock.c | 279 +++++++++++ clock.h | 22 + config.h | 76 +++ fcyc.c | 251 ++++++++++ fcyc.h | 68 +++ fsecs.c | 57 +++ fsecs.h | 4 + ftimer.c | 106 +++++ ftimer.h | 14 + list.c | 532 +++++++++++++++++++++ list.h | 170 +++++++ mdriver.c | 1075 +++++++++++++++++++++++++++++++++++++++++++ memlib.c | 126 +++++ memlib.h | 11 + mm-book-implicit.c | 400 ++++++++++++++++ mm-gback-implicit.c | 374 +++++++++++++++ mm.h | 23 + short1-bal.rep | 16 + short2-bal.rep | 16 + 20 files changed, 3660 insertions(+) create mode 100644 Makefile create mode 100644 clock.c create mode 100644 clock.h create mode 100644 config.h create mode 100644 fcyc.c create mode 100644 fcyc.h create mode 100644 fsecs.c create mode 100644 fsecs.h create mode 100644 ftimer.c create mode 100644 ftimer.h create mode 100644 list.c create mode 100644 list.h create mode 100644 mdriver.c create mode 100644 memlib.c create mode 100644 memlib.h create mode 100644 mm-book-implicit.c create mode 100644 mm-gback-implicit.c create mode 100644 mm.h create mode 100644 short1-bal.rep create mode 100644 short2-bal.rep 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