Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / utility / exceptions.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements classes and functions in exceptions.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_utility
37  */
38 #include "exceptions.h"
39
40 #include <cstring>
41
42 #include <new>
43 #include <stdexcept>
44 #include <typeinfo>
45
46 #include <boost/exception/get_error_info.hpp>
47 #include <boost/shared_ptr.hpp>
48
49 #include "gromacs/legacyheaders/thread_mpi/system_error.h"
50 #include "gromacs/utility/errorcodes.h"
51 #include "gromacs/utility/gmxassert.h"
52
53 #include "errorformat.h"
54
55 namespace gmx
56 {
57
58 namespace
59 {
60
61 /********************************************************************
62  * ErrorMessage
63  */
64
65 class ErrorMessage
66 {
67     public:
68         /*! \brief
69          * Creates an error message object with the specified text.
70          *
71          * \param[in] text  Text for the message.
72          */
73         explicit ErrorMessage(const std::string &text);
74
75         //! Whether this object is a context string.
76         bool isContext() const { return child_; }
77         //! Returns the text for this object.
78         const std::string &text() const { return text_; }
79         /*! \brief
80          * Returns the child object for a context object.
81          *
82          * Must not be called if isContext() returns false.
83          */
84         const ErrorMessage &child() const
85         {
86             GMX_ASSERT(isContext(),
87                        "Attempting to access nonexistent message object");
88             return *child_;
89         }
90
91         /*! \brief
92          * Creates a new message object with context prepended.
93          *
94          * \param[in] context  Context string to add.
95          * \returns   New error message object that has \p context as its text
96          *      and \c this as its child.
97          * \throws    std::bad_alloc if out of memory.
98          */
99         ErrorMessage prependContext(const std::string &context) const;
100
101     private:
102         std::string                     text_;
103         boost::shared_ptr<ErrorMessage> child_;
104 };
105
106 /*! \internal \brief
107  * Stores a reason or the top-most context string of an exception.
108  *
109  * \ingroup module_utility
110  */
111 typedef boost::error_info<struct errinfo_message_, ErrorMessage>
112         errinfo_message;
113
114 ErrorMessage::ErrorMessage(const std::string &text)
115     : text_(text)
116 {
117     size_t length = text_.find_last_not_of(" \n");
118     if (length == std::string::npos)
119     {
120         length = text_.length() - 1;
121     }
122     text_.resize(length + 1);
123 }
124
125 ErrorMessage
126 ErrorMessage::prependContext(const std::string &context) const
127 {
128     ErrorMessage newMessage(context);
129     newMessage.child_.reset(new ErrorMessage(*this));
130     return newMessage;
131 }
132
133 /*! \brief
134  * Stores list of nested exceptions for Gromacs exceptions.
135  *
136  * \ingroup module_utility
137  */
138 typedef boost::error_info<struct errinfo_message_, internal::NestedExceptionList>
139         errinfo_nested_exceptions;
140
141 } // namespace
142
143 /********************************************************************
144  * GromacsException
145  */
146
147 GromacsException::GromacsException(const ExceptionInitializer &details)
148 {
149     *this << errinfo_message(ErrorMessage(details.reason_));
150     if (details.hasNestedExceptions())
151     {
152         *this << errinfo_nested_exceptions(details.nested_);
153     }
154 }
155
156 const char *GromacsException::what() const throw()
157 {
158     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
159     while (msg != NULL && msg->isContext())
160     {
161         msg = &msg->child();
162     }
163     return msg != NULL ? msg->text().c_str() : "No reason provided";
164 }
165
166 void GromacsException::prependContext(const std::string &context)
167 {
168     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
169     GMX_RELEASE_ASSERT(msg != NULL, "Message should always be set");
170     *this << errinfo_message(msg->prependContext(context));
171 }
172
173 /********************************************************************
174  * Derived exception classes
175  */
176
177 int FileIOError::errorCode() const
178 {
179     return eeFileIO;
180 }
181
182 int InvalidInputError::errorCode() const
183 {
184     return eeInvalidInput;
185 }
186
187 int InconsistentInputError::errorCode() const
188 {
189     return eeInconsistentInput;
190 }
191
192 int SimulationInstabilityError::errorCode() const
193 {
194     return eeInstability;
195 }
196
197 int InternalError::errorCode() const
198 {
199     return eeInternalError;
200 }
201
202 int APIError::errorCode() const
203 {
204     return eeAPIError;
205 }
206
207 int NotImplementedError::errorCode() const
208 {
209     return eeNotImplemented;
210 }
211
212
213 /********************************************************************
214  * Global functions
215  */
216
217 namespace
218 {
219
220 void printExceptionMessage(FILE *fp, const std::exception &ex, int indent)
221 {
222     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
223     if (boostEx != NULL)
224     {
225         // TODO: Remove duplicate context if present in multiple nested exceptions.
226         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
227         if (msg != NULL)
228         {
229             while (msg != NULL && msg->isContext())
230             {
231                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
232                 ++indent;
233                 msg = &msg->child();
234             }
235             if (msg != NULL && !msg->text().empty())
236             {
237                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
238             }
239         }
240         else
241         {
242             internal::printFatalErrorMessageLine(fp, ex.what(), indent);
243         }
244
245         const int *errorNumber
246             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
247         if (errorNumber != NULL)
248         {
249             std::fprintf(fp, "%*sReason: %s\n", (indent+1)*2, "",
250                          std::strerror(*errorNumber));
251             const char * const *funcName
252                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
253             if (funcName != NULL)
254             {
255                 std::fprintf(fp, "%*s(call to %s() returned error code %d)\n",
256                              (indent+1)*2, "", *funcName, *errorNumber);
257             }
258         }
259
260         // TODO: Treat also boost::nested_exception (not currently used, though)
261
262         const internal::NestedExceptionList *nested
263             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
264         if (nested != NULL)
265         {
266             internal::NestedExceptionList::const_iterator ni;
267             for (ni = nested->begin(); ni != nested->end(); ++ni)
268             {
269                 try
270                 {
271                     rethrow_exception(*ni);
272                 }
273                 catch (const std::exception &nestedEx)
274                 {
275                     printExceptionMessage(fp, nestedEx, indent + 1);
276                 }
277             }
278         }
279     }
280     else
281     {
282         internal::printFatalErrorMessageLine(fp, ex.what(), indent);
283     }
284 }
285
286 } // namespace
287
288 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
289 {
290     const char *title = "Unknown exception";
291     bool bPrintType = false;
292     const GromacsException *gmxEx = dynamic_cast<const GromacsException *>(&ex);
293     // TODO: Treat more of the standard exceptions
294     if (gmxEx != NULL)
295     {
296         title = getErrorCodeString(gmxEx->errorCode());
297     }
298     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
299     {
300         title = "System error in thread synchronization";
301     }
302     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
303     {
304         title = "Memory allocation failed";
305     }
306     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
307     {
308         title = "Standard library logic error (bug)";
309         bPrintType = true;
310     }
311     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
312     {
313         title = "Standard library runtime error (possible bug)";
314         bPrintType = true;
315     }
316     else
317     {
318         bPrintType = true;
319     }
320     // We can't call get_error_info directly on ex since our internal boost
321     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
322     // here instead.
323     const char *const *funcPtr = NULL;
324     const char *const *filePtr = NULL;
325     const int         *linePtr = NULL;
326     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
327     if (boostEx != NULL)
328     {
329         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
330         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
331         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
332     }
333     internal::printFatalErrorHeader(fp, title,
334                                     funcPtr != NULL ? *funcPtr : NULL,
335                                     filePtr != NULL ? *filePtr : NULL,
336                                     linePtr != NULL ? *linePtr : 0);
337     if (bPrintType)
338     {
339         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
340     }
341     printExceptionMessage(fp, ex, 0);
342     internal::printFatalErrorFooter(fp);
343 }
344
345 } // namespace gmx