You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1017 lines
29 KiB

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