Update my e-mail address on author lines.
[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@gmail.com>
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 /*! \brief
221  * Prints error information for an exception object.
222  *
223  * \param[in] fp      File to write the information out to (typically stderr).
224  * \param[in] ex      Exception object to print.
225  * \param[in] indent  Indentation for the information.
226  *
227  * If the exception contains nested exceptions, information from them is
228  * recursively printed.
229  */
230 void printExceptionMessage(FILE *fp, const std::exception &ex, int indent)
231 {
232     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
233     if (boostEx != NULL)
234     {
235         // TODO: Remove duplicate context if present in multiple nested exceptions.
236         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
237         if (msg != NULL)
238         {
239             while (msg != NULL && msg->isContext())
240             {
241                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
242                 ++indent;
243                 msg = &msg->child();
244             }
245             if (msg != NULL && !msg->text().empty())
246             {
247                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
248             }
249         }
250         else
251         {
252             internal::printFatalErrorMessageLine(fp, ex.what(), indent);
253         }
254
255         const int *errorNumber
256             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
257         if (errorNumber != NULL)
258         {
259             std::fprintf(fp, "%*sReason: %s\n", (indent+1)*2, "",
260                          std::strerror(*errorNumber));
261             const char * const *funcName
262                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
263             if (funcName != NULL)
264             {
265                 std::fprintf(fp, "%*s(call to %s() returned error code %d)\n",
266                              (indent+1)*2, "", *funcName, *errorNumber);
267             }
268         }
269
270         // TODO: Treat also boost::nested_exception (not currently used, though)
271
272         const internal::NestedExceptionList *nested
273             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
274         if (nested != NULL)
275         {
276             internal::NestedExceptionList::const_iterator ni;
277             for (ni = nested->begin(); ni != nested->end(); ++ni)
278             {
279                 try
280                 {
281                     rethrow_exception(*ni);
282                 }
283                 catch (const std::exception &nestedEx)
284                 {
285                     printExceptionMessage(fp, nestedEx, indent + 1);
286                 }
287             }
288         }
289     }
290     else
291     {
292         internal::printFatalErrorMessageLine(fp, ex.what(), indent);
293     }
294 }
295
296 }   // namespace
297
298 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
299 {
300     const char             *title      = "Unknown exception";
301     bool                    bPrintType = false;
302     const GromacsException *gmxEx      = dynamic_cast<const GromacsException *>(&ex);
303     // TODO: Treat more of the standard exceptions
304     if (gmxEx != NULL)
305     {
306         title = getErrorCodeString(gmxEx->errorCode());
307     }
308     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
309     {
310         title = "System error in thread synchronization";
311     }
312     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
313     {
314         title = "Memory allocation failed";
315     }
316     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
317     {
318         title      = "Standard library logic error (bug)";
319         bPrintType = true;
320     }
321     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
322     {
323         title      = "Standard library runtime error (possible bug)";
324         bPrintType = true;
325     }
326     else
327     {
328         bPrintType = true;
329     }
330     // We can't call get_error_info directly on ex since our internal boost
331     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
332     // here instead.
333     const char *const      *funcPtr = NULL;
334     const char *const      *filePtr = NULL;
335     const int              *linePtr = NULL;
336     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
337     if (boostEx != NULL)
338     {
339         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
340         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
341         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
342     }
343     internal::printFatalErrorHeader(fp, title,
344                                     funcPtr != NULL ? *funcPtr : NULL,
345                                     filePtr != NULL ? *filePtr : NULL,
346                                     linePtr != NULL ? *linePtr : 0);
347     if (bPrintType)
348     {
349         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
350     }
351     printExceptionMessage(fp, ex, 0);
352     internal::printFatalErrorFooter(fp);
353 }
354
355 } // namespace gmx