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