Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 Kevin 1
/*
2
 * mdriver.c - CS:APP Malloc Lab Driver
3
 * 
4
 * Uses a collection of trace files to tests a malloc/free/realloc
5
 * implementation in mm.c.
6
 *
7
 * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
8
 * May not be used, modified, or copied without permission.
9
 */
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <unistd.h>
13
#include <errno.h>
14
#include <string.h>
15
#include <assert.h>
16
#include <float.h>
17
#include <time.h>
18
 
19
#include "mm.h"
20
#include "memlib.h"
21
#include "fsecs.h"
22
#include "config.h"
23
 
24
/**********************
25
 * Constants and macros
26
 **********************/
27
 
28
/* Misc */
29
#define MAXLINE     1024 /* max string size */
30
#define HDRLINES       4 /* number of header lines in a trace file */
31
#define LINENUM(i) (i+5) /* cnvt trace request nums to linenums (origin 1) */
32
 
33
/* Returns true if p is ALIGNMENT-byte aligned */
34
#define IS_ALIGNED(p)  ((((unsigned int)(p)) % ALIGNMENT) == 0)
35
 
36
/****************************** 
37
 * The key compound data types 
38
 *****************************/
39
 
40
/* Records the extent of each block's payload */
41
typedef struct range_t {
42
    char *lo;              /* low payload address */
43
    char *hi;              /* high payload address */
44
    struct range_t *next;  /* next list element */
45
} range_t;
46
 
47
/* Characterizes a single trace operation (allocator request) */
48
typedef struct {
49
    enum {ALLOC, FREE, REALLOC} type; /* type of request */
50
    int index;                        /* index for free() to use later */
51
    int size;                         /* byte size of alloc/realloc request */
52
} traceop_t;
53
 
54
/* Holds the information for one trace file*/
55
typedef struct {
56
    int sugg_heapsize;   /* suggested heap size (unused) */
57
    int num_ids;         /* number of alloc/realloc ids */
58
    int num_ops;         /* number of distinct requests */
59
    int weight;          /* weight for this trace (unused) */
60
    traceop_t *ops;      /* array of requests */
61
    char **blocks;       /* array of ptrs returned by malloc/realloc... */
62
    size_t *block_sizes; /* ... and a corresponding array of payload sizes */
63
} trace_t;
64
 
65
/* 
66
 * Holds the params to the xxx_speed functions, which are timed by fcyc. 
67
 * This struct is necessary because fcyc accepts only a pointer array
68
 * as input.
69
 */
70
typedef struct {
71
    trace_t *trace;  
72
    range_t *ranges;
73
} speed_t;
74
 
75
/* Summarizes the important stats for some malloc function on some trace */
76
typedef struct {
77
    /* defined for both libc malloc and student malloc package (mm.c) */
78
    double ops;      /* number of ops (malloc/free/realloc) in the trace */
79
    int valid;       /* was the trace processed correctly by the allocator? */
80
    double secs;     /* number of secs needed to run the trace */
81
 
82
    /* defined only for the student malloc package */
83
    double util;     /* space utilization for this trace (always 0 for libc) */
84
 
85
    /* Note: secs and util are only defined if valid is true */
86
} stats_t; 
87
 
88
/********************
89
 * Global variables
90
 *******************/
91
int verbose = 0;        /* global flag for verbose output */
92
static int errors = 0;  /* number of errs found when running student malloc */
93
char msg[MAXLINE];      /* for whenever we need to compose an error message */
94
 
95
/* Directory where default tracefiles are found */
96
static char tracedir[MAXLINE] = TRACEDIR;
97
 
98
/* The filenames of the default tracefiles */
99
static char *default_tracefiles[] = {  
100
    DEFAULT_TRACEFILES, NULL
101
};
102
 
103
 
104
/********************* 
105
 * Function prototypes 
106
 *********************/
107
 
108
/* these functions manipulate range lists */
109
static int add_range(range_t **ranges, char *lo, int size, 
110
		     int tracenum, int opnum);
111
static void remove_range(range_t **ranges, char *lo);
112
static void clear_ranges(range_t **ranges);
113
 
114
/* These functions read, allocate, and free storage for traces */
115
static trace_t *read_trace(char *tracedir, char *filename);
116
static void free_trace(trace_t *trace);
117
 
118
/* Routines for evaluating the correctness and speed of libc malloc */
119
static int eval_libc_valid(trace_t *trace, int tracenum);
120
static void eval_libc_speed(void *ptr);
121
 
122
/* Routines for evaluating correctnes, space utilization, and speed 
123
   of the student's malloc package in mm.c */
124
static int eval_mm_valid(trace_t *trace, int tracenum, range_t **ranges);
125
static double eval_mm_util(trace_t *trace, int tracenum, range_t **ranges);
126
static void eval_mm_speed(void *ptr);
127
 
128
/* Various helper routines */
129
static void printresults(int n, stats_t *stats);
130
static void usage(void);
131
static void unix_error(char *msg);
132
static void malloc_error(int tracenum, int opnum, char *msg);
133
static void app_error(char *msg);
134
 
135
/**************
136
 * Main routine
137
 **************/
138
int main(int argc, char **argv)
139
{
140
    int i;
141
    char c;
142
    char **tracefiles = NULL;  /* null-terminated array of trace file names */
143
    int num_tracefiles = 0;    /* the number of traces in that array */
144
    trace_t *trace = NULL;     /* stores a single trace file in memory */
145
    range_t *ranges = NULL;    /* keeps track of block extents for one trace */
146
    stats_t *libc_stats = NULL;/* libc stats for each trace */
147
    stats_t *mm_stats = NULL;  /* mm (i.e. student) stats for each trace */
148
    speed_t speed_params;      /* input parameters to the xx_speed routines */ 
149
 
150
    int team_check = 1;  /* If set, check team structure (reset by -a) */
151
    int run_libc = 0;    /* If set, run libc malloc (set by -l) */
152
    int autograder = 0;  /* If set, emit summary info for autograder (-g) */
153
    int use_mmap = 0;    /* If set, have memlib use mmap() instead malloc() */
154
 
155
    /* temporaries used to compute the performance index */
156
    double secs, ops, util, avg_mm_util, avg_mm_throughput, p1, p2, perfindex;
157
    int numcorrect;
158
 
159
    /* 
160
     * Read and interpret the command line arguments 
161
     */
162
    while ((c = getopt(argc, argv, "nf:t:hvVgal")) != EOF) {
163
        switch (c) {
164
        case 'n':
165
            use_mmap = 1;
166
            break;
167
	case 'g': /* Generate summary info for the autograder */
168
	    autograder = 1;
169
	    break;
170
        case 'f': /* Use one specific trace file only (relative to curr dir) */
171
            num_tracefiles = 1;
172
            if ((tracefiles = realloc(tracefiles, 2*sizeof(char *))) == NULL)
173
		unix_error("ERROR: realloc failed in main");
174
	    strcpy(tracedir, "./"); 
175
            tracefiles[0] = strdup(optarg);
176
            tracefiles[1] = NULL;
177
            break;
178
	case 't': /* Directory where the traces are located */
179
	    if (num_tracefiles == 1) /* ignore if -f already encountered */
180
		break;
181
	    strcpy(tracedir, optarg);
182
	    if (tracedir[strlen(tracedir)-1] != '/') 
183
		strcat(tracedir, "/"); /* path always ends with "/" */
184
	    break;
185
        case 'a': /* Don't check team structure */
186
            team_check = 0;
187
            break;
188
        case 'l': /* Run libc malloc */
189
            run_libc = 1;
190
            break;
191
        case 'v': /* Print per-trace performance breakdown */
192
            verbose = 1;
193
            break;
194
        case 'V': /* Be more verbose than -v */
195
            verbose = 2;
196
            break;
197
        case 'h': /* Print this message */
198
	    usage();
199
            exit(0);
200
        default:
201
	    usage();
202
            exit(1);
203
        }
204
    }
205
 
206
    /* 
207
     * Check and print team info 
208
     */
209
    if (team_check) {
210
	/* Students must fill in their team information */
211
	if (!strcmp(team.teamname, "")) {
212
	    printf("ERROR: Please provide the information about your team in mm.c.\n");
213
	    exit(1);
214
	} else
215
	    printf("Team Name:%s\n", team.teamname);
216
	if ((*team.name1 == '\0') || (*team.id1 == '\0')) {
217
	    printf("ERROR.  You must fill in all team member 1 fields!\n");
218
	    exit(1);
219
	} 
220
	else
221
	    printf("Member 1 :%s:%s\n", team.name1, team.id1);
222
 
223
	if (((*team.name2 != '\0') && (*team.id2 == '\0')) ||
224
	    ((*team.name2 == '\0') && (*team.id2 != '\0'))) { 
225
	    printf("ERROR.  You must fill in all or none of the team member 2 ID fields!\n");
226
	    exit(1);
227
	}
228
	else if (*team.name2 != '\0')
229
	    printf("Member 2 :%s:%s\n", team.name2, team.id2);
230
    }
231
 
232
    /* 
233
     * If no -f command line arg, then use the entire set of tracefiles 
234
     * defined in default_traces[]
235
     */
236
    if (tracefiles == NULL) {
237
        tracefiles = default_tracefiles;
238
        num_tracefiles = sizeof(default_tracefiles) / sizeof(char *) - 1;
239
	printf("Using default tracefiles in %s\n", tracedir);
240
    }
241
 
242
    /* Initialize the timing package */
243
    init_fsecs();
244
 
245
    /*
246
     * Optionally run and evaluate the libc malloc package 
247
     */
248
    if (run_libc) {
249
	if (verbose > 1)
250
	    printf("\nTesting libc malloc\n");
251
 
252
	/* Allocate libc stats array, with one stats_t struct per tracefile */
253
	libc_stats = (stats_t *)calloc(num_tracefiles, sizeof(stats_t));
254
	if (libc_stats == NULL)
255
	    unix_error("libc_stats calloc in main failed");
256
 
257
	/* Evaluate the libc malloc package using the K-best scheme */
258
	for (i=0; i < num_tracefiles; i++) {
259
	    trace = read_trace(tracedir, tracefiles[i]);
260
	    libc_stats[i].ops = trace->num_ops;
261
	    if (verbose > 1)
262
		printf("Checking libc malloc for correctness, ");
263
	    libc_stats[i].valid = eval_libc_valid(trace, i);
264
	    if (libc_stats[i].valid) {
265
		speed_params.trace = trace;
266
		if (verbose > 1)
267
		    printf("and performance.\n");
268
		libc_stats[i].secs = fsecs(eval_libc_speed, &speed_params);
269
	    }
270
	    free_trace(trace);
271
	}
272
 
273
	/* Display the libc results in a compact table */
274
	if (verbose) {
275
	    printf("\nResults for libc malloc:\n");
276
	    printresults(num_tracefiles, libc_stats);
277
	}
278
    }
279
 
280
    /*
281
     * Always run and evaluate the student's mm package
282
     */
283
    if (verbose > 1)
284
	printf("\nTesting mm malloc\n");
285
 
286
    /* Allocate the mm stats array, with one stats_t struct per tracefile */
287
    mm_stats = (stats_t *)calloc(num_tracefiles, sizeof(stats_t));
288
    if (mm_stats == NULL)
289
	unix_error("mm_stats calloc in main failed");
290
 
291
    /* Initialize the simulated memory system in memlib.c */
292
    mem_init(use_mmap); 
293
 
294
    /* Evaluate student's mm malloc package using the K-best scheme */
295
    for (i=0; i < num_tracefiles; i++) {
296
	trace = read_trace(tracedir, tracefiles[i]);
297
	mm_stats[i].ops = trace->num_ops;
298
	if (verbose > 1)
299
	    printf("Checking mm_malloc for correctness, ");
300
	mm_stats[i].valid = eval_mm_valid(trace, i, &ranges);
301
	if (mm_stats[i].valid) {
302
	    if (verbose > 1)
303
		printf("efficiency, ");
304
	    mm_stats[i].util = eval_mm_util(trace, i, &ranges);
305
	    speed_params.trace = trace;
306
	    speed_params.ranges = ranges;
307
	    if (verbose > 1)
308
		printf("and performance.\n");
309
	    mm_stats[i].secs = fsecs(eval_mm_speed, &speed_params);
310
	}
311
	free_trace(trace);
312
    }
313
 
314
    /* Display the mm results in a compact table */
315
    if (verbose) {
316
	printf("\nResults for mm malloc:\n");
317
	printresults(num_tracefiles, mm_stats);
318
	printf("\n");
319
    }
320
 
321
    /* 
322
     * Accumulate the aggregate statistics for the student's mm package 
323
     */
324
    secs = 0;
325
    ops = 0;
326
    util = 0;
327
    numcorrect = 0;
328
    for (i=0; i < num_tracefiles; i++) {
329
	secs += mm_stats[i].secs;
330
	ops += mm_stats[i].ops;
331
	util += mm_stats[i].util;
332
	if (mm_stats[i].valid)
333
	    numcorrect++;
334
    }
335
    avg_mm_util = util/num_tracefiles;
336
 
337
    /* 
338
     * Compute and print the performance index 
339
     */
340
    if (errors == 0) {
341
	avg_mm_throughput = ops/secs;
342
 
343
	p1 = UTIL_WEIGHT * avg_mm_util;
344
	if (avg_mm_throughput > AVG_LIBC_THRUPUT) {
345
	    p2 = (double)(1.0 - UTIL_WEIGHT);
346
	} 
347
	else {
348
	    p2 = ((double) (1.0 - UTIL_WEIGHT)) * 
349
		(avg_mm_throughput/AVG_LIBC_THRUPUT);
350
	}
351
 
352
	perfindex = (p1 + p2)*100.0;
353
	printf("Perf index = %.0f (util) + %.0f (thru) = %.0f/100\n",
354
	       p1*100, 
355
	       p2*100, 
356
	       perfindex);
357
 
358
    }
359
    else { /* There were errors */
360
	perfindex = 0.0;
361
	printf("Terminated with %d errors\n", errors);
362
    }
363
 
364
    if (autograder) {
365
	printf("correct:%d\n", numcorrect);
366
	printf("perfidx:%.0f\n", perfindex);
367
    }
368
 
369
    exit(0);
370
}
371
 
372
 
373
/*****************************************************************
374
 * The following routines manipulate the range list, which keeps 
375
 * track of the extent of every allocated block payload. We use the 
376
 * range list to detect any overlapping allocated blocks.
377
 ****************************************************************/
378
 
379
/*
380
 * add_range - As directed by request opnum in trace tracenum,
381
 *     we've just called the student's mm_malloc to allocate a block of 
382
 *     size bytes at addr lo. After checking the block for correctness,
383
 *     we create a range struct for this block and add it to the range list. 
384
 */
385
static int add_range(range_t **ranges, char *lo, int size, 
386
		     int tracenum, int opnum)
387
{
388
    char *hi = lo + size - 1;
389
    range_t *p;
390
    char msg[MAXLINE];
391
 
392
    assert(size > 0);
393
 
394
    /* Payload addresses must be ALIGNMENT-byte aligned */
395
    if (!IS_ALIGNED(lo)) {
396
	sprintf(msg, "Payload address (%p) not aligned to %d bytes", 
397
		lo, ALIGNMENT);
398
        malloc_error(tracenum, opnum, msg);
399
        return 0;
400
    }
401
 
402
    /* The payload must lie within the extent of the heap */
403
    if ((lo < (char *)mem_heap_lo()) || (lo > (char *)mem_heap_hi()) || 
404
	(hi < (char *)mem_heap_lo()) || (hi > (char *)mem_heap_hi())) {
405
	sprintf(msg, "Payload (%p:%p) lies outside heap (%p:%p)",
406
		lo, hi, mem_heap_lo(), mem_heap_hi());
407
	malloc_error(tracenum, opnum, msg);
408
        return 0;
409
    }
410
 
411
    /* The payload must not overlap any other payloads */
412
    for (p = *ranges;  p != NULL;  p = p->next) {
413
        if ((lo >= p->lo && lo <= p-> hi) ||
414
            (hi >= p->lo && hi <= p->hi)) {
415
	    sprintf(msg, "Payload (%p:%p) overlaps another payload (%p:%p)\n",
416
		    lo, hi, p->lo, p->hi);
417
	    malloc_error(tracenum, opnum, msg);
418
	    return 0;
419
        }
420
    }
421
 
422
    /* 
423
     * Everything looks OK, so remember the extent of this block 
424
     * by creating a range struct and adding it the range list.
425
     */
426
    if ((p = (range_t *)malloc(sizeof(range_t))) == NULL)
427
	unix_error("malloc error in add_range");
428
    p->next = *ranges;
429
    p->lo = lo;
430
    p->hi = hi;
431
    *ranges = p;
432
    return 1;
433
}
434
 
435
/* 
436
 * remove_range - Free the range record of block whose payload starts at lo 
437
 */
438
static void remove_range(range_t **ranges, char *lo)
439
{
440
    range_t *p;
441
    range_t **prevpp = ranges;
442
    int size;
443
 
444
    for (p = *ranges;  p != NULL; p = p->next) {
445
        if (p->lo == lo) {
446
	    *prevpp = p->next;
447
            size = p->hi - p->lo + 1;
448
            free(p);
449
            break;
450
        }
451
        prevpp = &(p->next);
452
    }
453
}
454
 
455
/*
456
 * clear_ranges - free all of the range records for a trace 
457
 */
458
static void clear_ranges(range_t **ranges)
459
{
460
    range_t *p;
461
    range_t *pnext;
462
 
463
    for (p = *ranges;  p != NULL;  p = pnext) {
464
        pnext = p->next;
465
        free(p);
466
    }
467
    *ranges = NULL;
468
}
469
 
470
 
471
/**********************************************
472
 * The following routines manipulate tracefiles
473
 *********************************************/
474
 
475
/*
476
 * read_trace - read a trace file and store it in memory
477
 */
478
static trace_t *read_trace(char *tracedir, char *filename)
479
{
480
    FILE *tracefile;
481
    trace_t *trace;
482
    char type[MAXLINE];
483
    char path[MAXLINE];
484
    unsigned index, size;
485
    unsigned max_index = 0;
486
    unsigned op_index;
487
 
488
    if (verbose > 1)
489
	printf("Reading tracefile: %s\n", filename);
490
 
491
    /* Allocate the trace record */
492
    if ((trace = (trace_t *) malloc(sizeof(trace_t))) == NULL)
493
	unix_error("malloc 1 failed in read_trance");
494
 
495
    /* Read the trace file header */
496
    strcpy(path, tracedir);
497
    strcat(path, filename);
498
    if ((tracefile = fopen(path, "r")) == NULL) {
499
	sprintf(msg, "Could not open %s in read_trace", path);
500
	unix_error(msg);
501
    }
502
    fscanf(tracefile, "%d", &(trace->sugg_heapsize)); /* not used */
503
    fscanf(tracefile, "%d", &(trace->num_ids));     
504
    fscanf(tracefile, "%d", &(trace->num_ops));     
505
    fscanf(tracefile, "%d", &(trace->weight));        /* not used */
506
 
507
    /* We'll store each request line in the trace in this array */
508
    if ((trace->ops = 
509
	 (traceop_t *)malloc(trace->num_ops * sizeof(traceop_t))) == NULL)
510
	unix_error("malloc 2 failed in read_trace");
511
 
512
    /* We'll keep an array of pointers to the allocated blocks here... */
513
    if ((trace->blocks = 
514
	 (char **)malloc(trace->num_ids * sizeof(char *))) == NULL)
515
	unix_error("malloc 3 failed in read_trace");
516
 
517
    /* ... along with the corresponding byte sizes of each block */
518
    if ((trace->block_sizes = 
519
	 (size_t *)malloc(trace->num_ids * sizeof(size_t))) == NULL)
520
	unix_error("malloc 4 failed in read_trace");
521
 
522
    /* read every request line in the trace file */
523
    index = 0;
524
    op_index = 0;
525
    while (fscanf(tracefile, "%s", type) != EOF) {
526
	switch(type[0]) {
527
	case 'a':
528
	    fscanf(tracefile, "%u %u", &index, &size);
529
	    trace->ops[op_index].type = ALLOC;
530
	    trace->ops[op_index].index = index;
531
	    trace->ops[op_index].size = size;
532
	    max_index = (index > max_index) ? index : max_index;
533
	    break;
534
	case 'r':
535
	    fscanf(tracefile, "%u %u", &index, &size);
536
	    trace->ops[op_index].type = REALLOC;
537
	    trace->ops[op_index].index = index;
538
	    trace->ops[op_index].size = size;
539
	    max_index = (index > max_index) ? index : max_index;
540
	    break;
541
	case 'f':
542
	    fscanf(tracefile, "%ud", &index);
543
	    trace->ops[op_index].type = FREE;
544
	    trace->ops[op_index].index = index;
545
	    break;
546
	default:
547
	    printf("Bogus type character (%c) in tracefile %s\n", 
548
		   type[0], path);
549
	    exit(1);
550
	}
551
	op_index++;
552
 
553
    }
554
    fclose(tracefile);
555
    assert(max_index == trace->num_ids - 1);
556
    assert(trace->num_ops == op_index);
557
 
558
    return trace;
559
}
560
 
561
/*
562
 * free_trace - Free the trace record and the three arrays it points
563
 *              to, all of which were allocated in read_trace().
564
 */
565
void free_trace(trace_t *trace)
566
{
567
    free(trace->ops);         /* free the three arrays... */
568
    free(trace->blocks);      
569
    free(trace->block_sizes);
570
    free(trace);              /* and the trace record itself... */
571
}
572
 
573
/**********************************************************************
574
 * The following functions evaluate the correctness, space utilization,
575
 * and throughput of the libc and mm malloc packages.
576
 **********************************************************************/
577
 
578
/*
579
 * eval_mm_valid - Check the mm malloc package for correctness
580
 */
581
static int eval_mm_valid(trace_t *trace, int tracenum, range_t **ranges) 
582
{
583
    int i, j;
584
    int index;
585
    int size;
586
    int oldsize;
587
    char *newp;
588
    char *oldp;
589
    char *p;
590
 
591
    /* Reset the heap and free any records in the range list */
592
    mem_reset_brk();
593
    clear_ranges(ranges);
594
 
595
    /* Call the mm package's init function */
596
    if (mm_init() < 0) {
597
	malloc_error(tracenum, 0, "mm_init failed.");
598
	return 0;
599
    }
600
 
601
    /* Interpret each operation in the trace in order */
602
    for (i = 0;  i < trace->num_ops;  i++) {
603
	index = trace->ops[i].index;
604
	size = trace->ops[i].size;
605
 
606
        switch (trace->ops[i].type) {
607
 
608
        case ALLOC: /* mm_malloc */
609
 
610
	    /* Call the student's malloc */
611
	    if ((p = mm_malloc(size)) == NULL) {
612
		malloc_error(tracenum, i, "mm_malloc failed.");
613
		return 0;
614
	    }
615
 
616
	    /* 
617
	     * Test the range of the new block for correctness and add it 
618
	     * to the range list if OK. The block must be  be aligned properly,
619
	     * and must not overlap any currently allocated block. 
620
	     */ 
621
	    if (add_range(ranges, p, size, tracenum, i) == 0)
622
		return 0;
623
 
624
	    /* ADDED: cgw
625
	     * fill range with low byte of index.  This will be used later
626
	     * if we realloc the block and wish to make sure that the old
627
	     * data was copied to the new block
628
	     */
629
	    memset(p, index & 0xFF, size);
630
 
631
	    /* Remember region */
632
	    trace->blocks[index] = p;
633
	    trace->block_sizes[index] = size;
634
	    break;
635
 
636
        case REALLOC: /* mm_realloc */
637
 
638
	    /* Call the student's realloc */
639
	    oldp = trace->blocks[index];
640
	    if ((newp = mm_realloc(oldp, size)) == NULL) {
641
		malloc_error(tracenum, i, "mm_realloc failed.");
642
		return 0;
643
	    }
644
 
645
	    /* Remove the old region from the range list */
646
	    remove_range(ranges, oldp);
647
 
648
	    /* Check new block for correctness and add it to range list */
649
	    if (add_range(ranges, newp, size, tracenum, i) == 0)
650
		return 0;
651
 
652
	    /* ADDED: cgw
653
	     * Make sure that the new block contains the data from the old 
654
	     * block and then fill in the new block with the low order byte
655
	     * of the new index
656
	     */
657
	    oldsize = trace->block_sizes[index];
658
	    if (size < oldsize) oldsize = size;
659
	    for (j = 0; j < oldsize; j++) {
660
	      if (newp[j] != (index & 0xFF)) {
661
		malloc_error(tracenum, i, "mm_realloc did not preserve the "
662
			     "data from old block");
663
		return 0;
664
	      }
665
	    }
666
	    memset(newp, index & 0xFF, size);
667
 
668
	    /* Remember region */
669
	    trace->blocks[index] = newp;
670
	    trace->block_sizes[index] = size;
671
	    break;
672
 
673
        case FREE: /* mm_free */
674
 
675
	    /* Remove region from list and call student's free function */
676
	    p = trace->blocks[index];
677
	    remove_range(ranges, p);
678
	    mm_free(p);
679
	    break;
680
 
681
	default:
682
	    app_error("Nonexistent request type in eval_mm_valid");
683
        }
684
 
685
    }
686
 
687
    /* As far as we know, this is a valid malloc package */
688
    return 1;
689
}
690
 
691
/* 
692
 * eval_mm_util - Evaluate the space utilization of the student's package
693
 *   The idea is to remember the high water mark "hwm" of the heap for 
694
 *   an optimal allocator, i.e., no gaps and no internal fragmentation.
695
 *   Utilization is the ratio hwm/heapsize, where heapsize is the 
696
 *   size of the heap in bytes after running the student's malloc 
697
 *   package on the trace. Note that our implementation of mem_sbrk() 
698
 *   doesn't allow the students to decrement the brk pointer, so brk
699
 *   is always the high water mark of the heap. 
700
 *   
701
 */
702
static double eval_mm_util(trace_t *trace, int tracenum, range_t **ranges)
703
{   
704
    int i;
705
    int index;
706
    int size, newsize, oldsize;
707
    int max_total_size = 0;
708
    int total_size = 0;
709
    char *p;
710
    char *newp, *oldp;
711
 
712
    /* initialize the heap and the mm malloc package */
713
    mem_reset_brk();
714
    if (mm_init() < 0)
715
	app_error("mm_init failed in eval_mm_util");
716
 
717
    for (i = 0;  i < trace->num_ops;  i++) {
718
        switch (trace->ops[i].type) {
719
 
720
        case ALLOC: /* mm_alloc */
721
	    index = trace->ops[i].index;
722
	    size = trace->ops[i].size;
723
 
724
	    if ((p = mm_malloc(size)) == NULL) 
725
		app_error("mm_malloc failed in eval_mm_util");
726
 
727
	    /* Remember region and size */
728
	    trace->blocks[index] = p;
729
	    trace->block_sizes[index] = size;
730
 
731
	    /* Keep track of current total size
732
	     * of all allocated blocks */
733
	    total_size += size;
734
 
735
	    /* Update statistics */
736
	    max_total_size = (total_size > max_total_size) ?
737
		total_size : max_total_size;
738
	    break;
739
 
740
	case REALLOC: /* mm_realloc */
741
	    index = trace->ops[i].index;
742
	    newsize = trace->ops[i].size;
743
	    oldsize = trace->block_sizes[index];
744
 
745
	    oldp = trace->blocks[index];
746
	    if ((newp = mm_realloc(oldp,newsize)) == NULL)
747
		app_error("mm_realloc failed in eval_mm_util");
748
 
749
	    /* Remember region and size */
750
	    trace->blocks[index] = newp;
751
	    trace->block_sizes[index] = newsize;
752
 
753
	    /* Keep track of current total size
754
	     * of all allocated blocks */
755
	    total_size += (newsize - oldsize);
756
 
757
	    /* Update statistics */
758
	    max_total_size = (total_size > max_total_size) ?
759
		total_size : max_total_size;
760
	    break;
761
 
762
        case FREE: /* mm_free */
763
	    index = trace->ops[i].index;
764
	    size = trace->block_sizes[index];
765
	    p = trace->blocks[index];
766
 
767
	    mm_free(p);
768
 
769
	    /* Keep track of current total size
770
	     * of all allocated blocks */
771
	    total_size -= size;
772
 
773
	    break;
774
 
775
	default:
776
	    app_error("Nonexistent request type in eval_mm_util");
777
 
778
        }
779
    }
780
 
781
    return ((double)max_total_size / (double)mem_heapsize());
782
}
783
 
784
 
785
/*
786
 * eval_mm_speed - This is the function that is used by fcyc()
787
 *    to measure the running time of the mm malloc package.
788
 */
789
static void eval_mm_speed(void *ptr)
790
{
791
    int i, index, size, newsize;
792
    char *p, *newp, *oldp, *block;
793
    trace_t *trace = ((speed_t *)ptr)->trace;
794
 
795
    /* Reset the heap and initialize the mm package */
796
    mem_reset_brk();
797
    if (mm_init() < 0) 
798
	app_error("mm_init failed in eval_mm_speed");
799
 
800
    /* Interpret each trace request */
801
    for (i = 0;  i < trace->num_ops;  i++)
802
        switch (trace->ops[i].type) {
803
 
804
        case ALLOC: /* mm_malloc */
805
            index = trace->ops[i].index;
806
            size = trace->ops[i].size;
807
            if ((p = mm_malloc(size)) == NULL)
808
		app_error("mm_malloc error in eval_mm_speed");
809
            trace->blocks[index] = p;
810
            break;
811
 
812
	case REALLOC: /* mm_realloc */
813
	    index = trace->ops[i].index;
814
            newsize = trace->ops[i].size;
815
	    oldp = trace->blocks[index];
816
            if ((newp = mm_realloc(oldp,newsize)) == NULL)
817
		app_error("mm_realloc error in eval_mm_speed");
818
            trace->blocks[index] = newp;
819
            break;
820
 
821
        case FREE: /* mm_free */
822
            index = trace->ops[i].index;
823
            block = trace->blocks[index];
824
            mm_free(block);
825
            break;
826
 
827
	default:
828
	    app_error("Nonexistent request type in eval_mm_valid");
829
        }
830
}
831
 
832
/*
833
 * eval_libc_valid - We run this function to make sure that the
834
 *    libc malloc can run to completion on the set of traces.
835
 *    We'll be conservative and terminate if any libc malloc call fails.
836
 *
837
 */
838
static int eval_libc_valid(trace_t *trace, int tracenum)
839
{
840
    int i, newsize;
841
    char *p, *newp, *oldp;
842
 
843
    for (i = 0;  i < trace->num_ops;  i++) {
844
        switch (trace->ops[i].type) {
845
 
846
        case ALLOC: /* malloc */
847
	    if ((p = malloc(trace->ops[i].size)) == NULL) {
848
		malloc_error(tracenum, i, "libc malloc failed");
849
		unix_error("System message");
850
	    }
851
	    trace->blocks[trace->ops[i].index] = p;
852
	    break;
853
 
854
	case REALLOC: /* realloc */
855
            newsize = trace->ops[i].size;
856
	    oldp = trace->blocks[trace->ops[i].index];
857
	    if ((newp = realloc(oldp, newsize)) == NULL) {
858
		malloc_error(tracenum, i, "libc realloc failed");
859
		unix_error("System message");
860
	    }
861
	    trace->blocks[trace->ops[i].index] = newp;
862
	    break;
863
 
864
        case FREE: /* free */
865
	    free(trace->blocks[trace->ops[i].index]);
866
	    break;
867
 
868
	default:
869
	    app_error("invalid operation type  in eval_libc_valid");
870
	}
871
    }
872
 
873
    return 1;
874
}
875
 
876
/* 
877
 * eval_libc_speed - This is the function that is used by fcyc() to
878
 *    measure the running time of the libc malloc package on the set
879
 *    of traces.
880
 */
881
static void eval_libc_speed(void *ptr)
882
{
883
    int i;
884
    int index, size, newsize;
885
    char *p, *newp, *oldp, *block;
886
    trace_t *trace = ((speed_t *)ptr)->trace;
887
 
888
    for (i = 0;  i < trace->num_ops;  i++) {
889
        switch (trace->ops[i].type) {
890
        case ALLOC: /* malloc */
891
	    index = trace->ops[i].index;
892
	    size = trace->ops[i].size;
893
	    if ((p = malloc(size)) == NULL)
894
		unix_error("malloc failed in eval_libc_speed");
895
	    trace->blocks[index] = p;
896
	    break;
897
 
898
	case REALLOC: /* realloc */
899
	    index = trace->ops[i].index;
900
	    newsize = trace->ops[i].size;
901
	    oldp = trace->blocks[index];
902
	    if ((newp = realloc(oldp, newsize)) == NULL)
903
		unix_error("realloc failed in eval_libc_speed\n");
904
 
905
	    trace->blocks[index] = newp;
906
	    break;
907
 
908
        case FREE: /* free */
909
	    index = trace->ops[i].index;
910
	    block = trace->blocks[index];
911
	    free(block);
912
	    break;
913
	}
914
    }
915
}
916
 
917
/*************************************
918
 * Some miscellaneous helper routines
919
 ************************************/
920
 
921
 
922
/*
923
 * printresults - prints a performance summary for some malloc package
924
 */
925
static void printresults(int n, stats_t *stats) 
926
{
927
    int i;
928
    double secs = 0;
929
    double ops = 0;
930
    double util = 0;
931
 
932
    /* Print the individual results for each trace */
933
    printf("%5s%7s %5s%8s%10s%6s\n", 
934
	   "trace", " valid", "util", "ops", "secs", "Kops");
935
    for (i=0; i < n; i++) {
936
	if (stats[i].valid) {
937
	    printf("%2d%10s%5.0f%%%8.0f%10.6f%6.0f\n", 
938
		   i,
939
		   "yes",
940
		   stats[i].util*100.0,
941
		   stats[i].ops,
942
		   stats[i].secs,
943
		   (stats[i].ops/1e3)/stats[i].secs);
944
	    secs += stats[i].secs;
945
	    ops += stats[i].ops;
946
	    util += stats[i].util;
947
	}
948
	else {
949
	    printf("%2d%10s%6s%8s%10s%6s\n", 
950
		   i,
951
		   "no",
952
		   "-",
953
		   "-",
954
		   "-",
955
		   "-");
956
	}
957
    }
958
 
959
    /* Print the aggregate results for the set of traces */
960
    if (errors == 0) {
961
	printf("%12s%5.0f%%%8.0f%10.6f%6.0f\n", 
962
	       "Total       ",
963
	       (util/n)*100.0,
964
	       ops, 
965
	       secs,
966
	       (ops/1e3)/secs);
967
    }
968
    else {
969
	printf("%12s%6s%8s%10s%6s\n", 
970
	       "Total       ",
971
	       "-", 
972
	       "-", 
973
	       "-", 
974
	       "-");
975
    }
976
 
977
}
978
 
979
/* 
980
 * app_error - Report an arbitrary application error
981
 */
982
void app_error(char *msg) 
983
{
984
    printf("%s\n", msg);
985
    exit(1);
986
}
987
 
988
/* 
989
 * unix_error - Report a Unix-style error
990
 */
991
void unix_error(char *msg) 
992
{
993
    printf("%s: %s\n", msg, strerror(errno));
994
    exit(1);
995
}
996
 
997
/*
998
 * malloc_error - Report an error returned by the mm_malloc package
999
 */
1000
void malloc_error(int tracenum, int opnum, char *msg)
1001
{
1002
    errors++;
1003
    printf("ERROR [trace %d, line %d]: %s\n", tracenum, LINENUM(opnum), msg);
1004
}
1005
 
1006
/* 
1007
 * usage - Explain the command line arguments
1008
 */
1009
static void usage(void) 
1010
{
1011
    fprintf(stderr, "Usage: mdriver [-hvVal] [-f <file>] [-t <dir>]\n");
1012
    fprintf(stderr, "Options\n");
1013
    fprintf(stderr, "\t-a         Don't check the team structure.\n");
1014
    fprintf(stderr, "\t-f <file>  Use <file> as the trace file.\n");
1015
    fprintf(stderr, "\t-g         Generate summary info for autograder.\n");
1016
    fprintf(stderr, "\t-h         Print this message.\n");
1017
    fprintf(stderr, "\t-l         Run libc malloc as well.\n");
1018
    fprintf(stderr, "\t-t <dir>   Directory to find default traces.\n");
1019
    fprintf(stderr, "\t-v         Print per-trace performance breakdowns.\n");
1020
    fprintf(stderr, "\t-V         Print additional debug info.\n");
1021
    fprintf(stderr, "\t-n         Don't randomize addresses.\n");
1022
}