/* * 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 #include #ifndef _WIN32 #include #include #else #include "getopt.h" #define strdup _strdup #define unlink _unlink #endif #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) */ #define MAXHEAP 1024 /* maximum allowed heap size for heapcheck */ #define MAX_ITER_COUNT 100 /* 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 enum { ALLOC, FREE, REALLOC, HEAPCHECK, ERROR_BUG } RequestType; typedef struct { RequestType 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 */ int* allocated; /* 0 : initial value 1 : allocated block 2 : freed block */ int* check; /* used to check the correctness of the heapcheck function */ } 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; /* Captures the output of the heapcheck function */ typedef struct { int request_id; int payload; } block_t; /******************** * Global variables *******************/ int verbose = 1; /* global flag for verbose output */ static int errors = 0; /* number of errs found when running student malloc */ char msg[2 * MAXLINE]; /* for whenever we need to compose an error message */ block_t heap[MAXHEAP]; /* heap to parse output of heapcheck */ /* 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, stats_t* stats); static void usage(char* exec); static int parse_heap(char* file, block_t* heap, int max_blocks, int tracenum, int opnum); static void expected_blocks(trace_t* trace); static void outputted_blocks(block_t* heap, int heapsize); void eval_libc_speed_ref_prepare(void); void eval_libc_speed_ref(void* ptr); void remove_enter(char* ptr); void remove_enter(char* ptr) { int i; int len = (int)strlen(ptr); for (i = 0; i < len; i++) if (ptr[i] == '\r' || ptr[i] == '\n') ptr[i] = 0; } /************** * Main routine **************/ int main(int argc, char** argv) { FILE* TRACE_LIST; char path[MAXLINE]; char line[MAXLINE]; //double ref_malloc_speed; 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) */ /* temporaries used to compute the performance index */ double secs, ops, util, avg_mm_util, avg_mm_throughput, p1, p2, perfindex; int numcorrect; /* * Read and interpret the command line arguments */ while ((c = getopt(argc, argv, "f:t:hvVgl")) != EOF) { switch (c) { 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 = (char**)realloc(tracefiles, 2 * sizeof(char*))) == NULL) { printf("[%s]realloc失败\n", __func__); } 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(argv[0]); exit(0); default: usage(argv[0]); exit(1); } } /* * If no -f command line arg, then use the entire set of tracefiles * defined in default_traces[] */ if (tracefiles == NULL) { strcpy(path, tracedir); strcat(path, "TRACE_LIST.txt"); TRACE_LIST = fopen(path, "r"); if (TRACE_LIST == NULL) { printf("[%s]没有找到%s文件,使用缺省的trace文件(%s)\n", __func__, path, tracedir); tracefiles = default_tracefiles; num_tracefiles = sizeof(default_tracefiles) / sizeof(char*) - 1; } else { num_tracefiles = 0; while (fgets(line, MAXLINE, TRACE_LIST)) { if (line[0] == '#' || line[0] == '/' || line[0] == '\n' || line[0] == '\r') continue; tracefiles = realloc(tracefiles, (num_tracefiles + 2) * sizeof(char*)); remove_enter(line); tracefiles[num_tracefiles] = strdup(line); tracefiles[num_tracefiles + 1] = NULL; num_tracefiles++; } fclose(TRACE_LIST); printf("[%s]num_tracefiles=%d\n", __func__, num_tracefiles); for (i = 0; i < num_tracefiles; i++) printf("[%2d]%s\n", i, tracefiles[i]); } } /* Initialize the timing package */ init_fsecs(); /* ref_malloc_speed = 0; eval_libc_speed_ref_prepare(); ref_malloc_speed = fsecs(eval_libc_speed_ref,&speed_params); printf("ref_malloc_speed=%f\n",ref_malloc_speed); // init_fsecs(); */ /* * Optionally run and evaluate the libc malloc package */ if (run_libc) { if (verbose > 1) printf("\n[%s]检测libc库函数malloc\n", __func__); /* 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) printf("[%s]libc_stats calloc失败\n", __func__); /* 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("[%s]检测libc malloc的正确性, ", __func__); libc_stats[i].valid = eval_libc_valid(trace, i); if (libc_stats[i].valid) { speed_params.trace = trace; if (verbose > 1) printf("和性能\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("\n[%s]libc malloc的检测结果:\n", __func__); printresults(num_tracefiles, libc_stats); } } /* * Always run and evaluate the student's mm package */ if (verbose > 1) printf("\n[%s]检测mm文件中的malloc\n", __func__); /* 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) printf("[%s]mm_stats calloc失败\n", __func__); /* 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(tracedir, tracefiles[i]); mm_stats[i].ops = trace->num_ops; if (verbose > 1) printf("[%s]检测mm_malloc的正确性, ", __func__); mm_stats[i].valid = eval_mm_valid(trace, i, &ranges); if (mm_stats[i].valid) { if (verbose > 1) printf("效率, "); mm_stats[i].util = eval_mm_util(trace, i, &ranges); speed_params.trace = trace; speed_params.ranges = ranges; if (verbose > 1) printf("和性能.\n"); // mm_stats[i].secs = 0; // for (ii=0;ii 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("[%s]评分 = %.0f (效率%d) + %.0f (性能%d) = %.0f/100\n", __func__, p1 * 100, (int)(UTIL_WEIGHT * 100), p2 * 100, (int)(100 - UTIL_WEIGHT * 100), perfindex); } else { /* There were errors */ perfindex = 0.0; printf("[%s]程序因为%d个错误而终止!\n", __func__, errors); } if (autograder) { printf("[%s]正确:%d\n", __func__, numcorrect); printf("[%s]评分:%.0f\n", __func__, perfindex); } 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; assert(size > 0); /* Payload addresses must be ALIGNMENT-byte aligned */ if (!IS_ALIGNED(lo)) { printf("[%s]Payload的地址(%p)没有对齐到%d字节的边界\n", __func__, lo, ALIGNMENT); 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())) { printf("[%s]Payload (%p:%p)处在堆范围之外(%p:%p)\n", __func__, lo, hi, mem_heap_lo(), mem_heap_hi()); 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)) { printf("[%s]Payload (%p:%p)与另外的payload重叠了(%p:%p)\n", __func__, lo, hi, p->lo, p->hi); 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) printf("[%s]malloc失败\n", __func__); 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; for (p = *ranges; p != NULL; p = p->next) { if (p->lo == lo) { *prevpp = p->next; // int 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]; int index, size; int max_index = 0; int op_index; int i; int line_count = 0; int retval; if (verbose > 1) printf("[%s]读取trace文件: %s\n", __func__, filename); /* Allocate the trace record */ if ((trace = (trace_t*)malloc(sizeof(trace_t))) == NULL) printf("[%s]malloc trace失败\n", __func__); /* Read the trace file header */ strcpy(path, tracedir); strcat(path, filename); if ((tracefile = fopen(path, "r")) == NULL) { printf("[%s]不能打开文件 %s\n", __func__, path); } retval = fscanf(tracefile, "%d", &(trace->sugg_heapsize)); /* not used */ if (retval == 0) { printf("[%s]读取trace文件格式错误:没有sugg_heapsize\n", __func__); } retval = fscanf(tracefile, "%d", &(trace->num_ids)); if (retval == 0) { printf("[%s]读取trace文件格式错误:没有num_ids\n", __func__); } retval = fscanf(tracefile, "%d", &(trace->num_ops)); if (retval == 0) { printf("[%s]读取trace文件格式错误:没有num_ops\n", __func__); } retval = fscanf(tracefile, "%d", &(trace->weight)); /* not used */ if (retval == 0) { printf("[%s]读取trace文件格式错误:没有weight\n", __func__); } line_count = 4; /* 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) printf("[%s]malloc trace->ops失败\n", __func__); /* We'll keep an array of pointers to the allocated blocks here... */ if ((trace->blocks = (char**)malloc(trace->num_ids * sizeof(char*))) == NULL) printf("[%s]malloc trace->blocks失败", __func__); memset(trace->blocks, 0, trace->num_ids * sizeof(char*)); /* ... along with the corresponding byte sizes of each block */ if ((trace->block_sizes = (size_t*)malloc(trace->num_ids * sizeof(size_t))) == NULL) printf("[%s]malloc trace->block_sizes失败", __func__); /* ... along with flags for the heapcheck function */ if ((trace->allocated = (int*)malloc(trace->num_ids * sizeof(int))) == NULL) printf("[%s]malloc trace->allocated失败", __func__); /* initialize allocated to 0 */ for (i = 0; i < trace->num_ids; i++) trace->allocated[i] = 0; if ((trace->check = (int*)malloc(trace->num_ids * sizeof(int))) == NULL) printf("[%s]malloc trace->check失败", __func__); /* read every request line in the trace file */ index = 0; op_index = 0; while (fscanf(tracefile, "%s", type) != EOF) { line_count++; switch (type[0]) { case 'a': if (2 != fscanf(tracefile, "%u %u", &index, &size)) { printf("[%s]%d 行,allocation需要两个参数\n", __func__, line_count); } 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; // workaround realloc bug trace->blocks[index] = (char *)1; break; case 'r': if (2 != fscanf(tracefile, "%u %u", &index, &size)) { printf("[%s]%d 行,relloc需要两个参数\n", __func__, line_count); } if (trace->blocks[index] == (char*)1) { trace->ops[op_index].type = REALLOC; trace->blocks[index] = (char*)1; } else { // workaround realloc bug if (verbose > 1) printf("[%s]%d 行,尝试realloc一个未初始化的指针(%d),修改为alloc\n", __func__, line_count, index); 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 'c': trace->ops[op_index].type = HEAPCHECK; break; case 'f': if (1 != fscanf(tracefile, "%ud", &index)) { printf("[%s]%d 行,free需要一个参数\n", __func__, line_count); } // workaround free -1 bug if (index == -1) { if (verbose > 1) printf("[%s]%d 行,free一个-1指针,跳过\n", __func__, line_count); trace->ops[op_index].type = ERROR_BUG; } else trace->ops[op_index].type = FREE; trace->ops[op_index].index = index; break; default: printf("[%s]trace文件[%s,%d行]中错误的类型字符(%c)\n", __func__, path, line_count, type[0]); 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(). */ static 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; char tmpfile[L_tmpnam]; //int fd; //int stdoutold; int heapsize; int request_id; int payload; char the_char; /* 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) { printf("[%s]mm_init失败[tracenum=%d]\n", __func__, tracenum); 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 */ //printf("ALLOC\n"); /* Call the student's malloc */ if ((p = mm_malloc(size)) == NULL) { printf("[%s]mm_malloc失败[tracenum=%d,i=%d]\n", __func__, tracenum, i); 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; trace->allocated[index] = 1; break; case REALLOC: /* mm_realloc */ //printf("REALLOC\n"); /* Call the student's realloc */ oldp = trace->blocks[index]; if ((newp = (char*)mm_realloc(oldp, size)) == NULL) { printf("[%s]mm_realloc失败[tracenum=%d,i=%d]\n", __func__, tracenum, i); 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 = (int)trace->block_sizes[index]; if (size < oldsize) oldsize = size; the_char = (index & 0xFF); for (j = 0; j < oldsize; j++) { the_char = index & 0xFF; if (newp[j] != the_char) { printf("[%s]mm_realloc没有保存旧块的数据[tracenum = % d, i = % d]\n", __func__, tracenum, i); return 0; } } memset(newp, index & 0xFF, size); /* Remember region */ trace->blocks[index] = newp; trace->block_sizes[index] = size; //printf("\nrealloc finish\n"); break; case HEAPCHECK: //printf("HEAPCHECK\n"); /* check the output of the mm_heapcheck routine */ /* for each allocated block that hasn't been freed yet, the mm_heapcheck function should print a line in the following format request_id payload_size stuff_for_debugging_not_used */ /* redirect stdout to a temporary file */ //fd = open(tmpnam(tmpfile),O_RDWR|O_CREAT); //if (fd < 0) // printf("[%s]不能为heapcheck创建临时文件\n",__func__); //fflush(stdout); //stdoutold = dup(1); //dup2(fd,1); // ///* Call the student's heapcheck */ //mm_heapcheck(); //fflush(stdout); //close(fd); //close(1); // ///* fix stdout */ //dup2(stdoutold, 1); //close(stdoutold); /* parse the output of the heapcheck function */ heapsize = parse_heap(tmpfile, heap, i, tracenum, i); /* initialize trace for scanning */ for (j = 0; j < trace->num_ids; j++) trace->check[j] = 0; for (j = 0; j < heapsize; j++) { request_id = heap[j].request_id; payload = heap[j].payload; if (((request_id) > trace->num_ids) || (trace->allocated[request_id] == 0)) { printf("[%s]%d 不是一个有效的request_id[tracenum=%d,i=%d]\n", __func__, request_id, tracenum, i); expected_blocks(trace); outputted_blocks(heap, heapsize); return 0; } if (trace->allocated[request_id] == 2) { printf("[%s]输出块 %d 已经被释放了[tracenum=%d,i=%d]\n", __func__, request_id, tracenum, i); expected_blocks(trace); outputted_blocks(heap, heapsize); return 0; } if (trace->check[request_id] == 1) { printf("[%s]块 %d 被输出了两次[tracenum=%d,i=%d]\n", __func__, request_id, tracenum, i); expected_blocks(trace); outputted_blocks(heap, heapsize); return 0; } if (trace->block_sizes[request_id] != (size_t)payload) { printf("[%s]块 %d 的大小不对%d, 应该是 %zu[tracenum=%d,i=%d]\n", __func__, request_id, payload, trace->block_sizes[request_id], tracenum, i); expected_blocks(trace); outputted_blocks(heap, heapsize); return 0; } trace->check[request_id] = 1; } for (j = 0; j < trace->num_ids; j++) if ((trace->allocated[j] == 1) && (trace->check[j] == 0)) { printf("[%s]块 %d 不能被heapcheck输出[tracenum=%d,i=%d]\n", __func__, j, tracenum, i); expected_blocks(trace); outputted_blocks(heap, heapsize); return 0; } break; case FREE: /* mm_free */ //printf("FREE\n"); /* Remove region from list and call student's free function */ p = trace->blocks[index]; remove_range(ranges, p); mm_free(p); /* mark the appropriate block as freed */ trace->allocated[index] = 2; break; case ERROR_BUG: break; default: printf("[%s]没有request类型\n", __func__); } } /* 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) printf("[%s]mm_init失败\n", __func__); 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) printf("[%s]mm_malloc失败\n", __func__); /* 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 = (int)trace->block_sizes[index]; oldp = trace->blocks[index]; if ((newp = (char*)mm_realloc(oldp, newsize)) == NULL) printf("[%s]mm_realloc失败\n", __func__); /* 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 HEAPCHECK: break; case FREE: /* mm_free */ index = trace->ops[i].index; size = (int)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; case ERROR_BUG: break; default: printf("[%s]没有request类型\n", __func__); } } 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) printf("[%s]mm_init失败\n", __func__); /* 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) printf("[%s]mm_malloc失败\n", __func__); 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 = (char*)mm_realloc(oldp, newsize)) == NULL) printf("[%s]mm_realloc失败\n", __func__); trace->blocks[index] = newp; break; case HEAPCHECK: break; case FREE: /* mm_free */ index = trace->ops[i].index; block = trace->blocks[index]; mm_free(block); break; case ERROR_BUG: break; default: printf("[%s]没有request类型\n", __func__); } } /* * 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) { printf("[%s]libc malloc失败[tracenum=%d,i=%d]\n", __func__, tracenum, i); } 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 = (char*)realloc(oldp, newsize)) == NULL) { printf("[%s]libc realloc失败[tracenum=%d,i=%d]\n", __func__, tracenum, i); } trace->blocks[trace->ops[i].index] = newp; break; case HEAPCHECK: break; case FREE: /* free */ free(trace->blocks[trace->ops[i].index]); break; case ERROR_BUG: break; default: printf("[%s]无效的操作类型[%d][tracenum=%d,i=%d]\n", __func__, trace->ops[i].type, tracenum, i); } } 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) printf("[%s]malloc失败\n", __func__); trace->blocks[index] = p; break; case REALLOC: /* realloc */ index = trace->ops[i].index; newsize = trace->ops[i].size; oldp = trace->blocks[index]; if ((newp = (char*)realloc(oldp, newsize)) == NULL) printf("[%s]realloc失败\n", __func__); trace->blocks[index] = newp; break; case HEAPCHECK: break; case FREE: /* free */ index = trace->ops[i].index; block = trace->blocks[index]; free(block); break; case ERROR_BUG: break; } } } unsigned int rand1_h, rand1_l, rand_div; /* 产生一个0~divv-1之间的随机数,同时更新随机数种子 */ void GenerateRandomNumber(unsigned int divv) { unsigned long long x = rand1_h; x *= 0x6AC690C5; x += rand1_l; rand1_h = (unsigned int)x; rand1_l = (unsigned int)(x >> 32); if (divv == 0) return; rand_div = rand1_h % divv; } #define MAX_BENCHMARK_MALLOC_TIMES 10000 struct ref_malloc_trace_struct { int index; int size; char* p; char op; } ref_malloc_trace[2 * MAX_BENCHMARK_MALLOC_TIMES]; /* * eval_libc_speed_ref_prepare - This is the function that is used by fcyc() to * measure the running time of the libc malloc package on the set * of traces. */ void eval_libc_speed_ref_prepare(void) { int i, malloc_count, free_count, j, last_free_index; char free_flag[MAX_BENCHMARK_MALLOC_TIMES]; malloc_count = 0; free_count = 0; last_free_index = 0; rand1_h = 0x11223344; rand1_l = 0x55667788; GenerateRandomNumber(0); memset(ref_malloc_trace, 0, sizeof(ref_malloc_trace)); memset(free_flag, 0, sizeof(free_flag)); i = 0; while (1) { GenerateRandomNumber(10); //printf("rand_div=%d malloc_count=%d free_count=%d\n",rand_div,malloc_count,free_count); if (rand_div >= 6 && malloc_count < MAX_BENCHMARK_MALLOC_TIMES) { // malloc ref_malloc_trace[i].index = malloc_count; ref_malloc_trace[i].op = 10; free_flag[malloc_count] = 10; GenerateRandomNumber(100000); ref_malloc_trace[i].size = rand_div + 1; //printf("a index=%02d size=%d\n",malloc_count,rand_div+1); malloc_count++; i++; } if (rand_div <= 4 && free_count < MAX_BENCHMARK_MALLOC_TIMES) { // free for (j = last_free_index; j < malloc_count; j++) if (free_flag[j] == 10) { //printf("f index=%02d\n",j); last_free_index = j; ref_malloc_trace[i].index = j; ref_malloc_trace[i].op = 20; free_flag[j] = 20; i++; free_count++; break; } } if (malloc_count == MAX_BENCHMARK_MALLOC_TIMES && free_count == MAX_BENCHMARK_MALLOC_TIMES) break; } } void eval_libc_speed_ref(void* ptr) { int i; for (i = 0; i < 2 * MAX_BENCHMARK_MALLOC_TIMES; i++) { if (ref_malloc_trace[i].op == 10) { //printf("%02d a %d\n",ref_malloc_trace[i].index,ref_malloc_trace[i].size); ref_malloc_trace[ref_malloc_trace[i].index].p = (char*)malloc(ref_malloc_trace[i].size); } if (ref_malloc_trace[i].op == 20) { //printf("%02d f\n",ref_malloc_trace[i].index); free(ref_malloc_trace[ref_malloc_trace[i].index].p); } } } /************************************* * Some miscellaneous helper routines ************************************/ /* * printresults - prints a performance summary for some malloc package */ static void printresults(int n, stats_t* stats) { int i; double secs = 0; double ops = 0; double util = 0; /* Print the individual results for each trace */ printf("[%s] +---------------------------------------------------------+\n", __func__); printf("[%s] | trace | 正确性 | 效率 | 操作次数 | 时间(毫秒) | Kops |\n", __func__); printf("[%s] +---------------------------------------------------------+\n", __func__); for (i = 0; i < n; i++) { if (stats[i].valid) { printf("[%s] | %5d | %6s | %3.0f%% | %8.0f | %10.6f | %6.0f |\n", __func__, i, "yes", stats[i].util * 100.0, stats[i].ops, stats[i].secs * 1000, (stats[i].ops / 1e3) / stats[i].secs); secs += stats[i].secs; ops += stats[i].ops; util += stats[i].util; } else { printf("[%s] | %5d | no | - | - | - | - |\n", __func__, i); } } /* Print the aggregate results for the set of traces */ if (errors == 0) { printf("[%s] | 累计 | | %3.0f%% | %8.0f | %10.6f | %6.0f |\n\n", __func__, (util / n) * 100.0, ops, secs * 1000, (ops / 1e3) / secs); } else { printf("%12s%6s%10s%12s%6s\n", "累计 ", "-", "-", "-", "-"); } } /* * parse_heapcheck - Parse the output of the heapcheck into an internal data structure * returns -1 on error, 0 on success */ static int parse_heap(char* file, block_t* heap, int max_blocks, int tracenum, int opnum) { FILE* fp; char line[MAXLINE]; char str[MAXLINE]; int i = 0; int request_id; int payload; fp = fopen(file, "r"); if (fp == NULL) printf("[%s]从临时文件中读取失败\n", __func__); while ((fgets(line, MAXLINE, fp) != NULL)) { if (sscanf(line, "%s%d%d", str, &request_id, &payload) == 3) if (!strcmp(str, "$BLOCK")) { /* found a block */ if (i < MAXHEAP) { heap[i].request_id = request_id; heap[i].payload = payload; i++; } } } fclose(fp); unlink(file); return i; } /* * Functions to print the expected output and the user input for heapcheck */ static void expected_blocks(trace_t* trace) { int active = 0; int j; printf("[%s]期待从HeapCheck中的输出:\n", __func__); for (j = 0; j < trace->num_ids; j++) if (trace->allocated[j] == 1) { active++; printf("$BLOCK %d %zu\n", j, trace->block_sizes[j]); } printf("[%s]真正有效的块数 = %d\n", __func__, active); } static void outputted_blocks(block_t* heap, int heapsize) { int i; printf("[%s]mm_heapcheck产生的输出\n", __func__); for (i = 0; i < heapsize; i++) { printf("[%s]$BLOCK %d %d\n", __func__, heap[i].request_id, heap[i].payload); } printf("[%s]输出有效的块数 = %d\n", __func__, heapsize); } /* * usage - Explain the command line arguments */ static void usage(char* exec) { // fprintf(stderr, "Usage: %s [-hvVal] [-f ] [-t ]\n", exec); fprintf(stderr, "Usage: %s [-hvVl] [-f ] [-t ]\n", exec); fprintf(stderr, "Options\n"); // fprintf(stderr, "\t-a 不检查团队信息.\n"); fprintf(stderr, "\t-f 使用作为trace文件.\n"); fprintf(stderr, "\t-g 为自动评分创建一个摘要信息.\n"); fprintf(stderr, "\t-h 打印此信息.\n"); fprintf(stderr, "\t-l 运行libc malloc的检测.\n"); fprintf(stderr, "\t-t 缺省trace存放的目录.\n"); fprintf(stderr, "\t-v 打印每条trace的性能.\n"); fprintf(stderr, "\t-V 打印额外的调试信息.\n"); }