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