X-Git-Url: http://plrg.eecs.uci.edu/git/?p=c11tester.git;a=blobdiff_plain;f=common.cc;h=c190ed37487e44ff37d9145017dad1bd654085d0;hp=f4aa7ecefa023417c7eec4680e38e1c25a5cc13b;hb=222118f318e4788839083a6d99aa7309246243e7;hpb=44118f596eb7cd9b4b0b5037fbbf958db001accc diff --git a/common.cc b/common.cc index f4aa7ece..c190ed37 100644 --- a/common.cc +++ b/common.cc @@ -1,15 +1,22 @@ #include #include #include +#include +#include +#include #include #include "common.h" #include "model.h" #include "stacktrace.h" +#include "output.h" #define MAX_TRACE_LEN 100 +/** @brief Model-checker output stream; default to stdout until redirected */ +FILE *model_out = stdout; + #define CONFIG_STACKTRACE /** Print a backtrace of the current program state. */ void print_trace(void) @@ -24,10 +31,10 @@ void print_trace(void) size = backtrace(array, MAX_TRACE_LEN); strings = backtrace_symbols(array, size); - printf("\nDumping stack trace (%d frames):\n", size); + model_print("\nDumping stack trace (%d frames):\n", size); for (i = 0; i < size; i++) - printf("\t%s\n", strings[i]); + model_print("\t%s\n", strings[i]); free(strings); #endif /* CONFIG_STACKTRACE */ @@ -40,15 +47,122 @@ void model_print_summary(void) void assert_hook(void) { - printf("Add breakpoint to line %u in file %s.\n",__LINE__,__FILE__); + model_print("Add breakpoint to line %u in file %s.\n",__LINE__,__FILE__); } void model_assert(bool expr, const char *file, int line) { if (!expr) { - printf(" [BUG] Program has hit assertion in file %s at line %d\n", + char msg[100]; + sprintf(msg, "Program has hit assertion in file %s at line %d\n", file, line); - model->set_assert(); - model->switch_to_master(NULL); + model->assert_user_bug(msg); + } +} + +#ifndef CONFIG_DEBUG + +static int fd_user_out; /**< @brief File descriptor from which to read user program output */ + +/** + * @brief Setup output redirecting + * + * Redirects user program's stdout to a pipe so that we can dump it + * selectively, when displaying bugs, etc. + * Also connects a special file 'model_out' directly to stdout, for printing + * data when needed. + * + * The model-checker can selectively choose to print/hide the user program + * output. + * @see clear_program_output + * @see print_program_output + * + * Note that the user program's pipe has limited memory, so if a program will + * output much data, we will need to buffer it in user-space during execution. + * This also means that if ModelChecker decides not to print an execution, it + * should promptly clear the pipe. + * + * This function should only be called once. + */ +void redirect_output() +{ + int fd; + + /* Save stdout for later use */ + fd = dup(fileno(stdout)); + model_out = fdopen(fd, "w"); + + /* Redirect program output to a pipe */ + int pipefd[2]; + if (pipe(pipefd) < 0) { + perror("pipe"); + exit(EXIT_FAILURE); + } + fd = dup2(pipefd[1], fileno(stdout)); // STDOUT_FILENO + close(pipefd[1]); + + /* Save the "read" side of the pipe for use later */ + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + fd_user_out = pipefd[0]; +} + +/** + * @brief Wrapper for reading data to buffer + * + * Besides a simple read, this handles the subtleties of EOF and nonblocking + * input (if fd is O_NONBLOCK). + * + * @param fd The file descriptor to read. + * @param buf Buffer to read to. + * @param maxlen Maximum data to read to buffer + * @return The length of data read. If zero, then we hit EOF or ran out of data + * (non-blocking) + */ +static ssize_t read_to_buf(int fd, char *buf, size_t maxlen) +{ + ssize_t ret = read(fd, buf, maxlen); + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } else { + perror("read"); + exit(EXIT_FAILURE); + } + } + return ret; +} + +/** @brief Dump any pending program output without printing */ +void clear_program_output() +{ + fflush(stdout); + char buf[200]; + while (read_to_buf(fd_user_out, buf, sizeof(buf))); +} + +/** @brief Print out any pending program output */ +void print_program_output() +{ + char buf[200]; + + /* Gather all program output */ + fflush(stdout); + + /* Read program output pipe and write to (real) stdout */ + int ret; + while (1) { + ret = read_to_buf(fd_user_out, buf, sizeof(buf)); + if (!ret) + break; + while (ret > 0) { + int res = fwrite(buf, 1, ret, model_out); + if (res < 0) { + errno = ferror(model_out); + perror("fwrite"); + exit(EXIT_FAILURE); + } + ret -= res; + } } } +#endif /* ! CONFIG_DEBUG */