Merge branch release-5-0 into release-5-1
[alexxy/gromacs.git] / src / gromacs / utility / exceptions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes and functions in exceptions.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_utility
41  */
42 #include "gmxpre.h"
43
44 #include "exceptions.h"
45
46 #include "config.h"
47
48 #include <cstring>
49
50 #include <new>
51 #include <stdexcept>
52 #include <typeinfo>
53
54 #include <boost/shared_ptr.hpp>
55 #include <boost/exception/get_error_info.hpp>
56
57 #include "thread_mpi/system_error.h"
58
59 #include "gromacs/utility/basenetwork.h"
60 #include "gromacs/utility/errorcodes.h"
61 #include "gromacs/utility/gmxassert.h"
62 #include "gromacs/utility/stringutil.h"
63
64 #include "errorformat.h"
65
66 namespace gmx
67 {
68
69 namespace
70 {
71
72 /********************************************************************
73  * ErrorMessage
74  */
75
76 /*! \brief
77  * Error message or error context text item.
78  *
79  * Error messages for an exception are represented as a chain of ErrorMessage
80  * objects: the elements at the bottom of the chain (with no children) is the
81  * error message, and other elements are the context strings added.
82  *
83  * \ingroup module_utility
84  */
85 class ErrorMessage
86 {
87     public:
88         /*! \brief
89          * Creates an error message object with the specified text.
90          *
91          * \param[in] text  Text for the message.
92          */
93         explicit ErrorMessage(const std::string &text);
94
95         //! Whether this object is a context string.
96         bool isContext() const { return static_cast<bool>(child_); }
97         //! Returns the text for this object.
98         const std::string &text() const { return text_; }
99         /*! \brief
100          * Returns the child object for a context object.
101          *
102          * Must not be called if isContext() returns false.
103          */
104         const ErrorMessage &child() const
105         {
106             GMX_ASSERT(isContext(),
107                        "Attempting to access nonexistent message object");
108             return *child_;
109         }
110
111         /*! \brief
112          * Creates a new message object with context prepended.
113          *
114          * \param[in] context  Context string to add.
115          * \returns   New error message object that has \p context as its text
116          *      and \c this as its child.
117          * \throws    std::bad_alloc if out of memory.
118          */
119         ErrorMessage prependContext(const std::string &context) const;
120
121     private:
122         std::string                     text_;
123         boost::shared_ptr<ErrorMessage> child_;
124 };
125
126 /*! \internal \brief
127  * Stores a reason or the top-most context string of an exception.
128  *
129  * \ingroup module_utility
130  */
131 typedef boost::error_info<struct errinfo_message_, ErrorMessage>
132     errinfo_message;
133
134 ErrorMessage::ErrorMessage(const std::string &text)
135     : text_(text)
136 {
137     size_t length = text_.find_last_not_of(" \n");
138     if (length == std::string::npos)
139     {
140         length = text_.length() - 1;
141     }
142     text_.resize(length + 1);
143 }
144
145 ErrorMessage
146 ErrorMessage::prependContext(const std::string &context) const
147 {
148     ErrorMessage newMessage(context);
149     newMessage.child_.reset(new ErrorMessage(*this));
150     return newMessage;
151 }
152
153 /*! \brief
154  * Stores list of nested exceptions for Gromacs exceptions.
155  *
156  * \ingroup module_utility
157  */
158 typedef boost::error_info<struct errinfo_message_, internal::NestedExceptionList>
159     errinfo_nested_exceptions;
160
161 }   // namespace
162
163 /********************************************************************
164  * GromacsException
165  */
166
167 GromacsException::GromacsException(const ExceptionInitializer &details)
168 {
169     *this << errinfo_message(ErrorMessage(details.reason_));
170     if (details.hasNestedExceptions())
171     {
172         *this << errinfo_nested_exceptions(details.nested_);
173     }
174 }
175
176 const char *GromacsException::what() const throw()
177 {
178     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
179     if (msg == NULL)
180     {
181         return "No reason provided";
182     }
183     while (msg->isContext())
184     {
185         msg = &msg->child();
186     }
187     return msg->text().c_str();
188 }
189
190 void GromacsException::prependContext(const std::string &context)
191 {
192     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
193     GMX_RELEASE_ASSERT(msg != NULL, "Message should always be set");
194     *this << errinfo_message(msg->prependContext(context));
195 }
196
197 /********************************************************************
198  * Derived exception classes
199  */
200
201 int FileIOError::errorCode() const
202 {
203     return eeFileIO;
204 }
205
206 int InvalidInputError::errorCode() const
207 {
208     return eeInvalidInput;
209 }
210
211 int InconsistentInputError::errorCode() const
212 {
213     return eeInconsistentInput;
214 }
215
216 int SimulationInstabilityError::errorCode() const
217 {
218     return eeInstability;
219 }
220
221 int InternalError::errorCode() const
222 {
223     return eeInternalError;
224 }
225
226 int APIError::errorCode() const
227 {
228     return eeAPIError;
229 }
230
231 int NotImplementedError::errorCode() const
232 {
233     return eeNotImplemented;
234 }
235
236
237 /********************************************************************
238  * Global functions
239  */
240
241 namespace
242 {
243
244 //! \addtogroup module_utility
245 //! \{
246
247 /*! \brief
248  * Abstracts actual output from the other logic in exception formatting.
249  *
250  * Object that implements this interface is passed to
251  * formatExceptionMessageInternal(), and is responsible for composing the
252  * output.  This allows using the same implementation of interpreting the
253  * exceptions while still supporting output to different formats (e.g., to a
254  * string or to \c stderr).
255  */
256 class MessageWriterInterface
257 {
258     public:
259         virtual ~MessageWriterInterface() {}
260
261         /*! \brief
262          * Writes a single line of text into the output.
263          *
264          * \param[in] text    Text to write on the line.
265          * \param[in] indent  Suggested number of spaces to indent the line.
266          */
267         virtual void writeLine(const char *text, int indent) = 0;
268         /*! \brief
269          * Writes information about a system error (errno-based).
270          *
271          * \param[in] errorNumber  errno value
272          * \param[in] funcName     Name of the system call (can be NULL).
273          * \param[in] indent       Suggested number of spaces to indent the output.
274          */
275         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
276                                     int indent) = 0;
277 };
278
279 /*! \brief
280  * Exception information writer for cases where exceptions should be avoided.
281  *
282  * Formats the messages into the provided FILE handle without checking for
283  * errors in std::fprintf() calls.
284  */
285 class MessageWriterFileNoThrow : public MessageWriterInterface
286 {
287     public:
288         //! Initializes a writer that writes to the given file handle.
289         explicit MessageWriterFileNoThrow(FILE *fp) : fp_(fp) {}
290
291         virtual void writeLine(const char *text, int indent)
292         {
293             internal::printFatalErrorMessageLine(fp_, text, indent);
294         }
295         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
296                                     int indent)
297         {
298             std::fprintf(fp_, "%*sReason: %s\n", indent, "",
299                          std::strerror(errorNumber));
300             if (funcName != NULL)
301             {
302                 std::fprintf(fp_, "%*s(call to %s() returned error code %d)\n",
303                              indent, "", funcName, errorNumber);
304             }
305         }
306
307     private:
308         FILE                   *fp_;
309 };
310
311 /*! \brief
312  * Exception information writer to format into an std::string.
313  */
314 class MessageWriterString : public MessageWriterInterface
315 {
316     public:
317         //! Post-processes the output string to not end in a line feed.
318         void removeTerminatingLineFeed()
319         {
320             if (result_.size() > 0U)
321             {
322                 result_.erase(result_.size() - 1);
323             }
324         }
325         //! Returns the constructed string.
326         const std::string &result() const { return result_; }
327
328         virtual void writeLine(const char *text, int indent)
329         {
330             result_.append(indent, ' ');
331             result_.append(text);
332             result_.append("\n");
333         }
334         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
335                                     int indent)
336         {
337             writeLine(formatString("Reason: %s", std::strerror(errorNumber)).c_str(),
338                       indent);
339             if (funcName != NULL)
340             {
341                 writeLine(formatString("(call to %s() returned error code %d)",
342                                        funcName, errorNumber).c_str(),
343                           indent);
344             }
345         }
346
347     private:
348         std::string             result_;
349 };
350
351 /*! \brief
352  * Prints error information for an exception object.
353  *
354  * \param[in] writer  Writer to write out the information.
355  * \param[in] ex      Exception object to print.
356  * \param[in] indent  Indentation for the information.
357  *
358  * If the exception contains nested exceptions, information from them is
359  * recursively printed.
360  *
361  * Does not throw unless the writer throws.
362  */
363 void formatExceptionMessageInternal(MessageWriterInterface *writer,
364                                     const std::exception &ex, int indent)
365 {
366     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
367     if (boostEx != NULL)
368     {
369         // TODO: Add an option to print this information for the tests
370         // const char *const *funcPtr =
371         //     boost::get_error_info<boost::throw_function>(*boostEx);
372         // const char *const *filePtr =
373         //     boost::get_error_info<boost::throw_file>(*boostEx);
374         // const int         *linePtr =
375         //     boost::get_error_info<boost::throw_line>(*boostEx);
376
377         // std::string        result;
378         // if (filePtr != NULL && linePtr != NULL)
379         // {
380         //     result = formatString("%s:%d: %s\n", *filePtr, *linePtr,
381         //                           funcPtr != NULL ? *funcPtr : "");
382         // }
383
384         bool                bAnythingWritten = false;
385         // TODO: Remove duplicate context if present in multiple nested exceptions.
386         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
387         if (msg != NULL)
388         {
389             while (msg != NULL && msg->isContext())
390             {
391                 writer->writeLine(msg->text().c_str(), indent*2);
392                 ++indent;
393                 msg = &msg->child();
394             }
395             if (msg != NULL && !msg->text().empty())
396             {
397                 writer->writeLine(msg->text().c_str(), indent*2);
398                 bAnythingWritten = true;
399             }
400         }
401         else
402         {
403             writer->writeLine(ex.what(), indent*2);
404             bAnythingWritten = true;
405         }
406
407         const int *errorNumber
408             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
409         if (errorNumber != NULL)
410         {
411             const char * const *funcName
412                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
413             writer->writeErrNoInfo(*errorNumber,
414                                    funcName != NULL ? *funcName : NULL,
415                                    (indent+1)*2);
416             bAnythingWritten = true;
417         }
418
419         // TODO: Treat also boost::nested_exception (not currently used, though)
420
421         const internal::NestedExceptionList *nested
422             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
423         if (nested != NULL)
424         {
425             internal::NestedExceptionList::const_iterator ni;
426             for (ni = nested->begin(); ni != nested->end(); ++ni)
427             {
428                 try
429                 {
430                     rethrow_exception(*ni);
431                 }
432                 catch (const std::exception &nestedEx)
433                 {
434                     const int newIndent = indent + (bAnythingWritten ? 1 : 0);
435                     formatExceptionMessageInternal(writer, nestedEx, newIndent);
436                 }
437             }
438         }
439     }
440     else
441     {
442         writer->writeLine(ex.what(), indent*2);
443     }
444 }
445
446 //! \}
447
448 }   // namespace
449
450 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
451 {
452     const char             *title      = "Unknown exception";
453     bool                    bPrintType = false;
454     const GromacsException *gmxEx      = dynamic_cast<const GromacsException *>(&ex);
455     // TODO: Treat more of the standard exceptions
456     if (gmxEx != NULL)
457     {
458         title = getErrorCodeString(gmxEx->errorCode());
459     }
460     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
461     {
462         title = "System error in thread synchronization";
463     }
464     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
465     {
466         title = "Memory allocation failed";
467     }
468     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
469     {
470         title      = "Standard library logic error (bug)";
471         bPrintType = true;
472     }
473     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
474     {
475         title      = "Standard library runtime error (possible bug)";
476         bPrintType = true;
477     }
478     else
479     {
480         bPrintType = true;
481     }
482     // We can't call get_error_info directly on ex since our internal boost
483     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
484     // here instead.
485     const char *const      *funcPtr = NULL;
486     const char *const      *filePtr = NULL;
487     const int              *linePtr = NULL;
488     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
489     if (boostEx != NULL)
490     {
491         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
492         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
493         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
494     }
495     internal::printFatalErrorHeader(fp, title,
496                                     funcPtr != NULL ? *funcPtr : NULL,
497                                     filePtr != NULL ? *filePtr : NULL,
498                                     linePtr != NULL ? *linePtr : 0);
499     if (bPrintType)
500     {
501         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
502     }
503     MessageWriterFileNoThrow writer(fp);
504     formatExceptionMessageInternal(&writer, ex, 0);
505     internal::printFatalErrorFooter(fp);
506 }
507
508 std::string formatExceptionMessageToString(const std::exception &ex)
509 {
510     MessageWriterString writer;
511     formatExceptionMessageInternal(&writer, ex, 0);
512     writer.removeTerminatingLineFeed();
513     return writer.result();
514 }
515
516 void formatExceptionMessageToFile(FILE *fp, const std::exception &ex)
517 {
518     MessageWriterFileNoThrow writer(fp);
519     formatExceptionMessageInternal(&writer, ex, 0);
520 }
521
522 int processExceptionAtExit(const std::exception & /*ex*/)
523 {
524     int returnCode = 1;
525 #ifdef GMX_LIB_MPI
526     // TODO: Consider moving the output done in gmx_abort() into the message
527     // printing routine above, so that this could become a simple MPI_Abort().
528     gmx_abort(returnCode);
529 #endif
530     return returnCode;
531 }
532
533 } // namespace gmx