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