bug fix
[c11tester.git] / common.cc
1 #include <execinfo.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <fcntl.h>
7
8 #include <model-assert.h>
9
10 #include "common.h"
11 #include "model.h"
12 #include "stacktrace.h"
13 #include "output.h"
14
15 #define MAX_TRACE_LEN 100
16
17 /** @brief Model-checker output file descriptor; default to stdout until redirected */
18 int model_out = STDOUT_FILENO;
19
20 #define CONFIG_STACKTRACE
21 /** Print a backtrace of the current program state. */
22 void print_trace(void)
23 {
24 #ifdef CONFIG_STACKTRACE
25         print_stacktrace(model_out);
26 #else
27         void *array[MAX_TRACE_LEN];
28         char **strings;
29         int size, i;
30
31         size = backtrace(array, MAX_TRACE_LEN);
32         strings = backtrace_symbols(array, size);
33
34         model_print("\nDumping stack trace (%d frames):\n", size);
35
36         for (i = 0;i < size;i++)
37                 model_print("\t%s\n", strings[i]);
38
39         free(strings);
40 #endif  /* CONFIG_STACKTRACE */
41 }
42
43 void assert_hook(void)
44 {
45         model_print("Add breakpoint to line %u in file %s.\n", __LINE__, __FILE__);
46 }
47
48 void model_assert(bool expr, const char *file, int line)
49 {
50         if (!expr) {
51                 char msg[100];
52                 sprintf(msg, "Program has hit assertion in file %s at line %d\n",
53                                                 file, line);
54                 model->assert_user_bug(msg);
55         }
56 }
57
58 #ifndef CONFIG_DEBUG
59
60 static int fd_user_out; /**< @brief File descriptor from which to read user program output */
61
62 /**
63  * @brief Setup output redirecting
64  *
65  * Redirects user program's stdout to a pipe so that we can dump it
66  * selectively, when displaying bugs, etc.
67  * Also connects a file descriptor 'model_out' directly to stdout, for printing
68  * data when needed.
69  *
70  * The model-checker can selectively choose to print/hide the user program
71  * output.
72  * @see clear_program_output
73  * @see print_program_output
74  *
75  * Note that the user program's pipe has limited memory, so if a program will
76  * output much data, we will need to buffer it in user-space during execution.
77  * This also means that if ModelChecker decides not to print an execution, it
78  * should promptly clear the pipe.
79  *
80  * This function should only be called once.
81  */
82 char filename[256];
83
84 void redirect_output()
85 {
86         /* Save stdout for later use */
87         model_out = dup(STDOUT_FILENO);
88         if (model_out < 0) {
89                 perror("dup");
90                 exit(EXIT_FAILURE);
91         }
92         snprintf_(filename, sizeof(filename), "C11FuzzerTmp%d", getpid());
93         fd_user_out = open(filename, O_CREAT | O_TRUNC| O_RDWR, S_IRWXU);
94         if (dup2(fd_user_out, STDOUT_FILENO) < 0) {
95                 perror("dup2");
96                 exit(EXIT_FAILURE);
97         }
98
99 }
100
101 /**
102  * @brief Wrapper for reading data to buffer
103  *
104  * Besides a simple read, this handles the subtleties of EOF and nonblocking
105  * input (if fd is O_NONBLOCK).
106  *
107  * @param fd The file descriptor to read.
108  * @param buf Buffer to read to.
109  * @param maxlen Maximum data to read to buffer
110  * @return The length of data read. If zero, then we hit EOF or ran out of data
111  * (non-blocking)
112  */
113 static ssize_t read_to_buf(int fd, char *buf, size_t maxlen)
114 {
115         ssize_t ret = read(fd, buf, maxlen);
116         if (ret < 0) {
117                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
118                         return 0;
119                 } else {
120                         perror("read");
121                         exit(EXIT_FAILURE);
122                 }
123         }
124         return ret;
125 }
126
127 /** @brief Dump any pending program output without printing */
128 void clear_program_output()
129 {
130         fflush(stdout);
131         close(fd_user_out);
132         unlink(filename);
133 }
134
135 /** @brief Print out any pending program output */
136 void print_program_output()
137 {
138         char buf[200];
139
140         model_print("---- BEGIN PROGRAM OUTPUT ----\n");
141
142         /* Gather all program output */
143         fflush(stdout);
144
145         lseek(fd_user_out, 0, SEEK_SET);
146         /* Read program output pipe and write to (real) stdout */
147         ssize_t ret;
148         while (1) {
149                 ret = read_to_buf(fd_user_out, buf, sizeof(buf));
150                 if (!ret)
151                         break;
152                 while (ret > 0) {
153                         ssize_t res = write(model_out, buf, ret);
154                         if (res < 0) {
155                                 perror("write");
156                                 exit(EXIT_FAILURE);
157                         }
158                         ret -= res;
159                 }
160         }
161
162         close(fd_user_out);
163         unlink(filename);
164
165         model_print("---- END PROGRAM OUTPUT   ----\n");
166 }
167 #endif  /* ! CONFIG_DEBUG */