1248b96ef7bec6b3933b59dcf2f9f92fed33f569
[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,2015, 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 #include "gromacs/utility/textwriter.h"
64
65 #include "errorformat.h"
66
67 namespace gmx
68 {
69
70 namespace
71 {
72
73 /********************************************************************
74  * ErrorMessage
75  */
76
77 /*! \brief
78  * Error message or error context text item.
79  *
80  * Error messages for an exception are represented as a chain of ErrorMessage
81  * objects: the elements at the bottom of the chain (with no children) is the
82  * error message, and other elements are the context strings added.
83  *
84  * \ingroup module_utility
85  */
86 class ErrorMessage
87 {
88     public:
89         /*! \brief
90          * Creates an error message object with the specified text.
91          *
92          * \param[in] text  Text for the message.
93          */
94         explicit ErrorMessage(const std::string &text);
95
96         //! Whether this object is a context string.
97         bool isContext() const { return static_cast<bool>(child_); }
98         //! Returns the text for this object.
99         const std::string &text() const { return text_; }
100         /*! \brief
101          * Returns the child object for a context object.
102          *
103          * Must not be called if isContext() returns false.
104          */
105         const ErrorMessage &child() const
106         {
107             GMX_ASSERT(isContext(),
108                        "Attempting to access nonexistent message object");
109             return *child_;
110         }
111
112         /*! \brief
113          * Creates a new message object with context prepended.
114          *
115          * \param[in] context  Context string to add.
116          * \returns   New error message object that has \p context as its text
117          *      and \c this as its child.
118          * \throws    std::bad_alloc if out of memory.
119          */
120         ErrorMessage prependContext(const std::string &context) const;
121
122     private:
123         std::string                     text_;
124         boost::shared_ptr<ErrorMessage> child_;
125 };
126
127 /*! \internal \brief
128  * Stores a reason or the top-most context string of an exception.
129  *
130  * \ingroup module_utility
131  */
132 typedef boost::error_info<struct errinfo_message_, ErrorMessage>
133     errinfo_message;
134
135 ErrorMessage::ErrorMessage(const std::string &text)
136     : text_(text)
137 {
138     size_t length = text_.find_last_not_of(" \n");
139     if (length == std::string::npos)
140     {
141         length = text_.length() - 1;
142     }
143     text_.resize(length + 1);
144 }
145
146 ErrorMessage
147 ErrorMessage::prependContext(const std::string &context) const
148 {
149     ErrorMessage newMessage(context);
150     newMessage.child_.reset(new ErrorMessage(*this));
151     return newMessage;
152 }
153
154 /*! \brief
155  * Stores list of nested exceptions for Gromacs exceptions.
156  *
157  * \ingroup module_utility
158  */
159 typedef boost::error_info<struct errinfo_message_, internal::NestedExceptionList>
160     errinfo_nested_exceptions;
161
162 }   // namespace
163
164 /********************************************************************
165  * GromacsException
166  */
167
168 GromacsException::GromacsException(const ExceptionInitializer &details)
169 {
170     *this << errinfo_message(ErrorMessage(details.reason_));
171     if (details.hasNestedExceptions())
172     {
173         *this << errinfo_nested_exceptions(details.nested_);
174     }
175 }
176
177 const char *GromacsException::what() const throw()
178 {
179     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
180     if (msg == NULL)
181     {
182         return "No reason provided";
183     }
184     while (msg->isContext())
185     {
186         msg = &msg->child();
187     }
188     return msg->text().c_str();
189 }
190
191 void GromacsException::prependContext(const std::string &context)
192 {
193     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
194     GMX_RELEASE_ASSERT(msg != NULL, "Message should always be set");
195     *this << errinfo_message(msg->prependContext(context));
196 }
197
198 /********************************************************************
199  * Derived exception classes
200  */
201
202 int FileIOError::errorCode() const
203 {
204     return eeFileIO;
205 }
206
207 int InvalidInputError::errorCode() const
208 {
209     return eeInvalidInput;
210 }
211
212 int InconsistentInputError::errorCode() const
213 {
214     return eeInconsistentInput;
215 }
216
217 int SimulationInstabilityError::errorCode() const
218 {
219     return eeInstability;
220 }
221
222 int InternalError::errorCode() const
223 {
224     return eeInternalError;
225 }
226
227 int APIError::errorCode() const
228 {
229     return eeAPIError;
230 }
231
232 int NotImplementedError::errorCode() const
233 {
234     return eeNotImplemented;
235 }
236
237
238 /********************************************************************
239  * Global functions
240  */
241
242 namespace
243 {
244
245 //! \addtogroup module_utility
246 //! \{
247
248 /*! \brief
249  * Abstracts actual output from the other logic in exception formatting.
250  *
251  * Object that implements this interface is passed to
252  * formatExceptionMessageInternal(), and is responsible for composing the
253  * output.  This allows using the same implementation of interpreting the
254  * exceptions while still supporting output to different formats (e.g., to a
255  * string or to \c stderr).
256  */
257 class MessageWriterInterface
258 {
259     public:
260         virtual ~MessageWriterInterface() {}
261
262         /*! \brief
263          * Writes a single line of text into the output.
264          *
265          * \param[in] text    Text to write on the line.
266          * \param[in] indent  Suggested number of spaces to indent the line.
267          */
268         virtual void writeLine(const char *text, int indent) = 0;
269         /*! \brief
270          * Writes information about a system error (errno-based).
271          *
272          * \param[in] errorNumber  errno value
273          * \param[in] funcName     Name of the system call (can be NULL).
274          * \param[in] indent       Suggested number of spaces to indent the output.
275          */
276         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
277                                     int indent) = 0;
278 };
279
280 /*! \brief
281  * Exception information writer for cases where exceptions should be avoided.
282  *
283  * Formats the messages into the provided FILE handle without checking for
284  * errors in std::fprintf() calls.
285  */
286 class MessageWriterFileNoThrow : public MessageWriterInterface
287 {
288     public:
289         //! Initializes a writer that writes to the given file handle.
290         explicit MessageWriterFileNoThrow(FILE *fp) : fp_(fp) {}
291
292         virtual void writeLine(const char *text, int indent)
293         {
294             internal::printFatalErrorMessageLine(fp_, text, indent);
295         }
296         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
297                                     int indent)
298         {
299             std::fprintf(fp_, "%*sReason: %s\n", indent, "",
300                          std::strerror(errorNumber));
301             if (funcName != NULL)
302             {
303                 std::fprintf(fp_, "%*s(call to %s() returned error code %d)\n",
304                              indent, "", funcName, errorNumber);
305             }
306         }
307
308     private:
309         FILE                   *fp_;
310 };
311
312 /*! \brief
313  * Exception information writer to format into a TextOutputStream.
314  */
315 class MessageWriterTextWriter : public MessageWriterInterface
316 {
317     public:
318         //! Initializes a writer that writes to the given stream.
319         explicit MessageWriterTextWriter(TextWriter *writer) : writer_(writer)
320         {
321         }
322
323         virtual void writeLine(const char *text, int indent)
324         {
325             // TODO: Line wrapping.
326             writer_->writeString(std::string(indent, ' '));
327             writer_->writeLine(text);
328         }
329         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
330                                     int indent)
331         {
332             writeLine(formatString("Reason: %s", std::strerror(errorNumber)).c_str(),
333                       indent);
334             if (funcName != NULL)
335             {
336                 writeLine(formatString("(call to %s() returned error code %d)",
337                                        funcName, errorNumber).c_str(),
338                           indent);
339             }
340         }
341
342     private:
343         TextWriter     *writer_;
344 };
345
346 /*! \brief
347  * Exception information writer to format into an std::string.
348  */
349 class MessageWriterString : public MessageWriterInterface
350 {
351     public:
352         //! Post-processes the output string to not end in a line feed.
353         void removeTerminatingLineFeed()
354         {
355             if (result_.size() > 0U)
356             {
357                 result_.erase(result_.size() - 1);
358             }
359         }
360         //! Returns the constructed string.
361         const std::string &result() const { return result_; }
362
363         virtual void writeLine(const char *text, int indent)
364         {
365             result_.append(indent, ' ');
366             result_.append(text);
367             result_.append("\n");
368         }
369         virtual void writeErrNoInfo(int errorNumber, const char *funcName,
370                                     int indent)
371         {
372             writeLine(formatString("Reason: %s", std::strerror(errorNumber)).c_str(),
373                       indent);
374             if (funcName != NULL)
375             {
376                 writeLine(formatString("(call to %s() returned error code %d)",
377                                        funcName, errorNumber).c_str(),
378                           indent);
379             }
380         }
381
382     private:
383         std::string             result_;
384 };
385
386 /*! \brief
387  * Prints error information for an exception object.
388  *
389  * \param[in] writer  Writer to write out the information.
390  * \param[in] ex      Exception object to print.
391  * \param[in] indent  Indentation for the information.
392  *
393  * If the exception contains nested exceptions, information from them is
394  * recursively printed.
395  *
396  * Does not throw unless the writer throws.
397  */
398 void formatExceptionMessageInternal(MessageWriterInterface *writer,
399                                     const std::exception &ex, int indent)
400 {
401     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
402     if (boostEx != NULL)
403     {
404         // TODO: Add an option to print this information for the tests
405         // const char *const *funcPtr =
406         //     boost::get_error_info<boost::throw_function>(*boostEx);
407         // const char *const *filePtr =
408         //     boost::get_error_info<boost::throw_file>(*boostEx);
409         // const int         *linePtr =
410         //     boost::get_error_info<boost::throw_line>(*boostEx);
411
412         // std::string        result;
413         // if (filePtr != NULL && linePtr != NULL)
414         // {
415         //     result = formatString("%s:%d: %s\n", *filePtr, *linePtr,
416         //                           funcPtr != NULL ? *funcPtr : "");
417         // }
418
419         bool                bAnythingWritten = false;
420         // TODO: Remove duplicate context if present in multiple nested exceptions.
421         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
422         if (msg != NULL)
423         {
424             while (msg != NULL && msg->isContext())
425             {
426                 writer->writeLine(msg->text().c_str(), indent*2);
427                 ++indent;
428                 msg = &msg->child();
429             }
430             if (msg != NULL && !msg->text().empty())
431             {
432                 writer->writeLine(msg->text().c_str(), indent*2);
433                 bAnythingWritten = true;
434             }
435         }
436         else
437         {
438             writer->writeLine(ex.what(), indent*2);
439             bAnythingWritten = true;
440         }
441
442         const int *errorNumber
443             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
444         if (errorNumber != NULL)
445         {
446             const char * const *funcName
447                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
448             writer->writeErrNoInfo(*errorNumber,
449                                    funcName != NULL ? *funcName : NULL,
450                                    (indent+1)*2);
451             bAnythingWritten = true;
452         }
453
454         // TODO: Treat also boost::nested_exception (not currently used, though)
455
456         const internal::NestedExceptionList *nested
457             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
458         if (nested != NULL)
459         {
460             internal::NestedExceptionList::const_iterator ni;
461             for (ni = nested->begin(); ni != nested->end(); ++ni)
462             {
463                 try
464                 {
465                     rethrow_exception(*ni);
466                 }
467                 catch (const std::exception &nestedEx)
468                 {
469                     const int newIndent = indent + (bAnythingWritten ? 1 : 0);
470                     formatExceptionMessageInternal(writer, nestedEx, newIndent);
471                 }
472             }
473         }
474     }
475     else
476     {
477         writer->writeLine(ex.what(), indent*2);
478     }
479 }
480
481 //! \}
482
483 }   // namespace
484
485 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
486 {
487     const char             *title      = "Unknown exception";
488     bool                    bPrintType = false;
489     const GromacsException *gmxEx      = dynamic_cast<const GromacsException *>(&ex);
490     // TODO: Treat more of the standard exceptions
491     if (gmxEx != NULL)
492     {
493         title = getErrorCodeString(gmxEx->errorCode());
494     }
495     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
496     {
497         title = "System error in thread synchronization";
498     }
499     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
500     {
501         title = "Memory allocation failed";
502     }
503     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
504     {
505         title      = "Standard library logic error (bug)";
506         bPrintType = true;
507     }
508     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
509     {
510         title      = "Standard library runtime error (possible bug)";
511         bPrintType = true;
512     }
513     else
514     {
515         bPrintType = true;
516     }
517     // We can't call get_error_info directly on ex since our internal boost
518     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
519     // here instead.
520     const char *const      *funcPtr = NULL;
521     const char *const      *filePtr = NULL;
522     const int              *linePtr = NULL;
523     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
524     if (boostEx != NULL)
525     {
526         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
527         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
528         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
529     }
530     internal::printFatalErrorHeader(fp, title,
531                                     funcPtr != NULL ? *funcPtr : NULL,
532                                     filePtr != NULL ? *filePtr : NULL,
533                                     linePtr != NULL ? *linePtr : 0);
534     if (bPrintType)
535     {
536         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
537     }
538     MessageWriterFileNoThrow writer(fp);
539     formatExceptionMessageInternal(&writer, ex, 0);
540     internal::printFatalErrorFooter(fp);
541 }
542
543 std::string formatExceptionMessageToString(const std::exception &ex)
544 {
545     MessageWriterString writer;
546     formatExceptionMessageInternal(&writer, ex, 0);
547     writer.removeTerminatingLineFeed();
548     return writer.result();
549 }
550
551 void formatExceptionMessageToFile(FILE *fp, const std::exception &ex)
552 {
553     MessageWriterFileNoThrow writer(fp);
554     formatExceptionMessageInternal(&writer, ex, 0);
555 }
556
557 void formatExceptionMessageToWriter(TextWriter           *writer,
558                                     const std::exception &ex)
559 {
560     MessageWriterTextWriter messageWriter(writer);
561     formatExceptionMessageInternal(&messageWriter, ex, 0);
562 }
563
564 int processExceptionAtExit(const std::exception & /*ex*/)
565 {
566     int returnCode = 1;
567 #ifdef GMX_LIB_MPI
568     // TODO: Consider moving the output done in gmx_abort() into the message
569     // printing routine above, so that this could become a simple MPI_Abort().
570     gmx_abort(returnCode);
571 #endif
572     return returnCode;
573 }
574
575 } // namespace gmx