output redirection
[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 FILE *model_out;
18 int fd_user_out; /**< @brief File descriptor from which to read user program output */
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(stdout);
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 model_print_summary(void)
44 {
45         model->print_summary();
46 }
47
48 void assert_hook(void)
49 {
50         model_print("Add breakpoint to line %u in file %s.\n",__LINE__,__FILE__);
51 }
52
53 void model_assert(bool expr, const char *file, int line)
54 {
55         if (!expr) {
56                 char msg[100];
57                 sprintf(msg, "Program has hit assertion in file %s at line %d\n",
58                                 file, line);
59                 model->assert_user_bug(msg);
60         }
61 }
62
63 /**
64  * @brief Setup output redirecting
65  *
66  * Redirects user program's stdout to a pipe so that we can dump it
67  * selectively, when displaying bugs, etc.
68  * Also connects a special file 'model_out' directly to stdout, for printing
69  * data when needed.
70  *
71  * The model-checker can selectively choose to print/hide the user program
72  * output.
73  * @see clear_program_output
74  * @see print_program_output
75  *
76  * Note that the user program's pipe has limited memory, so if a program will
77  * output much data, we will need to buffer it in user-space during execution.
78  * This also means that if ModelChecker decides not to print an execution, it
79  * should promptly clear the pipe.
80  */
81 void redirect_output()
82 {
83         int fd;
84
85         /* Save stdout for later use */
86         fd = dup(fileno(stdout));
87         model_out = fdopen(fd, "w");
88
89         /* Redirect program output to a pipe */
90         int pipefd[2];
91         if (pipe(pipefd) < 0) {
92                 perror("pipe");
93                 exit(EXIT_FAILURE);
94         }
95         fd = dup2(pipefd[1], fileno(stdout)); // STDOUT_FILENO
96         close(pipefd[1]);
97
98         /* Save the "read" side of the pipe for use later */
99         fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
100         fd_user_out = pipefd[0];
101 }
102
103 /**
104  * @brief Wrapper for reading data to buffer
105  *
106  * Besides a simple read, this handles the subtleties of EOF and nonblocking
107  * input (if fd is O_NONBLOCK).
108  *
109  * @param fd The file descriptor to read.
110  * @param buf Buffer to read to.
111  * @param maxlen Maximum data to read to buffer
112  * @return The length of data read. If zero, then we hit EOF or ran out of data
113  * (non-blocking)
114  */
115 static ssize_t read_to_buf(int fd, char *buf, size_t maxlen)
116 {
117         ssize_t ret = read(fd, buf, maxlen);
118         if (ret < 0) {
119                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
120                         return 0;
121                 } else {
122                         perror("read");
123                         exit(EXIT_FAILURE);
124                 }
125         }
126         return ret;
127 }
128
129 /** @brief Dump any pending program output without printing */
130 void clear_program_output()
131 {
132         fflush(stdout);
133         char buf[200];
134         while (read_to_buf(fd_user_out, buf, sizeof(buf)));
135 }
136
137 /** @brief Print out any pending program output */
138 void print_program_output()
139 {
140         char buf[200];
141
142         /* Gather all program output */
143         fflush(stdout);
144
145         /* Read program output pipe and write to (real) stdout */
146         int ret;
147         while (1) {
148                 ret = read_to_buf(fd_user_out, buf, sizeof(buf));
149                 if (!ret)
150                         break;
151                 while (ret > 0) {
152                         int res = fwrite(buf, 1, ret, model_out);
153                         if (res < 0) {
154                                 errno = ferror(model_out);
155                                 perror("fwrite");
156                                 exit(EXIT_FAILURE);
157                         }
158                         ret -= res;
159                 }
160         }
161 }