Used IWYU to partially clean up some includes
[alexxy/gromacs.git] / src / gromacs / utility / fatalerror.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "fatalerror.h"
40
41 #include "config.h"
42
43 #include <cerrno>
44 #include <cstddef>
45 #include <cstdlib>
46 #include <cstring>
47
48 #include <exception>
49
50 #include "thread_mpi/threads.h"
51
52 #include "gromacs/utility/baseversion.h"
53 #include "gromacs/utility/cstringutil.h"
54 #include "gromacs/utility/futil.h"
55 #include "gromacs/utility/programcontext.h"
56
57 #ifdef GMX_MPI
58 #include "gromacs/utility/basenetwork.h"
59 #include "gromacs/utility/gmxmpi.h"
60 #else
61 #include "gromacs/utility/common.h"
62 #endif
63
64 static bool                bDebug         = false;
65 static tMPI_Thread_mutex_t where_mutex    = TMPI_THREAD_MUTEX_INITIALIZER;
66
67 FILE                      *debug          = NULL;
68 gmx_bool                   gmx_debug_at   = FALSE;
69
70 static FILE               *log_file       = NULL;
71 static tMPI_Thread_mutex_t error_mutex    = TMPI_THREAD_MUTEX_INITIALIZER;
72 static const char *const   gmxuser
73     = "Please report this to the mailing list (gmx-users@gromacs.org)";
74
75 void gmx_init_debug(const int dbglevel, const char *dbgfile)
76 {
77     if (!bDebug)
78     {
79         gmx_disable_file_buffering();
80         debug  = gmx_ffopen(dbgfile, "w+");
81         bDebug = true;
82         if (dbglevel >= 2)
83         {
84             gmx_debug_at = TRUE;
85         }
86     }
87 }
88
89 gmx_bool bDebugMode(void)
90 {
91     return bDebug;
92 }
93
94 void _where(const char *file, int line)
95 {
96     static gmx_bool bFirst = TRUE;
97     static int      nskip  = -1;
98     static int      nwhere =  0;
99     FILE           *fp;
100     char           *temp;
101
102     if (bFirst)
103     {
104         tMPI_Thread_mutex_lock(&where_mutex);
105         if (bFirst) /* we repeat the check in the locked section because things
106                        might have changed */
107         {
108             if ((temp = getenv("GMX_PRINT_DEBUG_LINES")) != NULL)
109             {
110                 nskip = strtol(temp, NULL, 10);
111             }
112             bFirst = FALSE;
113         }
114         tMPI_Thread_mutex_unlock(&where_mutex);
115     }
116
117     if (nskip >= 0)
118     {
119         /* Skip the first n occasions, this allows to see where it goes wrong */
120         if (nwhere >= nskip)
121         {
122             if (log_file)
123             {
124                 fp = log_file;
125             }
126             else
127             {
128                 fp = stderr;
129             }
130             fprintf(fp, "WHERE %d, file %s - line %d\n", nwhere, file, line);
131         }
132         nwhere++;
133     }
134 }
135
136 void gmx_fatal_set_log_file(FILE *fp)
137 {
138     log_file = fp;
139 }
140
141 static int fatal_errno = 0;
142
143 static void default_error_handler(const char *msg)
144 {
145     tMPI_Thread_mutex_lock(&error_mutex);
146     if (fatal_errno == 0)
147     {
148         if (log_file)
149         {
150             fprintf(log_file, "%s\n", msg);
151         }
152         fprintf(stderr, "%s\n", msg);
153         /* we set it to no-zero because if this function is called, something
154            has gone wrong */
155         fatal_errno = 255;
156     }
157     else
158     {
159         if (fatal_errno != -1)
160         {
161             errno = fatal_errno;
162         }
163         perror(msg);
164     }
165     tMPI_Thread_mutex_unlock(&error_mutex);
166 }
167
168 static void (*gmx_error_handler)(const char *msg) = default_error_handler;
169
170 void set_gmx_error_handler(void (*func)(const char *msg))
171 {
172     // TODO: Either this is unnecessary, or also reads to the handler should be
173     // protected by a mutex.
174     tMPI_Thread_mutex_lock(&error_mutex);
175     gmx_error_handler = func;
176     tMPI_Thread_mutex_unlock(&error_mutex);
177 }
178
179 static void call_error_handler(const char *key, const char *file, int line, const char *msg)
180 {
181     char        buf[10240], errerrbuf[1024];
182     const char *llines = "-------------------------------------------------------";
183     char       *strerr;
184
185     if (msg == NULL)
186     {
187         sprintf(errerrbuf, "Empty fatal_error message. %s", gmxuser);
188     }
189     // In case ProgramInfo is not initialized and there is an issue with the
190     // initialization, fall back to "GROMACS".
191     const char *programName = "GROMACS";
192     try
193     {
194         programName = gmx::getProgramContext().displayName();
195     }
196     catch (const std::exception &)
197     {
198     }
199
200     strerr = gmx_strerror(key);
201     sprintf(buf, "\n%s\nProgram %s, %s\n"
202             "Source code file: %s, line: %d\n\n"
203             "%s:\n%s\nFor more information and tips for troubleshooting, please check the GROMACS\n"
204             "website at http://www.gromacs.org/Documentation/Errors\n%s\n",
205             llines, programName, gmx_version(), file, line,
206             strerr, msg ? msg : errerrbuf, llines);
207     free(strerr);
208
209     gmx_error_handler(buf);
210 }
211
212 gmx_noreturn static void do_exit(bool bMaster, bool bFinalize)
213 {
214     if (debug)
215     {
216         fflush(debug);
217     }
218
219 #ifdef GMX_MPI
220     if (gmx_mpi_initialized())
221     {
222         if (bFinalize)
223         {
224             /* Broadcast the fatal error number possibly modified
225              * on the master process, in case the user would like
226              * to use the return status on a non-master process.
227              * The master process in cr and dd always has global rank 0.
228              */
229             MPI_Bcast(&fatal_errno, sizeof(fatal_errno), MPI_BYTE,
230                       0, MPI_COMM_WORLD);
231
232             /* Finalize nicely instead of aborting */
233             MPI_Finalize();
234         }
235         else if (bMaster)
236         {
237 #ifdef GMX_LIB_MPI
238             gmx_abort(1);
239 #endif
240         }
241         else
242         {
243             /* Let all other processes wait till the master has printed
244              * the error message and issued MPI_Abort.
245              */
246             MPI_Barrier(MPI_COMM_WORLD);
247         }
248     }
249 #else
250     GMX_UNUSED_VALUE(bMaster);
251     GMX_UNUSED_VALUE(bFinalize);
252 #endif
253
254     if (bDebugMode())
255     {
256         std::abort();
257     }
258     std::exit(1);
259 }
260
261 void gmx_fatal_mpi_va(int f_errno, const char *file, int line,
262                       gmx_bool bMaster, gmx_bool bFinalize,
263                       const char *fmt, va_list ap)
264 {
265     if (bMaster)
266     {
267         char msg[STRLEN];
268         vsprintf(msg, fmt, ap);
269
270         tMPI_Thread_mutex_lock(&error_mutex);
271         fatal_errno = f_errno;
272         tMPI_Thread_mutex_unlock(&error_mutex);
273
274         call_error_handler("fatal", file, line, msg);
275     }
276
277     do_exit(bMaster, bFinalize);
278 }
279
280 void gmx_fatal(int f_errno, const char *file, int line, const char *fmt, ...)
281 {
282     va_list ap;
283     va_start(ap, fmt);
284     gmx_fatal_mpi_va(f_errno, file, line, TRUE, FALSE, fmt, ap);
285     va_end(ap);
286 }
287
288 char *gmx_strerror(const char *key)
289 {
290     typedef struct {
291         const char *key, *msg;
292     } error_msg_t;
293     error_msg_t msg[] = {
294         { "bug",    "Possible bug" },
295         { "call",   "Routine should not have been called" },
296         { "comm",   "Communication (parallel processing) problem" },
297         { "fatal",  "Fatal error" },
298         { "cmd",    "Invalid command line argument" },
299         { "file",   "File input/output error" },
300         { "impl",   "Implementation restriction" },
301         { "incons", "Software inconsistency error" },
302         { "input",  "Input error or input inconsistency" },
303         { "mem",    "Memory allocation/freeing error" },
304         { "open",   "Can not open file" },
305         { "range",  "Range checking error" },
306         { NULL,     NULL}
307     };
308
309     if (key == NULL)
310     {
311         return strdup("Empty message");
312     }
313     else
314     {
315         for (size_t i = 0; msg[i].key != NULL; ++i)
316         {
317             if (strcmp(key, msg[i].key) == 0)
318             {
319                 return strdup(msg[i].msg);
320             }
321         }
322         char buf[1024];
323         sprintf(buf, "No error message associated with key %s\n%s", key, gmxuser);
324         return strdup(buf);
325     }
326 }
327
328
329 void _gmx_error(const char *key, const char *msg, const char *file, int line)
330 {
331     call_error_handler(key, file, line, msg);
332     do_exit(true, false);
333 }
334
335 void _range_check(int n, int n_min, int n_max, const char *warn_str,
336                   const char *var, const char *file, int line)
337 {
338     char buf[1024];
339
340     if ((n < n_min) || (n >= n_max))
341     {
342         if (warn_str != NULL)
343         {
344             strcpy(buf, warn_str);
345             strcat(buf, "\n");
346         }
347         else
348         {
349             buf[0] = '\0';
350         }
351
352         sprintf(buf+strlen(buf), "Variable %s has value %d. It should have been "
353                 "within [ %d .. %d ]\n", var, n, n_min, n_max);
354
355         _gmx_error("range", buf, file, line);
356     }
357 }
358
359 void gmx_warning(const char *fmt, ...)
360 {
361     va_list ap;
362     char    msg[STRLEN];
363
364     va_start(ap, fmt);
365     vsprintf(msg, fmt, ap);
366     va_end(ap);
367
368     fprintf(stderr, "\nWARNING: %s\n\n", msg);
369 }