📄 mtest.c
字号:
/* * mtest.c - test harness for the CS:APP malloc lab * * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. * May not be used, modified, or copied without permission. */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <assert.h>#include <float.h>#include <time.h>#include "mm.h"#include "memlib.h"#include "fsecs.h"#include "config.h"/********************** * Constants and macros **********************//* Misc */#define MAXLINE 128 /* max string size */#define HDRLINES 4 /* number of header lines in a trace file */#define LINENUM(i) (i+5) /* cnvt trace request nums to line nums (origin unity)*//* 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 *//* * List of Default tracefiles. * Modify this if you want to add or delete traces. */static char *default_tracefiles[] = { "traces/amptjp-bal.rep", "traces/cccp-bal.rep", "traces/cp-decl-bal.rep", "traces/expr-bal.rep", "traces/coalescing-bal.rep", "traces/random-bal.rep", "traces/random2-bal.rep", "traces/binary-bal.rep", "traces/binary2-bal.rep", "traces/realloc-bal.rep", "traces/realloc2-bal.rep", 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 *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, 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) */ /* temporaries used to compute the performance index */ double secs, ops, util, avg_mm_util, avg_mm_throughput, p1, p2; /* * Read and interpret the command line arguments */ while ((c = getopt(argc, argv, "f:hvVal")) != EOF) { switch (c) { case 'f': /* Use one specific trace file only */ num_tracefiles = 1; if ((tracefiles = realloc(tracefiles, sizeof(char *))) == NULL) unix_error("ERROR: realloc failed in main"); tracefiles[0] = strdup(optarg); tracefiles[1] = NULL; 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. Fill in information about your team in bits.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 traces.h */ if (tracefiles == NULL) { tracefiles = default_tracefiles; num_tracefiles = sizeof(default_tracefiles) / sizeof(char *) - 1; } /* 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(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, 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(); /* Evaluate student's mm malloc package using the K-best scheme */ for (i=0; i < num_tracefiles; i++) { trace = read_trace(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, mm_stats); printf("\n"); } /* * Accumulate the aggregate statistics for the student's mm package */ secs = 0; ops = 0; util = 0; for (i=0; i < num_tracefiles; i++) { secs += mm_stats[i].secs; ops += mm_stats[i].ops; util += mm_stats[i].util; } 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); } if (errors == 0) printf("Perf index = %.0f (util) + %.0f (thru) = %.0f/100\n", p1*100, p2*100, (p1 + p2)*100.0); else printf("Terminated with %d errors\n", errors); exit(0); } else exit(1);}/***************************************************************** * 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 *filename){ FILE *tracefile; trace_t *trace; char type[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 */ if ((tracefile = fopen(filename, "r")) == NULL) unix_error("fopen failed in read_trace"); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -