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