From 542c9b7dd400e7adc418dbe0143a49dee86cd1be Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Fri, 21 Sep 2018 05:36:18 +0800 Subject: [PATCH] Initial import from CMU CS213 C Programming Lab: Assessing Your C Programming Skills --- .clang-format | 15 + Makefile | 22 ++ README | 51 +++ console.c | 645 +++++++++++++++++++++++++++++++++++++ console.h | 104 ++++++ driver.py | 151 +++++++++ harness.c | 281 ++++++++++++++++ harness.h | 60 ++++ qtest.c | 576 +++++++++++++++++++++++++++++++++ queue.c | 111 +++++++ queue.h | 90 ++++++ report.c | 380 ++++++++++++++++++++++ report.h | 112 +++++++ traces/trace-01-ops.cmd | 11 + traces/trace-02-ops.cmd | 16 + traces/trace-03-ops.cmd | 24 ++ traces/trace-04-ops.cmd | 18 ++ traces/trace-05-ops.cmd | 23 ++ traces/trace-06-string.cmd | 30 ++ traces/trace-07-robust.cmd | 9 + traces/trace-08-robust.cmd | 7 + traces/trace-09-robust.cmd | 7 + traces/trace-10-malloc.cmd | 11 + traces/trace-11-malloc.cmd | 9 + traces/trace-12-malloc.cmd | 10 + traces/trace-13-perf.cmd | 8 + traces/trace-14-perf.cmd | 7 + traces/trace-15-perf.cmd | 11 + traces/trace-eg.cmd | 21 ++ 29 files changed, 2820 insertions(+) create mode 100644 .clang-format create mode 100644 Makefile create mode 100644 README create mode 100644 console.c create mode 100644 console.h create mode 100755 driver.py create mode 100644 harness.c create mode 100644 harness.h create mode 100644 qtest.c create mode 100644 queue.c create mode 100644 queue.h create mode 100644 report.c create mode 100644 report.h create mode 100644 traces/trace-01-ops.cmd create mode 100644 traces/trace-02-ops.cmd create mode 100644 traces/trace-03-ops.cmd create mode 100644 traces/trace-04-ops.cmd create mode 100644 traces/trace-05-ops.cmd create mode 100644 traces/trace-06-string.cmd create mode 100644 traces/trace-07-robust.cmd create mode 100644 traces/trace-08-robust.cmd create mode 100644 traces/trace-09-robust.cmd create mode 100644 traces/trace-10-malloc.cmd create mode 100644 traces/trace-11-malloc.cmd create mode 100644 traces/trace-12-malloc.cmd create mode 100644 traces/trace-13-perf.cmd create mode 100644 traces/trace-14-perf.cmd create mode 100644 traces/trace-15-perf.cmd create mode 100644 traces/trace-eg.cmd diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..9705d27f7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +BasedOnStyle: Chromium +Language: Cpp +MaxEmptyLinesToKeep: 3 +IndentCaseLabels: false +AllowShortIfStatementsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +DerivePointerAlignment: false +PointerAlignment: Right +SpaceAfterCStyleCast: true +TabWidth: 4 +UseTab: Never +IndentWidth: 4 +BreakBeforeBraces: Linux +AccessModifierOffset: -4 diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..a8aef756a --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +CC = gcc +CFLAGS = -O0 -g -Wall -Werror + +all: qtest + -tar -cf handin.tar queue.c queue.h + +queue.o: queue.c queue.h harness.h + $(CC) $(CFLAGS) -c queue.c + +qtest: qtest.c report.c console.c harness.c queue.o + $(CC) $(CFLAGS) -o qtest qtest.c report.c console.c harness.c queue.o + tar cf handin.tar queue.c queue.h + +test: qtest driver.py + chmod +x driver.py + ./driver.py + +clean: + rm -f *.o *~ qtest + rm -rf *.dSYM + (cd traces; rm -f *~) + diff --git a/README b/README new file mode 100644 index 000000000..21112ab30 --- /dev/null +++ b/README @@ -0,0 +1,51 @@ +This is the handout directory for the 15-213 C Lab. + +************************ +Running the autograders: +************************ + +Before running the autograders, compile your code to create the testing program qtest + linux> make + +Check the correctness of your code: + linux> make test + +****** +Using qtest: +****** + +qtest provides a command interpreter that can create and manipulate queues. + +Run ./qtest -h to see the list of command-line options + +When you execute ./qtest, it will give a command prompt "cmd>". Type +"help" to see a list of available commands + + +****** +Files: +****** + +# You will handing in these two files +queue.h Modified version of declarations including new fields you want to introduce +queue.c Modified version of queue code to fix deficiencies of original code + +# Tools for evaluating your queue code +Makefile Builds the evaluation program qtest +README This file +driver.py* The C lab driver program, runs qtest on a standard set of traces + +# Helper files + +console.{c,h}: Implements command-line interpreter for qtest +report.{c,h}: Implements printing of information at different levels of verbosity +harness.{c,h}: Customized version of malloc and free to provide rigorous testing framework +qtest.c Code for qtest + +# Trace files + +traces/trace-XX-CAT.cmd Trace files used by the driver. These are input files for qtest. + They are short and simple. We encourage to study them to see what tests are being performed. + XX is the trace number (1-15). CAT describes the general nature of the test. + +traces/trace-eg.cmd: A simple, documented trace file to demonstrate the operation of qtest diff --git a/console.c b/console.c new file mode 100644 index 000000000..9682e4b4d --- /dev/null +++ b/console.c @@ -0,0 +1,645 @@ +/* Implementation of simple command-line interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "report.h" +#include "console.h" + + +/* Some global values */ +static cmd_ptr cmd_list = NULL; +static param_ptr param_list = NULL; +static bool block_flag = false; +static bool prompt_flag = true; + +/* Am I timing a command that has the console blocked? */ +static bool block_timing = false; + +/* Time of day */ +static double first_time; +static double last_time; + +/* + Implement buffered I/O using variant of RIO package from CS:APP + Must create stack of buffers to handle I/O with nested source commands. +*/ + +#define RIO_BUFSIZE 8192 +typedef struct RIO_ELE rio_t, *rio_ptr; + +struct RIO_ELE { + int fd; /* File descriptor */ + int cnt; /* Unread bytes in internal buffer */ + char *bufptr; /* Next unread byte in internal buffer */ + char buf[RIO_BUFSIZE]; /* Internal buffer */ + rio_ptr prev; /* Next element in stack */ +}; + +rio_ptr buf_stack; +char linebuf[RIO_BUFSIZE]; + +/* Maximum file descriptor */ +int fd_max = 0; + +/* Parameters */ +static int err_limit = 5; +static int err_cnt = 0; +static int echo = 0; + +static bool quit_flag = false; +static char *prompt = "cmd>"; + + +/* Optional function to call as part of exit process */ +/* Maximum number of quit functions */ + +#define MAXQUIT 10 +static cmd_function quit_helpers[MAXQUIT]; +static int quit_helper_cnt = 0; + +bool do_quit_cmd(int argc, char *argv[]); +bool do_help_cmd(int argc, char *argv[]); +bool do_option_cmd(int argc, char *argv[]); +bool do_source_cmd(int argc, char *argv[]); +bool do_log_cmd(int argc, char *argv[]); +bool do_time_cmd(int argc, char *argv[]); +bool do_comment_cmd(int argc, char *argv[]); + +static void init_in(); + +static bool push_file(char *fname); +static void pop_file(); + +static bool interpret_cmda(int argc, char *argv[]); + +/* Initialize interpreter */ +void init_cmd() +{ + cmd_list = NULL; + param_list = NULL; + err_cnt = 0; + quit_flag = false; + add_cmd("help", do_help_cmd, " | Show documentation"); + add_cmd("option", do_option_cmd, + " [name val] | Display or set options"); + add_cmd("quit", do_quit_cmd, " | Exit program"); + add_cmd("source", do_source_cmd, + " file | Read commands from source file"); + add_cmd("log", do_log_cmd, " file | Copy output to file"); + add_cmd("time", do_time_cmd, " cmd arg ... | Time command execution"); + add_cmd("#", do_comment_cmd, " ... | Display comment"); + add_param("verbose", &verblevel, "Verbosity level", NULL); + add_param("error", &err_limit, "Number of errors until exit", NULL); + add_param("echo", &echo, "Do/don't echo commands", NULL); +#if 0 + add_param("megabytes", &mblimit, "Maximum megabytes allowed", NULL); + add_param("seconds", &timelimit, "Maximum seconds allowed", + change_timeout); +#endif + init_in(); + init_time(&last_time); + first_time = last_time; +} + +/* Add a new command */ +void add_cmd(char *name, cmd_function operation, char *documentation) +{ + cmd_ptr next_cmd = cmd_list; + cmd_ptr *last_loc = &cmd_list; + while (next_cmd && strcmp(name, next_cmd->name) > 0) { + last_loc = &next_cmd->next; + next_cmd = next_cmd->next; + } + cmd_ptr ele = (cmd_ptr) malloc_or_fail(sizeof(cmd_ele), "add_cmd"); + ele->name = name; + ele->operation = operation; + ele->documentation = documentation; + ele->next = next_cmd; + *last_loc = ele; +} + +/* Add a new parameter */ +void add_param(char *name, + int *valp, + char *documentation, + setter_function setter) +{ + param_ptr next_param = param_list; + param_ptr *last_loc = ¶m_list; + while (next_param && strcmp(name, next_param->name) > 0) { + last_loc = &next_param->next; + next_param = next_param->next; + } + param_ptr ele = (param_ptr) malloc_or_fail(sizeof(param_ele), "add_param"); + ele->name = name; + ele->valp = valp; + ele->documentation = documentation; + ele->setter = setter; + ele->next = next_param; + *last_loc = ele; +} + + +/* Parse a string into a command line */ +char **parse_args(char *line, int *argcp) +{ + /* + Must first determine how many arguments there are. + Replace all white space with null characters + */ + size_t len = strlen(line); + /* First copy into buffer with each substring null-terminated */ + char *buf = malloc_or_fail(len + 1, "parse_args"); + char *src = line; + char *dst = buf; + bool skipping = true; + int c; + int argc = 0; + while ((c = *src++) != '\0') { + if (isspace(c)) { + if (!skipping) { + /* Hit end of word */ + *dst++ = '\0'; + skipping = true; + } + } else { + if (skipping) { + /* Hit start of new word */ + argc++; + skipping = false; + } + *dst++ = c; + } + } + /* Now assemble into array of strings */ + char **argv = calloc_or_fail(argc, sizeof(char *), "parse_args"); + size_t i; + src = buf; + for (i = 0; i < argc; i++) { + argv[i] = strsave_or_fail(src, "parse_args"); + src += strlen(argv[i]) + 1; + } + free_block(buf, len + 1); + *argcp = argc; + return argv; +} + +void record_error() +{ + err_cnt++; + if (err_cnt >= err_limit) { + report(1, "Error limit exceeded. Stopping command execution"); + quit_flag = true; + } +} + +/* Execute a command that has already been split into arguments */ +static bool interpret_cmda(int argc, char *argv[]) +{ + if (argc == 0) + return true; + /* Try to find matching command */ + cmd_ptr next_cmd = cmd_list; + bool ok = true; + while (next_cmd && strcmp(argv[0], next_cmd->name) != 0) + next_cmd = next_cmd->next; + if (next_cmd) { + ok = next_cmd->operation(argc, argv); + if (!ok) + record_error(); + } else { + report(1, "Unknown command '%s'", argv[0]); + record_error(); + ok = false; + } + return ok; +} + +/* Execute a command from a command line */ +bool interpret_cmd(char *cmdline) +{ + int argc; + if (quit_flag) + return false; +#if RPT >= 6 + report(6, "Interpreting command '%s'\n", cmdline); +#endif + char **argv = parse_args(cmdline, &argc); + bool ok = interpret_cmda(argc, argv); + int i; + for (i = 0; i < argc; i++) + free_string(argv[i]); + free_array(argv, argc, sizeof(char *)); + return ok; +} + +/* Set function to be executed as part of program exit */ +void add_quit_helper(cmd_function qf) +{ + if (quit_helper_cnt < MAXQUIT) { + quit_helpers[quit_helper_cnt++] = qf; + } else + report_event(MSG_FATAL, "Exceeded limit on quit helpers"); +} + +/* Set prompt string */ +void set_prompt(char *p) +{ + prompt = p; +} + +/* Turn echoing on/off */ +void set_echo(bool on) +{ + echo = on ? 1 : 0; +} + + +/* Built-in commands */ +bool do_quit_cmd(int argc, char *argv[]) +{ + cmd_ptr c = cmd_list; + bool ok = true; + while (c) { + cmd_ptr ele = c; + c = c->next; + free_block(ele, sizeof(cmd_ele)); + } + param_ptr p = param_list; + while (p) { + param_ptr ele = p; + p = p->next; + free_block(ele, sizeof(param_ele)); + } + while (buf_stack) + pop_file(); + int i; + for (i = 0; i < quit_helper_cnt; i++) { + ok = ok && quit_helpers[i](argc, argv); + } + quit_flag = true; + return ok; +} + +bool do_help_cmd(int argc, char *argv[]) +{ + cmd_ptr clist = cmd_list; + report(1, "Commands:", argv[0]); + while (clist) { + report(1, "\t%s\t%s", clist->name, clist->documentation); + clist = clist->next; + } + param_ptr plist = param_list; + report(1, "Options:"); + while (plist) { + report(1, "\t%s\t%d\t%s", plist->name, *plist->valp, + plist->documentation); + plist = plist->next; + } + return true; +} + +bool do_comment_cmd(int argc, char *argv[]) +{ + int i; + if (echo) + return true; + for (i = 0; i < argc - 1; i++) { + report_noreturn(1, "%s ", argv[i]); + } + if (i < argc) { + report(1, "%s", argv[i]); + } + return true; +} + +/* Extract integer from text and store at loc */ +bool get_int(char *vname, int *loc) +{ + char *end = NULL; + long int v = strtol(vname, &end, 0); + if (v == LONG_MIN || *end != '\0') + return false; + *loc = (int) v; + return true; +} + +bool do_option_cmd(int argc, char *argv[]) +{ + size_t i; + if (argc == 1) { + param_ptr plist = param_list; + report(1, "Options:"); + while (plist) { + report(1, "\t%s\t%d\t%s", plist->name, *plist->valp, + plist->documentation); + plist = plist->next; + } + return true; + } + for (i = 1; i < argc; i++) { + char *name = argv[i]; + int value = 0; + bool found = false; + /* Get value from next argument */ + if (i + 1 >= argc) { + report(1, "No value given for parameter %s", name); + return false; + } else if (!get_int(argv[++i], &value)) { + report(1, "Cannot parse '%s' as integer", argv[i]); + return false; + } + /* Find parameter in list */ + param_ptr plist = param_list; + while (!found && plist) { + if (strcmp(plist->name, name) == 0) { + int oldval = *plist->valp; + *plist->valp = value; + if (plist->setter) + plist->setter(oldval); + found = true; + } else + plist = plist->next; + } + /* Didn't find parameter */ + if (!found) { + report(1, "Unknown parameter '%s'", name); + return false; + } + } + return true; +} + +bool do_source_cmd(int argc, char *argv[]) +{ + if (argc < 2) { + report(1, "No source file given"); + return false; + } + if (!push_file(argv[1])) { + report(1, "Could not open source file '%s'", argv[1]); + return false; + } + return true; +} + +bool do_log_cmd(int argc, char *argv[]) +{ + if (argc < 2) { + report(1, "No log file given"); + return false; + } + bool result = set_logfile(argv[1]); + if (!result) { + report(1, "Couldn't open log file '%s'", argv[1]); + } + return result; +} + +bool do_time_cmd(int argc, char *argv[]) +{ + double delta = delta_time(&last_time); + bool ok = true; + if (argc <= 1) { + double elapsed = last_time - first_time; + report(1, "Elapsed time = %.3f, Delta time = %.3f", elapsed, delta); + } else { + ok = interpret_cmda(argc - 1, argv + 1); + if (block_flag) { + block_timing = true; + } else { + delta = delta_time(&last_time); + report(1, "Delta time = %.3f", delta); + } + } + return ok; +} + +/* Create new buffer for named file. + Name == NULL for stdin. + Return true if successful. +*/ +static bool push_file(char *fname) +{ + int fd = fname ? open(fname, O_RDONLY) : STDIN_FILENO; + if (fd < 0) + return false; + if (fd > fd_max) + fd_max = fd; + rio_ptr rnew = malloc_or_fail(sizeof(rio_t), "push_file"); + rnew->fd = fd; + rnew->cnt = 0; + rnew->bufptr = rnew->buf; + rnew->prev = buf_stack; + buf_stack = rnew; + return true; +} + +/* Pop a file buffer from stack. + Return true if stack is now empty +*/ +static void pop_file() +{ + if (buf_stack) { + rio_ptr rsave = buf_stack; + buf_stack = rsave->prev; + close(rsave->fd); + free_block(rsave, sizeof(rio_t)); + } +} + + +/* Handling of input */ +static void init_in() +{ + buf_stack = NULL; +} + +/* Read command from input file. + When hit EOF, close that file and return NULL +*/ +static char *readline() +{ + int cnt; + char c; + char *lptr = linebuf; + + if (buf_stack == NULL) + return NULL; + + for (cnt = 0; cnt < RIO_BUFSIZE - 2; cnt++) { + if (buf_stack->cnt <= 0) { + /* Need to read from input file */ + buf_stack->cnt = read(buf_stack->fd, buf_stack->buf, RIO_BUFSIZE); + buf_stack->bufptr = buf_stack->buf; + if (buf_stack->cnt <= 0) { + /* Encountered EOF */ + pop_file(); + if (cnt > 0) { + /* Last line of file did not terminate with newline. */ + /* Terminate line & return it */ + *lptr++ = '\n'; + *lptr++ = '\0'; + if (echo) { + report_noreturn(1, prompt); + report_noreturn(1, linebuf); + } + return linebuf; + } else + return NULL; + } + } + /* Have text in buffer */ + c = *buf_stack->bufptr++; + *lptr++ = c; + buf_stack->cnt--; + if (c == '\n') + break; + } + if (c != '\n') { + /* Hit buffer limit. Artificially terminate line */ + *lptr++ = '\n'; + } + *lptr++ = '\0'; + if (echo) { + report_noreturn(1, prompt); + report_noreturn(1, linebuf); + } + return linebuf; +} + + +void block_console() +{ + block_flag = true; +} + +void unblock_console() +{ + block_flag = false; + if (block_timing) { + double delta = delta_time(&last_time); + report(1, "Delta time = %.3f", delta); + } + block_timing = false; +} + + +/* Determine if there is a complete command line in input buffer */ +static bool read_ready() +{ + int i; + for (i = 0; buf_stack && i < buf_stack->cnt; i++) { + if (buf_stack->bufptr[i] == '\n') + return true; + } + return false; +} + +/* + Handle command processing in program that uses select as main control loop. + Like select, but checks whether command input either present in internal + buffer + or readable from command input. If so, that command is executed. + Same return as select. Command input file removed from readfds + + nfds should be set to the maximum file descriptor for network sockets. + If nfds == 0, this indicates that there is no pending network activity +*/ + +int cmd_select(int nfds, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout) +{ + char *cmdline; + int infd; + fd_set local_readset; + while (!block_flag && read_ready()) { + cmdline = readline(); + interpret_cmd(cmdline); + prompt_flag = true; + } + if (cmd_done()) + return 0; + if (!block_flag) { + /* Process any commands in input buffer */ + if (readfds == NULL) + readfds = &local_readset; + /* Add input fd to readset for select */ + infd = buf_stack->fd; + FD_SET(infd, readfds); + if (infd == STDIN_FILENO && prompt_flag) { + printf("%s", prompt); + fflush(stdout); + prompt_flag = true; + } + if (infd >= nfds) { + nfds = infd + 1; + } + } + if (nfds == 0) + return 0; + int result = select(nfds, readfds, writefds, exceptfds, timeout); + if (result <= 0) + return result; + infd = buf_stack->fd; + if (readfds && FD_ISSET(infd, readfds)) { + /* Commandline input available */ + FD_CLR(infd, readfds); + result--; + cmdline = readline(); + if (cmdline) + interpret_cmd(cmdline); + } + return result; +} + + +bool start_cmd(char *infile_name) +{ + bool ok = push_file(infile_name); + if (!ok) + report(1, "Could not open source file '%s'", + infile_name ? infile_name : "standard input"); + return ok; +} + +bool cmd_done() +{ + return buf_stack == NULL || quit_flag; +} + + +bool finish_cmd() +{ + bool ok = true; + if (!quit_flag) { + ok = ok && do_quit_cmd(0, NULL); + } + return ok && err_cnt == 0; +} + +bool run_console(char *infile_name) +{ + if (!push_file(infile_name)) { + report(1, "ERROR: Could not open source file '%s'", infile_name); + return false; + } + while (!cmd_done()) { + cmd_select(0, NULL, NULL, NULL, NULL); + } + return err_cnt == 0; +} diff --git a/console.h b/console.h new file mode 100644 index 000000000..8ed2f5981 --- /dev/null +++ b/console.h @@ -0,0 +1,104 @@ +/* Implementation of simple command-line interface */ + +/* Each command defined in terms of a function */ +typedef bool (*cmd_function)(int argc, char *argv[]); + +/* Information about each command */ +/* Organized as linked list in alphabetical order */ +typedef struct CELE cmd_ele, *cmd_ptr; +struct CELE { + char *name; + cmd_function operation; + char *documentation; + cmd_ptr next; +}; + +/* Optionally supply function that gets invoked when parameter changes */ +typedef void (*setter_function)(int oldval); + +/* Integer-valued parameters */ +typedef struct PELE param_ele, *param_ptr; +struct PELE { + char *name; + int *valp; + char *documentation; + /* Function that gets called whenever parameter changes */ + setter_function setter; + param_ptr next; +}; + +/* Initialize interpreter */ +void init_cmd(); + +/* Add a new command */ +void add_cmd(char *name, cmd_function operation, char *documentation); + +/* Add a new parameter */ +void add_param(char *name, + int *valp, + char *doccumentation, + setter_function setter); + +/* Execute a command from a command line */ +bool interpret_cmd(char *cmdline); + +/* Execute a sequence of commands read from a file */ +bool interpret_file(FILE *fp); + +/* Extract integer from text and store at loc */ +bool get_int(char *vname, int *loc); + +/* Add function to be executed as part of program exit */ +void add_quit_helper(cmd_function qf); + +/* Set prompt */ +void set_prompt(char *prompt); + +/* Turn echoing on/off */ +void set_echo(bool on); + +/* + Some console commands require network activity to complete. + Program maintains a flag indicating whether console is ready to execute a new + command (unblocked) or it must wait for pending network activity (blocked). + + The following calls set/clear that flag +*/ + +void block_console(); +void unblock_console(); + +/* Start command intrepretation. + If infile_name is NULL, then read commands from stdin + Otherwise, use infile as command source +*/ +bool start_cmd(char *infile_name); + + +/* Is it time to quit the command loop? */ +bool cmd_done(); + +/* Complete command interpretation */ +/* Return true if no errors occurred */ +bool finish_cmd(); + +/* + Handle command processing in program that uses select as main + control loop. Like select, but (if console unblocked) it checks + whether command input either present in internal buffer or readable + from command input. If so, that command is executed. Same return + as select. Command input file removed from readfds + + nfds should be set to the maximum file descriptor for network sockets. + If nfds == 0, this indicates that there is no pending network activity +*/ + +int cmd_select(int nfds, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout); + +/* Run command loop. Non-null infile_name implies read commands from that file + */ +bool run_console(char *infile_name); diff --git a/driver.py b/driver.py new file mode 100755 index 000000000..de8dff6ca --- /dev/null +++ b/driver.py @@ -0,0 +1,151 @@ +#!/usr/bin/python + +import subprocess +import sys +import getopt + +# Driver program for C programming exercise +class Tracer: + + traceDirectory = "./traces" + qtest = "./qtest" + verbLevel = 0 + autograde = False + + traceDict = { + 1 : "trace-01-ops", + 2 : "trace-02-ops", + 3 : "trace-03-ops", + 4 : "trace-04-ops", + 5 : "trace-05-ops", + 6 : "trace-06-string", + 7 : "trace-07-robust", + 8 : "trace-08-robust", + 9 : "trace-09-robust", + 10 : "trace-10-malloc", + 11 : "trace-11-malloc", + 12 : "trace-12-malloc", + 13 : "trace-13-perf", + 14 : "trace-14-perf", + 15 : "trace-15-perf" + } + + traceProbs = { + 1 : "Trace-01", + 2 : "Trace-02", + 3 : "Trace-03", + 4 : "Trace-04", + 5 : "Trace-05", + 6 : "Trace-06", + 7 : "Trace-07", + 8 : "Trace-08", + 9 : "Trace-09", + 10 : "Trace-10", + 11 : "Trace-11", + 12 : "Trace-12", + 13 : "Trace-13", + 14 : "Trace-14", + 15 : "Trace-15" + } + + + maxScores = [0, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7] + + def __init__(self, qtest = "", verbLevel = 0, autograde = False): + if qtest != "": + self.qtest = qtest + self.verbLevel = verbLevel + self.autograde = autograde + + def runTrace(self, tid): + if not tid in self.traceDict: + print "ERROR: No trace with id %d" % tid + return False + fname = "%s/%s.cmd" % (self.traceDirectory, self.traceDict[tid]) + vname = "%d" % self.verbLevel + clist = [self.qtest, "-v", vname, "-f", fname] + try: + retcode = subprocess.call(clist) + except Exception as e: + print "Call of '%s' failed: %s" % (" ".join(clist), e) + return False + return retcode == 0 + + def run(self, tid = 0): + scoreDict = { k : 0 for k in self.traceDict.keys() } + print "---\tTrace\t\tPoints" + if tid == 0: + tidList = self.traceDict.keys() + else: + if not tid in self.traceDict: + print "ERROR: Invalid trace ID %d" % tid + return + tidList = [tid] + score = 0 + maxscore = 0 + for t in tidList: + tname = self.traceDict[t] + if self.verbLevel > 0: + print "+++ TESTING trace %s:" % tname + ok = self.runTrace(t) + maxval = self.maxScores[t] + tval = maxval if ok else 0 + print "---\t%s\t%d/%d" % (tname, tval, maxval) + score += tval + maxscore += maxval + scoreDict[t] = tval + print "---\tTOTAL\t\t%d/%d" % (score, maxscore) + if self.autograde: + # Generate JSON string + jstring = '{"scores": {' + first = True + for k in scoreDict.keys(): + if not first: + jstring += ', ' + first = False + jstring += '"%s" : %d' % (self.traceProbs[k], scoreDict[k]) + jstring += '}}' + print jstring + +def usage(name): + print "Usage: %s [-h] [-p PROG] [-t TID] [-v VLEVEL]" % name + print " -h Print this message" + print " -p PROG Program to test" + print " -t TID Trace ID to test" + print " -v VLEVEL Set verbosity level (0-3)" + sys.exit(0) + +def run(name, args): + prog = "" + tid = 0 + vlevel = 1 + levelFixed = False + autograde = False + + + optlist, args = getopt.getopt(args, 'hp:t:v:A') + for (opt, val) in optlist: + if opt == '-h': + usage(name) + elif opt == '-p': + prog = val + elif opt == '-t': + tid = int(val) + elif opt == '-v': + vlevel = int(val) + levelFixed = True + elif opt == '-A': + autograde = True + else: + print "Unrecognized option '%s'" % opt + usage(name) + if not levelFixed and autograde: + vlevel = 0 + t = Tracer(qtest = prog, verbLevel = vlevel, autograde = autograde) + t.run(tid) + +if __name__ == "__main__": + run(sys.argv[0], sys.argv[1:]) + + + diff --git a/harness.c b/harness.c new file mode 100644 index 000000000..7247bc586 --- /dev/null +++ b/harness.c @@ -0,0 +1,281 @@ +/* Test support code */ + +#include +#include +#include +#include +#include +#include + +#include "report.h" + +/* Our program needs to use regular malloc/free */ +#define INTERNAL 1 +#include "harness.h" + +/** Special values **/ + +/* Value at start of every allocated block */ +#define MAGICHEADER 0xdeadbeef +/* Value when deallocate block */ +#define MAGICFREE 0xffffffff +/* Value at end of every block */ +#define MAGICFOOTER 0xbeefdead +/* Byte to fill newly malloced space with */ +#define FILLCHAR 0x55 + +/** Data structures used by our code **/ + +/* + Represent allocated blocks as doubly-linked list, with + next and prev pointers at beginning +*/ + + +typedef struct BELE { + struct BELE *next; + struct BELE *prev; + size_t payload_size; + size_t magic_header; /* Marker to see if block seems legitimate */ + unsigned char payload[0]; + /* Also place magic number at tail of every block */ +} block_ele_t; + +static block_ele_t *allocated = NULL; +static size_t allocated_count = 0; +/* Percent probability of malloc failure */ +int fail_probability = 0; +static bool cautious_mode = true; +static bool noallocate_mode = false; +static bool error_occurred = false; +static char *error_message = ""; + +static int time_limit = 1; + +/* + * Data for managing exceptions + */ +static jmp_buf env; +static volatile sig_atomic_t jmp_ready = false; +static bool time_limited = false; + + +/* + Internal functions + */ +/* Should this allocation fail? */ +static bool fail_allocation() +{ + double weight = (double) random() / RAND_MAX; + return (weight < 0.01 * fail_probability); +} + +/* + Find header of block, given its payload. + Signal error if doesn't seem like legitimate block + */ +static block_ele_t *find_header(void *p) +{ + if (p == NULL) { + report_event(MSG_ERROR, "Attempting to free null block"); + error_occurred = true; + } + block_ele_t *b = (block_ele_t *) ((size_t) p - sizeof(block_ele_t)); + if (cautious_mode) { + /* Make sure this is really an allocated block */ + block_ele_t *ab = allocated; + bool found = false; + while (ab && !found) { + found = ab == b; + ab = ab->next; + } + if (!found) { + report_event(MSG_ERROR, + "Attempted to free unallocated block. Address = %p", + p); + error_occurred = true; + } + } + if (b->magic_header != MAGICHEADER) { + report_event( + MSG_ERROR, + "Attempted to free unallocated or corrupted block. Address = %p", + p); + error_occurred = true; + } + return b; +} + +/* Given pointer to block, find its footer */ +static size_t *find_footer(block_ele_t *b) +{ + size_t *p = (size_t *) ((size_t) b + b->payload_size + sizeof(block_ele_t)); + return p; +} + + +/* + Implementation of application functions + */ +void *test_malloc(size_t size) +{ + if (noallocate_mode) { + report_event(MSG_FATAL, "Calls to malloc disallowed"); + return NULL; + } + if (fail_allocation()) { + report_event(MSG_WARN, "Malloc returning NULL"); + return NULL; + } + block_ele_t *new_block = + malloc(size + sizeof(block_ele_t) + sizeof(size_t)); + if (new_block == NULL) { + report_event(MSG_FATAL, "Couldn't allocate any more memory"); + error_occurred = true; + } + new_block->magic_header = MAGICHEADER; + new_block->payload_size = size; + *find_footer(new_block) = MAGICFOOTER; + void *p = (void *) &new_block->payload; + memset(p, FILLCHAR, size); + new_block->next = allocated; + new_block->prev = NULL; + if (allocated) + allocated->prev = new_block; + allocated = new_block; + allocated_count++; + return p; +} + +void test_free(void *p) +{ + if (noallocate_mode) { + report_event(MSG_FATAL, "Calls to free disallowed"); + return; + } + if (p == NULL) { + report(MSG_ERROR, "Attempt to free NULL"); + error_occurred = true; + return; + } + block_ele_t *b = find_header(p); + size_t footer = *find_footer(b); + if (footer != MAGICFOOTER) { + report_event(MSG_ERROR, + "Corruption detected in block with address %p when " + "attempting to free it", + p); + error_occurred = true; + } + b->magic_header = MAGICFREE; + *find_footer(b) = MAGICFREE; + memset(p, FILLCHAR, b->payload_size); + + /* Unlink from list */ + block_ele_t *bn = b->next; + block_ele_t *bp = b->prev; + if (bp) + bp->next = bn; + else + allocated = bn; + if (bn) + bn->prev = bp; + + free(b); + allocated_count--; +} + +size_t allocation_check() +{ + return allocated_count; +} + +/* + Implementation of functions for testing + */ + + +/* + Set/unset cautious mode. + In this mode, makes extra sure any block to be freed is currently allocated. +*/ +void set_cautious_mode(bool cautious) +{ + cautious_mode = cautious; +} + +/* + Set/unset restricted allocation mode. + In this mode, calls to malloc and free are disallowed. + */ +void set_noallocate_mode(bool noallocate) +{ + noallocate_mode = noallocate; +} + + +/* + Return whether any errors have occurred since last time set error limit + */ +bool error_check() +{ + bool e = error_occurred; + error_occurred = false; + return e; +} + +/* + * Prepare for a risky operation using setjmp. + * Function returns true for initial return, false for error return + */ +bool exception_setup(bool limit_time) +{ + if (sigsetjmp(env, 1)) { + /* Got here from longjmp */ + jmp_ready = false; + if (time_limited) { + alarm(0); + time_limited = false; + } + if (error_message) { + report_event(MSG_ERROR, error_message); + } + error_message = ""; + return false; + } else { + /* Got here from initial call */ + jmp_ready = true; + if (limit_time) { + alarm(time_limit); + time_limited = true; + } + return true; + } +} + +/* + * Call once past risky code + */ +void exception_cancel() +{ + if (time_limited) { + alarm(0); + time_limited = false; + } + jmp_ready = false; + error_message = ""; +} + + +/* + * Use longjmp to return to most recent exception setup + */ +void trigger_exception(char *msg) +{ + error_occurred = true; + error_message = msg; + if (jmp_ready) + siglongjmp(env, 1); + else + exit(1); +} diff --git a/harness.h b/harness.h new file mode 100644 index 000000000..c80c71121 --- /dev/null +++ b/harness.h @@ -0,0 +1,60 @@ +#include +#include +#include + +/* + This test harness enables us to do stringent testing of code. + It overloads the library versions of malloc and free with ones that + allow checking for common allocation errors. +*/ + +void *test_malloc(size_t size); +void test_free(void *p); + +#ifdef INTERNAL +/* Report number of allocated blocks */ +size_t allocation_check(); + +/* Probability of malloc failing, expressed as percent */ +int fail_probability; + +/* + Set/unset cautious mode. + In this mode, makes extra sure any block to be freed is currently allocated. +*/ +void set_cautious_mode(bool cautious); + +/* + Set/unset restricted allocation mode. + In this mode, calls to malloc and free are disallowed. + */ +void set_noallocate_mode(bool noallocate); + + +/* + Return whether any errors have occurred since last time checked + */ +bool error_check(); + +/* + * Prepare for a risky operation using setjmp. + * Function returns true for initial return, false for error return + */ +bool exception_setup(bool limit_time); + +/* + * Call once past risky code + */ +void exception_cancel(); + +/* + * Use longjmp to return to most recent exception setup. Include error message + */ +void trigger_exception(char *msg); + + +#else +/* Tested program use our versions of malloc and free */ +#define malloc test_malloc +#define free test_free +#endif diff --git a/qtest.c b/qtest.c new file mode 100644 index 000000000..2d68f4912 --- /dev/null +++ b/qtest.c @@ -0,0 +1,576 @@ +/* Implementation of testing code for queue code */ + +#include +#include +#include +#include +#include + +/* Our program needs to use regular malloc/free */ +#define INTERNAL 1 +#include "harness.h" + +/* What character limit will be used for displaying strings? */ +#define MAXSTRING 1024 +/* How much padding should be added to check for string overrun? */ +#define STRINGPAD MAXSTRING + +/* + It is a bit sketchy to use this #include file on the solution version of the + code. + OK as long as head field of queue_t structure is in first position in solution + code +*/ +#include "queue.h" + +#include "report.h" +#include "console.h" + +/***** Settable parameters *****/ + +/* + How large is a queue before it's considered big. + This affects how it gets printed + and whether cautious mode is used when freeing the list +*/ +#define BIG_QUEUE 30 + +int big_queue_size = BIG_QUEUE; + +/******* Global variables ******/ + +/* Queue being tested */ +queue_t *q = NULL; +/* Number of elements in queue */ +size_t qcnt = 0; + +/* How many times can queue operations fail */ +int fail_limit = BIG_QUEUE; +int fail_count = 0; + +int string_length = MAXSTRING; + +/****** Forward declarations ******/ +static bool show_queue(int vlevel); +bool do_new(int argc, char *argv[]); +bool do_free(int argc, char *argv[]); +bool do_insert_head(int argc, char *argv[]); +bool do_insert_tail(int argc, char *argv[]); +bool do_remove_head(int argc, char *argv[]); +bool do_remove_head_quiet(int argc, char *argv[]); +bool do_reverse(int argc, char *argv[]); +bool do_size(int argc, char *argv[]); +bool do_show(int argc, char *argv[]); + +static void queue_init(); + +static void console_init() +{ + add_cmd("new", do_new, " | Create new queue"); + add_cmd("free", do_free, " | Delete queue"); + add_cmd("ih", do_insert_head, + " str [n] | Insert string str at head of queue n times " + "(default: n == 1)"); + add_cmd("it", do_insert_tail, + " str [n] | Insert string str at tail of queue n times " + "(default: n == 1)"); + add_cmd("rh", do_remove_head, + " [str] | Remove from head of queue. Optionally compare " + "to expected value str"); + add_cmd( + "rhq", do_remove_head_quiet, + " | Remove from head of queue without reporting value."); + add_cmd("reverse", do_reverse, " | Reverse queue"); + add_cmd("size", do_size, + " [n] | Compute queue size n times (default: n == 1)"); + add_cmd("show", do_show, " | Show queue contents"); + add_param("length", &string_length, "Maximum length of displayed string", + NULL); + add_param("malloc", &fail_probability, "Malloc failure probability percent", + NULL); + add_param("fail", &fail_limit, + "Number of times allow queue operations to return false", NULL); +} + +bool do_new(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + bool ok = true; + if (q != NULL) { + report(3, "Freeing old queue"); + ok = do_free(argc, argv); + } + error_check(); + if (exception_setup(true)) + q = q_new(); + exception_cancel(); + qcnt = 0; + show_queue(3); + return ok && !error_check(); +} + +bool do_free(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + bool ok = true; + if (q == NULL) + report(3, "Warning: Calling free on null queue"); + error_check(); + if (qcnt > big_queue_size) + set_cautious_mode(false); + if (exception_setup(true)) + q_free(q); + exception_cancel(); + set_cautious_mode(true); + q = NULL; + qcnt = 0; + show_queue(3); + size_t bcnt = allocation_check(); + if (bcnt > 0) { + report(1, "ERROR: Freed queue, but %lu blocks are still allocated", + bcnt); + ok = false; + } + return ok && !error_check(); +} + +bool do_insert_head(int argc, char *argv[]) +{ + char *inserts; + char *lasts = NULL; + int reps = 1; + int r; + bool ok = true; + if (argc != 2 && argc != 3) { + report(1, "%s needs 1-2 arguments", argv[0]); + return false; + } + inserts = argv[1]; + if (argc == 3) { + if (!get_int(argv[2], &reps)) { + report(1, "Invalid number of insertions '%s'", argv[2]); + return false; + } + } + if (q == NULL) + report(3, "Warning: Calling insert head on null queue"); + error_check(); + if (exception_setup(true)) { + for (r = 0; ok && r < reps; r++) { + bool rval = q_insert_head(q, inserts); + if (rval) { + qcnt++; + if (!q->head->value) { + report(1, "ERROR: Failed to save copy of string in list"); + ok = false; + } else if (r == 0 && inserts == q->head->value) { + report(1, + "ERROR: Need to allocate and copy string for new " + "list element"); + ok = false; + break; + } else if (r == 1 && lasts == q->head->value) { + report(1, + "ERROR: Need to allocate separate string for each " + "list element"); + ok = false; + break; + } + lasts = q->head->value; + } else { + fail_count++; + if (fail_count < fail_limit) + report(2, "Insertion of %s failed", inserts); + else { + report(1, + "ERROR: Insertion of %s failed (%d failures total)", + inserts, fail_count); + ok = false; + } + } + ok = ok && !error_check(); + } + } + exception_cancel(); + show_queue(3); + return ok; +} + +bool do_insert_tail(int argc, char *argv[]) +{ + char *inserts; + int reps = 1; + int r; + bool ok = true; + if (argc != 2 && argc != 3) { + report(1, "%s needs 1-2 arguments", argv[0]); + return false; + } + inserts = argv[1]; + if (argc == 3) { + if (!get_int(argv[2], &reps)) { + report(1, "Invalid number of insertions '%s'", argv[2]); + return false; + } + } + if (q == NULL) + report(3, "Warning: Calling insert tail on null queue"); + error_check(); + if (exception_setup(true)) { + for (r = 0; ok && r < reps; r++) { + bool rval = q_insert_tail(q, inserts); + if (rval) { + qcnt++; + if (!q->head->value) { + report(1, "ERROR: Failed to save copy of string in list"); + ok = false; + } + } else { + fail_count++; + if (fail_count < fail_limit) + report(2, "Insertion of %s failed", inserts); + else { + report(1, + "ERROR: Insertion of %s failed (%d failures total)", + inserts, fail_count); + ok = false; + } + } + ok = ok && !error_check(); + } + } + exception_cancel(); + show_queue(3); + return ok; +} + +bool do_remove_head(int argc, char *argv[]) +{ + if (argc != 1 && argc != 2) { + report(1, "%s needs 0-1 arguments", argv[0]); + return false; + } + char *removes = malloc(string_length + STRINGPAD + 1); + if (removes == NULL) { + report(1, + "INTERNAL ERROR. Could not allocate space for removed strings"); + return false; + } + char *checks = malloc(string_length + 1); + if (checks == NULL) { + report(1, + "INTERNAL ERROR. Could not allocate space for removed strings"); + free(removes); + return false; + } + bool check = argc > 1; + bool ok = true; + if (check) { + strncpy(checks, argv[1], string_length + 1); + checks[string_length] = '\0'; + } + + removes[0] = '\0'; + memset(removes + 1, 'X', string_length + STRINGPAD - 1); + removes[string_length + STRINGPAD] = '\0'; + + if (q == NULL) + report(3, "Warning: Calling remove head on null queue"); + else if (q->head == NULL) + report(3, "Warning: Calling remove head on empty queue"); + error_check(); + bool rval = false; + if (exception_setup(true)) + rval = q_remove_head(q, removes, string_length + 1); + exception_cancel(); + if (rval) { + removes[string_length + STRINGPAD] = '\0'; + if (removes[0] == '\0') { + report(1, "ERROR: Failed to store removed value"); + ok = false; + } else if (removes[string_length + 1] != 'X') { + report(1, + "ERROR: copying of string in remove_head overflowed " + "destination buffer."); + ok = false; + } else { + report(2, "Removed %s from queue", removes); + } + qcnt--; + } else { + fail_count++; + if (!check && fail_count < fail_limit) { + report(2, "Removal from queue failed"); + } else { + report(1, "ERROR: Removal from queue failed (%d failures total)", + fail_count); + ok = false; + } + } + if (ok && check && strcmp(removes, checks) != 0) { + report(1, "ERROR: Removed value %s != expected value %s", removes, + checks); + ok = false; + } + show_queue(3); + free(removes); + free(checks); + return ok && !error_check(); +} + +bool do_remove_head_quiet(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + bool ok = true; + if (q == NULL) + report(3, "Warning: Calling remove head on null queue"); + else if (q->head == NULL) + report(3, "Warning: Calling remove head on empty queue"); + error_check(); + bool rval = false; + if (exception_setup(true)) + rval = q_remove_head(q, NULL, 0); + exception_cancel(); + if (rval) { + report(2, "Removed element from queue"); + qcnt--; + } else { + fail_count++; + if (fail_count < fail_limit) + report(2, "Removal failed"); + else { + report(1, "ERROR: Removal failed (%d failures total)", fail_count); + ok = false; + } + } + show_queue(3); + return ok && !error_check(); +} + +bool do_reverse(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + if (q == NULL) + report(3, "Warning: Calling reverse on null queue"); + error_check(); + set_noallocate_mode(true); + if (exception_setup(true)) + q_reverse(q); + exception_cancel(); + set_noallocate_mode(false); + show_queue(3); + return !error_check(); +} + +bool do_size(int argc, char *argv[]) +{ + if (argc != 1 && argc != 2) { + report(1, "%s takes 0-1 arguments", argv[0]); + return false; + } + int reps = 1; + int r; + bool ok = true; + if (argc != 1 && argc != 2) { + report(1, "%s needs 0-1 arguments", argv[0]); + return false; + } + if (argc == 2) { + if (!get_int(argv[1], &reps)) { + report(1, "Invalid number of calls to size '%s'", argv[2]); + } + } + int cnt = 0; + if (q == NULL) + report(3, "Warning: Calling size on null queue"); + error_check(); + if (exception_setup(true)) { + for (r = 0; ok && r < reps; r++) { + cnt = q_size(q); + ok = ok && !error_check(); + } + } + exception_cancel(); + if (ok) { + if (qcnt == cnt) { + report(2, "Queue size = %d", cnt); + } else { + report(1, + "ERROR: Computed queue size as %d, but correct value is %d", + cnt, (int) qcnt); + ok = false; + } + } + show_queue(3); + + return ok && !error_check(); +} + +static bool show_queue(int vlevel) +{ + bool ok = true; + if (verblevel < vlevel) + return true; + int cnt = 0; + if (q == NULL) { + report(vlevel, "q = NULL"); + return true; + } + report_noreturn(vlevel, "q = ["); + list_ele_t *e = q->head; + if (exception_setup(true)) { + while (ok && e && cnt < qcnt) { + if (cnt < big_queue_size) + report_noreturn(vlevel, cnt == 0 ? "%s" : " %s", e->value); + e = e->next; + cnt++; + ok = ok && !error_check(); + } + } + exception_cancel(); + if (!ok) { + report(vlevel, " ... ]"); + return false; + } + if (e == NULL) { + if (cnt <= big_queue_size) + report(vlevel, "]"); + else + report(vlevel, " ... ]"); + } else { + report(vlevel, " ... ]"); + report( + vlevel, + "ERROR: Either list has cycle, or queue has more than %d elements", + qcnt); + ok = false; + } + return ok; +} + +bool do_show(int argc, char *argv[]) +{ + if (argc != 1) { + report(1, "%s takes no arguments", argv[0]); + return false; + } + return show_queue(0); +} + +/* Signal handlers */ +void sigsegvhandler(int sig) +{ + trigger_exception( + "Segmentation fault occurred. You dereferenced a NULL or invalid " + "pointer"); +} + +void sigalrmhandler(int sig) +{ + trigger_exception( + "Time limit exceeded. Either you are in an infinite loop, or your " + "code is too inefficient"); +} + + +static void queue_init() +{ + fail_count = 0; + q = NULL; + signal(SIGSEGV, sigsegvhandler); + signal(SIGALRM, sigalrmhandler); +} + + +static bool queue_quit(int argc, char *argv[]) +{ + report(3, "Freeing queue"); + if (qcnt > big_queue_size) + set_cautious_mode(false); + if (exception_setup(true)) + q_free(q); + exception_cancel(); + set_cautious_mode(true); + size_t bcnt = allocation_check(); + if (bcnt > 0) { + report(1, "ERROR: Freed queue, but %lu blocks are still allocated", + bcnt); + return false; + } + return true; +} + + +static void usage(char *cmd) +{ + printf("Usage: %s [-h] [-f IFILE][-v VLEVEL][-l LFILE]\n", cmd); + printf("\t-h Print this information\n"); + printf("\t-f IFILE Read commands from IFILE\n"); + printf("\t-v VLEVEL Set verbosity level\n"); + printf("\t-l LFILE Echo results to LFILE\n"); + exit(0); +} + +#define BUFSIZE 256 + +int main(int argc, char *argv[]) +{ + /* To hold input file name */ + char buf[BUFSIZE]; + char *infile_name = NULL; + char lbuf[BUFSIZE]; + char *logfile_name = NULL; + int level = 4; + int c; + + while ((c = getopt(argc, argv, "hv:f:l:")) != -1) { + switch (c) { + case 'h': + usage(argv[0]); + break; + case 'f': + strncpy(buf, optarg, BUFSIZE); + buf[BUFSIZE - 1] = '\0'; + infile_name = buf; + break; + case 'v': + level = atoi(optarg); + break; + case 'l': + strncpy(lbuf, optarg, BUFSIZE); + buf[BUFSIZE - 1] = '\0'; + logfile_name = lbuf; + break; + default: + printf("Unknown option '%c'\n", c); + usage(argv[0]); + break; + } + } + queue_init(); + init_cmd(); + console_init(); + set_verblevel(level); + if (level > 1) { + set_echo(true); + } + if (logfile_name) + set_logfile(logfile_name); + add_quit_helper(queue_quit); + bool ok = true; + ok = ok && run_console(infile_name); + ok = ok && finish_cmd(); + return ok ? 0 : 1; +} diff --git a/queue.c b/queue.c new file mode 100644 index 000000000..e494dcf22 --- /dev/null +++ b/queue.c @@ -0,0 +1,111 @@ +/* + * Code for basic C skills diagnostic. + * Developed for courses 15-213/18-213/15-513 by R. E. Bryant, 2017 + * Modified to store strings, 2018 + */ + +/* + * This program implements a queue supporting both FIFO and LIFO + * operations. + * + * It uses a singly-linked list to represent the set of queue elements + */ + +#include +#include +#include + +#include "harness.h" +#include "queue.h" + +/* + Create empty queue. + Return NULL if could not allocate space. +*/ +queue_t *q_new() +{ + queue_t *q = malloc(sizeof(queue_t)); + /* What if malloc returned NULL? */ + q->head = NULL; + return q; +} + +/* Free all storage used by queue */ +void q_free(queue_t *q) +{ + /* How about freeing the list elements and the strings? */ + /* Free queue structure */ + free(q); +} + +/* + Attempt to insert element at head of queue. + Return true if successful. + Return false if q is NULL or could not allocate space. + Argument s points to the string to be stored. + The function must explicitly allocate space and copy the string into it. + */ +bool q_insert_head(queue_t *q, char *s) +{ + list_ele_t *newh; + /* What should you do if the q is NULL? */ + newh = malloc(sizeof(list_ele_t)); + /* Don't forget to allocate space for the string and copy it */ + /* What if either call to malloc returns NULL? */ + newh->next = q->head; + q->head = newh; + return true; +} + + +/* + Attempt to insert element at tail of queue. + Return true if successful. + Return false if q is NULL or could not allocate space. + Argument s points to the string to be stored. + The function must explicitly allocate space and copy the string into it. + */ +bool q_insert_tail(queue_t *q, char *s) +{ + /* You need to write the complete code for this function */ + /* Remember: It should operate in O(1) time */ + return false; +} + +/* + Attempt to remove element from head of queue. + Return true if successful. + Return false if queue is NULL or empty. + If sp is non-NULL and an element is removed, copy the removed string to *sp + (up to a maximum of bufsize-1 characters, plus a null terminator.) + The space used by the list element and the string should be freed. +*/ +bool q_remove_head(queue_t *q, char *sp, size_t bufsize) +{ + /* You need to fix up this code. */ + q->head = q->head->next; + return true; +} + +/* + Return number of elements in queue. + Return 0 if q is NULL or empty + */ +int q_size(queue_t *q) +{ + /* You need to write the code for this function */ + /* Remember: It should operate in O(1) time */ + return 0; +} + +/* + Reverse elements in queue + No effect if q is NULL or empty + This function should not allocate or free any list elements + (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head). + It should rearrange the existing ones. + */ +void q_reverse(queue_t *q) +{ + /* You need to write the code for this function */ +} diff --git a/queue.h b/queue.h new file mode 100644 index 000000000..88b082baf --- /dev/null +++ b/queue.h @@ -0,0 +1,90 @@ +/* + * Code for basic C skills diagnostic. + * Developed for courses 15-213/18-213/15-513 by R. E. Bryant, 2017 + * Extended to store strings, 2018 + */ + +/* + * This program implements a queue supporting both FIFO and LIFO + * operations. + * + * It uses a singly-linked list to represent the set of queue elements + */ + +#include + +/************** Data structure declarations ****************/ + +/* Linked list element (You shouldn't need to change this) */ +typedef struct ELE { + /* Pointer to array holding string. + This array needs to be explicitly allocated and freed */ + char *value; + struct ELE *next; +} list_ele_t; + +/* Queue structure */ +typedef struct { + list_ele_t *head; /* Linked list of elements */ + /* + You will need to add more fields to this structure + to efficiently implement q_size and q_insert_tail + */ +} queue_t; + +/************** Operations on queue ************************/ + +/* + Create empty queue. + Return NULL if could not allocate space. +*/ +queue_t *q_new(); + +/* + Free ALL storage used by queue. + No effect if q is NULL +*/ +void q_free(queue_t *q); + +/* + Attempt to insert element at head of queue. + Return true if successful. + Return false if q is NULL or could not allocate space. + Argument s points to the string to be stored. + The function must explicitly allocate space and copy the string into it. + */ +bool q_insert_head(queue_t *q, char *s); + +/* + Attempt to insert element at tail of queue. + Return true if successful. + Return false if q is NULL or could not allocate space. + Argument s points to the string to be stored. + The function must explicitly allocate space and copy the string into it. + */ +bool q_insert_tail(queue_t *q, char *s); + +/* + Attempt to remove element from head of queue. + Return true if successful. + Return false if queue is NULL or empty. + If sp is non-NULL and an element is removed, copy the removed string to *sp + (up to a maximum of bufsize-1 characters, plus a null terminator.) + The space used by the list element and the string should be freed. +*/ +bool q_remove_head(queue_t *q, char *sp, size_t bufsize); + +/* + Return number of elements in queue. + Return 0 if q is NULL or empty + */ +int q_size(queue_t *q); + +/* + Reverse elements in queue + No effect if q is NULL or empty + This function should not allocate or free any list elements + (e.g., by calling q_insert_head, q_insert_tail, or q_remove_head). + It should rearrange the existing ones. + */ +void q_reverse(queue_t *q); diff --git a/report.c b/report.c new file mode 100644 index 000000000..fcb353124 --- /dev/null +++ b/report.c @@ -0,0 +1,380 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "report.h" + +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +FILE *errfile = NULL; +FILE *verbfile = NULL; +FILE *logfile = NULL; + +int verblevel = 0; +void init_files(FILE *efile, FILE *vfile) +{ + errfile = efile; + verbfile = vfile; +} + +static char fail_buf[1024] = "FATAL Error. Exiting\n"; + +volatile int rval = 0; + +/* Default fatal function */ +void default_fatal_fun() +{ + /* + sprintf(fail_buf, "FATAL. Memory: allocated = %.3f GB, resident = %.3f + GB\n", + gigabytes(current_bytes), gigabytes(resident_bytes())); + */ + rval = write(STDOUT_FILENO, fail_buf, strlen(fail_buf) + 1); + if (logfile) + fputs(fail_buf, logfile); +} + +/* Optional function to call when fatal error encountered */ +void (*fatal_fun)() = default_fatal_fun; + +void set_verblevel(int level) +{ + verblevel = level; +} + +bool set_logfile(char *file_name) +{ + logfile = fopen(file_name, "w"); + return logfile != NULL; +} + +void report_event(message_t msg, char *fmt, ...) +{ + va_list ap; + bool fatal = msg == MSG_FATAL; + char *msg_name = msg == MSG_WARN ? "WARNING" : msg == MSG_ERROR + ? "ERROR" + : "FATAL ERROR"; + int level = msg == MSG_WARN ? 2 : msg == MSG_ERROR ? 1 : 0; + if (verblevel < level) + return; + if (!errfile) + init_files(stdout, stdout); + va_start(ap, fmt); + fprintf(errfile, "%s: ", msg_name); + vfprintf(errfile, fmt, ap); + fprintf(errfile, "\n"); + fflush(errfile); + va_end(ap); + if (logfile) { + va_start(ap, fmt); + fprintf(logfile, "Error: "); + vfprintf(logfile, fmt, ap); + fprintf(logfile, "\n"); + fflush(logfile); + va_end(ap); + fclose(logfile); + } + if (fatal) { + if (fatal_fun) + fatal_fun(); + exit(1); + } +} + + +void report(int level, char *fmt, ...) +{ + va_list ap; + if (!verbfile) + init_files(stdout, stdout); + if (level <= verblevel) { + va_start(ap, fmt); + vfprintf(verbfile, fmt, ap); + fprintf(verbfile, "\n"); + fflush(verbfile); + va_end(ap); + if (logfile) { + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + fprintf(logfile, "\n"); + fflush(logfile); + va_end(ap); + } + } +} + +void report_noreturn(int level, char *fmt, ...) +{ + va_list ap; + if (!verbfile) + init_files(stdout, stdout); + if (level <= verblevel) { + va_start(ap, fmt); + vfprintf(verbfile, fmt, ap); + fflush(verbfile); + va_end(ap); + if (logfile) { + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + fflush(logfile); + va_end(ap); + } + } +} + +void safe_report(int level, char *msg) +{ + if (level > verblevel) + return; + if (!errfile) + init_files(stdout, stdout); + fputs(msg, errfile); + if (logfile) { + fputs(msg, logfile); + } +} + + + +/* Functions denoting failures */ + +/* General failure */ + +/* Need to be able to print without using malloc */ +void fail_fun(char *format, char *msg) +{ + sprintf(fail_buf, format, msg); + /* Tack on return */ + fail_buf[strlen(fail_buf)] = '\n'; + /* Use write to avoid any buffering issues */ + rval = write(STDOUT_FILENO, fail_buf, strlen(fail_buf) + 1); + if (logfile) { + /* Don't know file descriptor for logfile */ + fputs(fail_buf, logfile); + } + if (fatal_fun) + fatal_fun(); + if (logfile) + fclose(logfile); + exit(1); +} + +/* Maximum number of megabytes that application can use (0 = unlimited) */ +int mblimit = 0; +/* Maximum number of seconds that application can use. (0 = unlimited) */ +int timelimit = 0; + +/* Keeping track of memory allocation */ +static size_t allocate_cnt = 0; +static size_t allocate_bytes = 0; +static size_t free_cnt = 0; +static size_t free_bytes = 0; +/* These are externally visible */ +size_t peak_bytes = 0; +size_t last_peak_bytes = 0; +size_t current_bytes = 0; + +static void check_exceed(size_t new_bytes) +{ + size_t limit_bytes = (size_t) mblimit << 20; + size_t request_bytes = new_bytes + current_bytes; + if (mblimit > 0 && request_bytes > limit_bytes) { + report_event(MSG_FATAL, + "Exceeded memory limit of %u megabytes with %lu bytes", + mblimit, request_bytes); + } +} + +/* Call malloc & exit if fails */ +void *malloc_or_fail(size_t bytes, char *fun_name) +{ + check_exceed(bytes); + void *p = malloc(bytes); + if (!p) { + fail_fun("Malloc returned NULL in %s", fun_name); + return NULL; + } + allocate_cnt++; + allocate_bytes += bytes; + current_bytes += bytes; + peak_bytes = MAX(peak_bytes, current_bytes); + last_peak_bytes = MAX(last_peak_bytes, current_bytes); + return p; +} + +/* Call calloc returns NULL & exit if fails */ +void *calloc_or_fail(size_t cnt, size_t bytes, char *fun_name) +{ + check_exceed(cnt * bytes); + void *p = calloc(cnt, bytes); + if (!p) { + fail_fun("Calloc returned NULL in %s", fun_name); + return NULL; + } + allocate_cnt++; + allocate_bytes += cnt * bytes; + current_bytes += cnt * bytes; + peak_bytes = MAX(peak_bytes, current_bytes); + last_peak_bytes = MAX(last_peak_bytes, current_bytes); + + return p; +} + +/* Call realloc returns NULL & exit if fails. + Require explicit indication of current allocation */ +void *realloc_or_fail(void *old, + size_t old_bytes, + size_t new_bytes, + char *fun_name) +{ + if (new_bytes > old_bytes) + check_exceed(new_bytes - old_bytes); + void *p = realloc(old, new_bytes); + if (!p) { + fail_fun("Realloc returned NULL in %s", fun_name); + return NULL; + } + allocate_cnt++; + allocate_bytes += new_bytes; + current_bytes += (new_bytes - old_bytes); + peak_bytes = MAX(peak_bytes, current_bytes); + last_peak_bytes = MAX(last_peak_bytes, current_bytes); + free_cnt++; + free_bytes += old_bytes; + return p; +} + +char *strsave_or_fail(char *s, char *fun_name) +{ + if (!s) + return NULL; + size_t len = strlen(s); + check_exceed(len + 1); + char *ss = malloc(len + 1); + if (!ss) { + fail_fun("strsave failed in %s", fun_name); + } + allocate_cnt++; + allocate_bytes += len + 1; + current_bytes += len + 1; + peak_bytes = MAX(peak_bytes, current_bytes); + last_peak_bytes = MAX(last_peak_bytes, current_bytes); + + return strcpy(ss, s); +} + +/* Free block, as from malloc, realloc, or strsave */ +void free_block(void *b, size_t bytes) +{ + if (b == NULL) { + report_event(MSG_ERROR, "Attempting to free null block"); + } + free(b); + free_cnt++; + free_bytes += bytes; + current_bytes -= bytes; +} + +/* Free array, as from calloc */ +void free_array(void *b, size_t cnt, size_t bytes) +{ + if (b == NULL) { + report_event(MSG_ERROR, "Attempting to free null block"); + } + free(b); + free_cnt++; + free_bytes += cnt * bytes; + current_bytes -= cnt * bytes; +} + +/* Free string saved by strsave_or_fail */ +void free_string(char *s) +{ + if (s == NULL) { + report_event(MSG_ERROR, "Attempting to free null block"); + } + free_block((void *) s, strlen(s) + 1); +} + + +/* Report current allocation status */ +void mem_status(FILE *fp) +{ + fprintf(fp, + "Allocated cnt/bytes: %lu/%lu. Freed cnt/bytes: %lu/%lu.\n" + " Peak bytes %lu, Last peak bytes %ld, Current bytes %ld\n", + (long unsigned) allocate_cnt, (long unsigned) allocate_bytes, + (long unsigned) free_cnt, (long unsigned) free_bytes, + (long unsigned) peak_bytes, (long unsigned) last_peak_bytes, + (long unsigned) current_bytes); +} + +/* Initialization of timers */ +void init_time(double *timep) +{ + (void) delta_time(timep); +} + +double delta_time(double *timep) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + double current_time = tv.tv_sec + 1.0E-6 * tv.tv_usec; + double delta = current_time - *timep; + *timep = current_time; + return delta; +} + +/* Number of bytes resident in physical memory */ +size_t resident_bytes() +{ + struct rusage r; + size_t mem = 0; + int code = getrusage(RUSAGE_SELF, &r); + if (code < 0) { + report_event(MSG_ERROR, "Call to getrusage failed"); + } else { + mem = r.ru_maxrss * 1024; + } + return mem; +} + +double gigabytes(size_t n) +{ + return (double) n / (1UL << 30); +} + +void reset_peak_bytes() +{ + last_peak_bytes = current_bytes; +} + +#if 0 +static char timeout_buf[256]; + +void sigalrmhandler(int sig) { + safe_report(true, timeout_buf); +} + + +void change_timeout(int oldval) { + sprintf(timeout_buf, "Program timed out after %d seconds\n", timelimit); + /* alarm function will correctly cancel existing alarms */ + signal(SIGALRM, sigalrmhandler); + alarm(timelimit); +} + +/* Handler for SIGTERM signals */ +void sigterm_handler(int sig) { + safe_report(true, "SIGTERM signal received"); +} +#endif diff --git a/report.h b/report.h new file mode 100644 index 000000000..3f26fbe64 --- /dev/null +++ b/report.h @@ -0,0 +1,112 @@ +#include +#include + +/* Default reporting level. Must recompile when change */ +#ifndef RPT +#define RPT 2 +#endif + +/* Ways to report interesting behavior and errors */ + +/* Things to report */ +typedef enum { MSG_WARN, MSG_ERROR, MSG_FATAL } message_t; + +/* Buffer sizes */ +#define MAX_CHAR 512 + +void init_files(FILE *errfile, FILE *verbfile); + +bool set_logfile(char *file_name); + +extern int verblevel; +void set_verblevel(int level); + +/* Maximum number of megabytes that application can use (0 = unlimited) */ +extern int mblimit; + +/* Maximum number of seconds that application can use. (0 = unlimited) */ +extern int timelimit; + +/* Optional function to call when fatal error encountered */ +extern void (*fatal_fun)(); + +/* Error messages */ +void report_event(message_t msg, char *fmt, ...); + +/* Report useful information */ +void report(int verblevel, char *fmt, ...); + +/* Like report, but without return character */ +void report_noreturn(int verblevel, char *fmt, ...); + +/* Simple failure report. Works even when malloc returns NULL */ +void fail_fun(char *format, char *msg); + +/* Signal safe reporting function */ +void safe_report(int verblevel, char *msg); + +/* Attempt to call malloc. Fail when returns NULL */ +void *malloc_or_fail(size_t bytes, char *fun_name); + +/* Attempt to call calloc. Fail when returns NULL */ +void *calloc_or_fail(size_t cnt, size_t bytes, char *fun_name); + +/* Attempt to call realloc. Fail when returns NULL */ +void *realloc_or_fail(void *old, + size_t old_bytes, + size_t new_bytes, + char *fun_name); + +/* Attempt to save string. Fail when malloc returns NULL */ +char *strsave_or_fail(char *s, char *fun_name); + +/* Free block, as from malloc, realloc, or strsave */ +void free_block(void *b, size_t len); + +/* Free array, as from calloc */ +void free_array(void *b, size_t cnt, size_t bytes); + +/* Free string saved by strsave_or_fail */ +void free_string(char *s); + +/* Report current allocation status */ +void mem_status(FILE *fp); + +/** Time measurement. **/ + +/* Time counted as fp number in seconds */ +void init_time(double *timep); + +/* Compute time since last call with this timer + and reset timer */ +double delta_time(double *timep); + +/** Memory usage **/ + +/* Number of bytes resident in physical memory */ +size_t resident_bytes(); + +/* Convert bytes to gigabytes */ +double gigabytes(size_t bytes); + +/** Counters giving peak memory usage **/ + +/* Never resets */ +size_t peak_bytes; + +/* Resettable */ +size_t last_peak_bytes; + +/* Instantaneous */ +size_t current_bytes; + + +/* Reset last_peak_bytes */ +void reset_peak_bytes(); + + +/* Change value of timeout */ +void change_timeout(int oldval); + +/* Handler for SIGTERM signals */ +void sigterm_handler(int sig); diff --git a/traces/trace-01-ops.cmd b/traces/trace-01-ops.cmd new file mode 100644 index 000000000..4c38ea79d --- /dev/null +++ b/traces/trace-01-ops.cmd @@ -0,0 +1,11 @@ +# Test of insert_head and remove_head +option fail 0 +option malloc 0 +new +ih gerbil +ih bear +ih dolphin +rh dolphin +rh bear +rh gerbil + diff --git a/traces/trace-02-ops.cmd b/traces/trace-02-ops.cmd new file mode 100644 index 000000000..41ec44e7b --- /dev/null +++ b/traces/trace-02-ops.cmd @@ -0,0 +1,16 @@ +# Test of insert_head, insert_tail, and remove_head +option fail 0 +option malloc 0 +new +ih gerbil +ih bear +ih dolphin +it meerkat +it bear +it gerbil +rh dolphin +rh bear +rh gerbil +rh meerkat +rh bear +rh gerbil diff --git a/traces/trace-03-ops.cmd b/traces/trace-03-ops.cmd new file mode 100644 index 000000000..0a89aa4ab --- /dev/null +++ b/traces/trace-03-ops.cmd @@ -0,0 +1,24 @@ +# Test of insert_head, insert_tail, reverse, and remove_head +option fail 0 +option malloc 0 +new +ih dolphin +ih bear +ih gerbil +reverse +it meerkat +it bear +it gerbil +reverse +it squirrel +reverse +rh squirrel +ih vulture +reverse +rh gerbil +rh bear +rh meerkat +rh gerbil +rh bear +rh dolphin +rh vulture diff --git a/traces/trace-04-ops.cmd b/traces/trace-04-ops.cmd new file mode 100644 index 000000000..af8a166f3 --- /dev/null +++ b/traces/trace-04-ops.cmd @@ -0,0 +1,18 @@ +# Test of insert_head, insert_tail, and size +option fail 0 +option malloc 0 +new +ih gerbil +ih bear +ih dolphin +size +it meerkat +it bear +it gerbil +size +rh dolphin +rh +rh +rh +size + diff --git a/traces/trace-05-ops.cmd b/traces/trace-05-ops.cmd new file mode 100644 index 000000000..3084aa3d1 --- /dev/null +++ b/traces/trace-05-ops.cmd @@ -0,0 +1,23 @@ +# Test of insert_head, insert_tail, remove_head reverse, and size +option fail 0 +option malloc 0 +new +ih dolphin +ih bear +ih gerbil +reverse +size +it meerkat +it bear +it gerbil +size +rh dolphin +reverse +size +rh gerbil +rh bear +rh meerkat +rh gerbil +rh bear +size +free diff --git a/traces/trace-06-string.cmd b/traces/trace-06-string.cmd new file mode 100644 index 000000000..07c4e5cbe --- /dev/null +++ b/traces/trace-06-string.cmd @@ -0,0 +1,30 @@ +# Test of truncated strings +option fail 0 +option malloc 0 +new +ih aardvark_bear_dolphin_gerbil_jaguar 5 +it meerkat_panda_squirrel_vulture_wolf 5 +rh aardvark_bear_dolphin_gerbil_jaguar +reverse +rh meerkat_panda_squirrel_vulture_wolf +option length 30 +rh meerkat_panda_squirrel_vulture +reverse +option length 28 +rh aardvark_bear_dolphin_gerbil +option length 21 +rh aardvark_bear_dolphin +reverse +option length 22 +rh meerkat_panda_squirrel +option length 7 +rh meerkat +reverse +option length 8 +rh aardvark +option length 100 +rh aardvark_bear_dolphin_gerbil_jaguar +reverse +rh meerkat_panda_squirrel_vulture_wolf +free +quit \ No newline at end of file diff --git a/traces/trace-07-robust.cmd b/traces/trace-07-robust.cmd new file mode 100644 index 000000000..190383c4b --- /dev/null +++ b/traces/trace-07-robust.cmd @@ -0,0 +1,9 @@ +# Test operations on NULL queue +option fail 10 +option malloc 0 +free +ih bear +it dolphin +rh +reverse +size diff --git a/traces/trace-08-robust.cmd b/traces/trace-08-robust.cmd new file mode 100644 index 000000000..e506980fa --- /dev/null +++ b/traces/trace-08-robust.cmd @@ -0,0 +1,7 @@ +# Test operations on empty queue +option fail 10 +option malloc 0 +new +rh +reverse +size diff --git a/traces/trace-09-robust.cmd b/traces/trace-09-robust.cmd new file mode 100644 index 000000000..61f14559b --- /dev/null +++ b/traces/trace-09-robust.cmd @@ -0,0 +1,7 @@ +# Test remove_head with NULL argument +option fail 10 +option malloc 0 +new +ih bear +rhq + diff --git a/traces/trace-10-malloc.cmd b/traces/trace-10-malloc.cmd new file mode 100644 index 000000000..b24814fbb --- /dev/null +++ b/traces/trace-10-malloc.cmd @@ -0,0 +1,11 @@ +# Test of malloc failure on new +option fail 10 +option malloc 50 +new +new +new +new +new +new + + diff --git a/traces/trace-11-malloc.cmd b/traces/trace-11-malloc.cmd new file mode 100644 index 000000000..a95d91abf --- /dev/null +++ b/traces/trace-11-malloc.cmd @@ -0,0 +1,9 @@ +# Test of malloc failure on insert_head +option fail 30 +option malloc 0 +new +option malloc 25 +ih gerbil 20 + + + diff --git a/traces/trace-12-malloc.cmd b/traces/trace-12-malloc.cmd new file mode 100644 index 000000000..88c5bb00d --- /dev/null +++ b/traces/trace-12-malloc.cmd @@ -0,0 +1,10 @@ +# Test of malloc failure on insert_tail +option fail 50 +option malloc 0 +new +ih jaguar 20 +option malloc 25 +it gerbil 20 + + + diff --git a/traces/trace-13-perf.cmd b/traces/trace-13-perf.cmd new file mode 100644 index 000000000..abd926d37 --- /dev/null +++ b/traces/trace-13-perf.cmd @@ -0,0 +1,8 @@ +# Test performance of insert_tail +option fail 0 +option malloc 0 +new +ih dolphin 1000000 +it gerbil 1000 +reverse +it jaguar 1000 diff --git a/traces/trace-14-perf.cmd b/traces/trace-14-perf.cmd new file mode 100644 index 000000000..ce0fe458c --- /dev/null +++ b/traces/trace-14-perf.cmd @@ -0,0 +1,7 @@ +# Test performance of size +option fail 0 +option malloc 0 +new +ih dolphin 1000000 +size 1000 + diff --git a/traces/trace-15-perf.cmd b/traces/trace-15-perf.cmd new file mode 100644 index 000000000..2c04c4296 --- /dev/null +++ b/traces/trace-15-perf.cmd @@ -0,0 +1,11 @@ +# Test performance of insert_tail, size, and reverse +option fail 0 +option malloc 0 +new +ih dolphin 1000000 +it gerbil 1000000 +size 1000 +reverse +reverse +size 1000 + diff --git a/traces/trace-eg.cmd b/traces/trace-eg.cmd new file mode 100644 index 000000000..d72e16ced --- /dev/null +++ b/traces/trace-eg.cmd @@ -0,0 +1,21 @@ +# Demonstration of queue testing framework +# Use help command to see list of commands and options +# Initial queue is NULL. +show +# Create empty queue +new +# Fill it with some values. First at the head +ih dolphin +ih bear +ih gerbil +# Now at the tail +it meerkat +it bear +# Reverse it +reverse +# See how long it is +size +# Delete queue. Goes back to a NULL queue. +free +# Exit program +quit