/*
|
|
* test-trans.c - Checks the correctness and performance of all of the
|
|
* student's transpose functions and records the results for their
|
|
* official submitted version as well.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <getopt.h>
|
|
#include <sys/types.h>
|
|
#include "cachelab.h"
|
|
#include <sys/wait.h> // fir WEXITSTATUS
|
|
#include <limits.h> // for INT_MAX
|
|
|
|
/* Maximum array dimension */
|
|
#define MAXN 256
|
|
|
|
/* The description string for the transpose_submit() function that the
|
|
student submits for credit */
|
|
#define SUBMIT_DESCRIPTION "Transpose submission"
|
|
|
|
/* External function defined in trans.c */
|
|
extern void registerFunctions();
|
|
|
|
/* External variables defined in cachelab-tools.c */
|
|
extern trans_func_t func_list[MAX_TRANS_FUNCS];
|
|
extern int func_counter;
|
|
|
|
/* Globals set on the command line */
|
|
static int M = 0;
|
|
static int N = 0;
|
|
|
|
/* The correctness and performance for the submitted transpose function */
|
|
struct results {
|
|
int funcid;
|
|
int correct;
|
|
int misses;
|
|
};
|
|
static struct results results = {-1, 0, INT_MAX};
|
|
|
|
/*
|
|
* eval_perf - Evaluate the performance of the registered transpose functions
|
|
*/
|
|
void eval_perf(unsigned int s, unsigned int E, unsigned int b)
|
|
{
|
|
int i,flag;
|
|
unsigned int len, hits, misses, evictions;
|
|
unsigned long long int marker_start, marker_end, addr;
|
|
char buf[1000], cmd[255];
|
|
char filename[128];
|
|
|
|
registerFunctions();
|
|
|
|
/* Open the complete trace file */
|
|
FILE* full_trace_fp;
|
|
FILE* part_trace_fp;
|
|
|
|
/* Evaluate the performance of each registered transpose function */
|
|
|
|
for (i=0; i<func_counter; i++) {
|
|
if (strcmp(func_list[i].description, SUBMIT_DESCRIPTION) == 0 )
|
|
results.funcid = i; /* remember which function is the submission */
|
|
|
|
|
|
printf("\nFunction %d (%d total)\nStep 1: Validating and generating memory traces\n",i,func_counter);
|
|
/* Use valgrind to generate the trace */
|
|
|
|
sprintf(cmd, "valgrind --tool=lackey --trace-mem=yes --log-fd=1 -v ./tracegen -M %d -N %d -F %d > trace.tmp", M, N,i);
|
|
flag=WEXITSTATUS(system(cmd));
|
|
if (0!=flag) {
|
|
printf("Validation error at function %d! Run ./tracegen -M %d -N %d -F %d for details.\nSkipping performance evaluation for this function.\n",flag-1,M,N,i);
|
|
continue;
|
|
}
|
|
|
|
/* Get the start and end marker addresses */
|
|
FILE* marker_fp = fopen(".marker", "r");
|
|
assert(marker_fp);
|
|
fscanf(marker_fp, "%llx %llx", &marker_start, &marker_end);
|
|
fclose(marker_fp);
|
|
|
|
|
|
func_list[i].correct=1;
|
|
|
|
/* Save the correctness of the transpose submission */
|
|
if (results.funcid == i ) {
|
|
results.correct = 1;
|
|
}
|
|
|
|
full_trace_fp = fopen("trace.tmp", "r");
|
|
assert(full_trace_fp);
|
|
|
|
|
|
/* Filtered trace for each transpose function goes in a separate file */
|
|
sprintf(filename, "trace.f%d", i);
|
|
part_trace_fp = fopen(filename, "w");
|
|
assert(part_trace_fp);
|
|
|
|
/* Locate trace corresponding to the trans function */
|
|
flag = 0;
|
|
while (fgets(buf, 1000, full_trace_fp) != NULL) {
|
|
|
|
/* We are only interested in memory access instructions */
|
|
if (buf[0]==' ' && buf[2]==' ' &&
|
|
(buf[1]=='S' || buf[1]=='M' || buf[1]=='L' )) {
|
|
sscanf(buf+3, "%llx,%u", &addr, &len);
|
|
|
|
/* If start marker found, set flag */
|
|
if (addr == marker_start)
|
|
flag = 1;
|
|
|
|
/* Valgrind creates many spurious accesses to the
|
|
stack that have nothing to do with the students
|
|
code. At the moment, we are ignoring all stack
|
|
accesses by using the simple filter of recording
|
|
accesses to only the low 32-bit portion of the
|
|
address space. At some point it would be nice to
|
|
try to do more informed filtering so that would
|
|
eliminate the valgrind stack references while
|
|
include the student stack references. */
|
|
if (flag && addr < 0xffffffff) {
|
|
fputs(buf, part_trace_fp);
|
|
}
|
|
|
|
/* if end marker found, close trace file */
|
|
if (addr == marker_end) {
|
|
flag = 0;
|
|
fclose(part_trace_fp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose(full_trace_fp);
|
|
|
|
/* Run the reference simulator */
|
|
printf("Step 2: Evaluating performance (s=%d, E=%d, b=%d)\n", s, E, b);
|
|
char cmd[255];
|
|
sprintf(cmd, "./csim-ref -s %u -E %u -b %u -t trace.f%d > /dev/null",
|
|
s, E, b, i);
|
|
system(cmd);
|
|
|
|
/* Collect results from the reference simulator */
|
|
FILE* in_fp = fopen(".csim_results","r");
|
|
assert(in_fp);
|
|
fscanf(in_fp, "%u %u %u", &hits, &misses, &evictions);
|
|
fclose(in_fp);
|
|
func_list[i].num_hits = hits;
|
|
func_list[i].num_misses = misses;
|
|
func_list[i].num_evictions = evictions;
|
|
printf("func %u (%s): hits:%u, misses:%u, evictions:%u\n",
|
|
i, func_list[i].description, hits, misses, evictions);
|
|
|
|
/* If it is transpose_submit(), record number of misses */
|
|
if (results.funcid == i) {
|
|
results.misses = misses;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* usage - Print usage info
|
|
*/
|
|
void usage(char *argv[]){
|
|
printf("Usage: %s [-h] -M <rows> -N <cols>\n", argv[0]);
|
|
printf("Options:\n");
|
|
printf(" -h Print this help message.\n");
|
|
printf(" -M <rows> Number of matrix rows (max %d)\n", MAXN);
|
|
printf(" -N <cols> Number of matrix columns (max %d)\n", MAXN);
|
|
printf("Example: %s -M 8 -N 8\n", argv[0]);
|
|
}
|
|
|
|
/*
|
|
* sigsegv_handler - SIGSEGV handler
|
|
*/
|
|
void sigsegv_handler(int signum){
|
|
printf("Error: Segmentation Fault.\n");
|
|
printf("TEST_TRANS_RESULTS=0:0\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* sigalrm_handler - SIGALRM handler
|
|
*/
|
|
void sigalrm_handler(int signum){
|
|
printf("Error: Program timed out.\n");
|
|
printf("TEST_TRANS_RESULTS=0:0\n");
|
|
fflush(stdout);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* main - Main routine
|
|
*/
|
|
int main(int argc, char* argv[])
|
|
{
|
|
char c;
|
|
|
|
while ((c = getopt(argc,argv,"M:N:h")) != -1) {
|
|
switch(c) {
|
|
case 'M':
|
|
M = atoi(optarg);
|
|
break;
|
|
case 'N':
|
|
N = atoi(optarg);
|
|
break;
|
|
case 'h':
|
|
usage(argv);
|
|
exit(0);
|
|
default:
|
|
usage(argv);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (M == 0 || N == 0) {
|
|
printf("Error: Missing required argument\n");
|
|
usage(argv);
|
|
exit(1);
|
|
}
|
|
|
|
if (M > MAXN || N > MAXN) {
|
|
printf("Error: M or N exceeds %d\n", MAXN);
|
|
usage(argv);
|
|
exit(1);
|
|
}
|
|
|
|
/* Install SIGSEGV and SIGALRM handlers */
|
|
if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
|
|
fprintf(stderr, "Unable to install SIGALRM handler\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {
|
|
fprintf(stderr, "Unable to install SIGALRM handler\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Time out and give up after a while */
|
|
alarm(120);
|
|
|
|
/* Check the performance of the student's transpose function */
|
|
eval_perf(5, 1, 5);
|
|
|
|
/* Emit the results for this particular test */
|
|
if (results.funcid == -1) {
|
|
printf("\nError: We could not find your transpose_submit() function\n");
|
|
printf("Error: Please ensure that description field is exactly \"%s\"\n",
|
|
SUBMIT_DESCRIPTION);
|
|
printf("\nTEST_TRANS_RESULTS=0:0\n");
|
|
}
|
|
else {
|
|
printf("\nSummary for official submission (func %d): correctness=%d misses=%d\n",
|
|
results.funcid, results.correct, results.misses);
|
|
printf("\nTEST_TRANS_RESULTS=%d:%d\n", results.correct, results.misses);
|
|
}
|
|
return 0;
|
|
}
|