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