Change initialize a bit
[c11tester.git] / common.cc
index b274989f48bc4d885f6976b596139b1e5bde42ed..8bef5194201c59f3ee3ff16bf87e1dbaa18a3319 100644 (file)
--- a/common.cc
+++ b/common.cc
@@ -1,19 +1,28 @@
 #include <execinfo.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <model-assert.h>
 
 #include "common.h"
 #include "model.h"
 #include "stacktrace.h"
+#include "output.h"
 
 #define MAX_TRACE_LEN 100
 
+/** @brief Model-checker output file descriptor; default to stdout until redirected */
+int model_out = STDOUT_FILENO;
+
 #define CONFIG_STACKTRACE
 /** Print a backtrace of the current program state. */
 void print_trace(void)
 {
 #ifdef CONFIG_STACKTRACE
-       print_stacktrace(stdout);
+       print_stacktrace(model_out);
 #else
        void *array[MAX_TRACE_LEN];
        char **strings;
@@ -22,21 +31,137 @@ 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]);
+       for (i = 0;i < size;i++)
+               model_print("\t%s\n", strings[i]);
 
        free(strings);
-#endif /* CONFIG_STACKTRACE */
+#endif /* CONFIG_STACKTRACE */
 }
 
-void model_print_summary(void)
+void assert_hook(void)
 {
-       model->print_summary();
+       model_print("Add breakpoint to line %u in file %s.\n", __LINE__, __FILE__);
 }
 
-void assert_hook(void)
+void model_assert(bool expr, const char *file, int line)
 {
-       printf("Add breakpoint to line %u in file %s.\n",__LINE__,__FILE__);
+       if (!expr) {
+               char msg[100];
+               sprintf(msg, "Program has hit assertion in file %s at line %d\n",
+                                               file, line);
+               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 file descriptor '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.
+ */
+char filename[256];
+
+void redirect_output()
+{
+       /* Save stdout for later use */
+       model_out = dup(STDOUT_FILENO);
+       if (model_out < 0) {
+               perror("dup");
+               exit(EXIT_FAILURE);
+       }
+       snprintf_(filename, sizeof(filename), "C11FuzzerTmp%d", getpid());
+       fd_user_out = open(filename, O_CREAT | O_TRUNC| O_RDWR, S_IRWXU);
+       if (dup2(fd_user_out, STDOUT_FILENO) < 0) {
+               perror("dup2");
+               exit(EXIT_FAILURE);
+       }
+
+}
+
+/**
+ * @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);
+       close(fd_user_out);
+       unlink(filename);
+}
+
+/** @brief Print out any pending program output */
+void print_program_output()
+{
+       char buf[200];
+
+       model_print("---- BEGIN PROGRAM OUTPUT ----\n");
+
+       /* Gather all program output */
+       fflush(stdout);
+
+       lseek(fd_user_out, 0, SEEK_SET);
+       /* Read program output pipe and write to (real) stdout */
+       ssize_t ret;
+       while (1) {
+               ret = read_to_buf(fd_user_out, buf, sizeof(buf));
+               if (!ret)
+                       break;
+               while (ret > 0) {
+                       ssize_t res = write(model_out, buf, ret);
+                       if (res < 0) {
+                               perror("write");
+                               exit(EXIT_FAILURE);
+                       }
+                       ret -= res;
+               }
+       }
+
+       close(fd_user_out);
+       unlink(filename);
+
+       model_print("---- END PROGRAM OUTPUT   ----\n");
 }
+#endif /* ! CONFIG_DEBUG */