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 #include <cstring>
45
46 #include <new>
47 #include <stdexcept>
48 #include <typeinfo>
49
50 #include <boost/exception/get_error_info.hpp>
51 #include <boost/shared_ptr.hpp>
52
53 #include "gromacs/legacyheaders/thread_mpi/system_error.h"
54 #include "gromacs/utility/errorcodes.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/stringutil.h"
57
58 #include "errorformat.h"
59
60 namespace gmx
61 {
62
63 namespace
64 {
65
66 /********************************************************************
67  * ErrorMessage
68  */
69
70 class ErrorMessage
71 {
72     public:
73         /*! \brief
74          * Creates an error message object with the specified text.
75          *
76          * \param[in] text  Text for the message.
77          */
78         explicit ErrorMessage(const std::string &text);
79
80         //! Whether this object is a context string.
81         bool isContext() const { return static_cast<bool>(child_); }
82         //! Returns the text for this object.
83         const std::string &text() const { return text_; }
84         /*! \brief
85          * Returns the child object for a context object.
86          *
87          * Must not be called if isContext() returns false.
88          */
89         const ErrorMessage &child() const
90         {
91             GMX_ASSERT(isContext(),
92                        "Attempting to access nonexistent message object");
93             return *child_;
94         }
95
96         /*! \brief
97          * Creates a new message object with context prepended.
98          *
99          * \param[in] context  Context string to add.
100          * \returns   New error message object that has \p context as its text
101          *      and \c this as its child.
102          * \throws    std::bad_alloc if out of memory.
103          */
104         ErrorMessage prependContext(const std::string &context) const;
105
106     private:
107         std::string                     text_;
108         boost::shared_ptr<ErrorMessage> child_;
109 };
110
111 /*! \internal \brief
112  * Stores a reason or the top-most context string of an exception.
113  *
114  * \ingroup module_utility
115  */
116 typedef boost::error_info<struct errinfo_message_, ErrorMessage>
117     errinfo_message;
118
119 ErrorMessage::ErrorMessage(const std::string &text)
120     : text_(text)
121 {
122     size_t length = text_.find_last_not_of(" \n");
123     if (length == std::string::npos)
124     {
125         length = text_.length() - 1;
126     }
127     text_.resize(length + 1);
128 }
129
130 ErrorMessage
131 ErrorMessage::prependContext(const std::string &context) const
132 {
133     ErrorMessage newMessage(context);
134     newMessage.child_.reset(new ErrorMessage(*this));
135     return newMessage;
136 }
137
138 /*! \brief
139  * Stores list of nested exceptions for Gromacs exceptions.
140  *
141  * \ingroup module_utility
142  */
143 typedef boost::error_info<struct errinfo_message_, internal::NestedExceptionList>
144     errinfo_nested_exceptions;
145
146 }   // namespace
147
148 /********************************************************************
149  * GromacsException
150  */
151
152 GromacsException::GromacsException(const ExceptionInitializer &details)
153 {
154     *this << errinfo_message(ErrorMessage(details.reason_));
155     if (details.hasNestedExceptions())
156     {
157         *this << errinfo_nested_exceptions(details.nested_);
158     }
159 }
160
161 const char *GromacsException::what() const throw()
162 {
163     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
164     while (msg != NULL && msg->isContext())
165     {
166         msg = &msg->child();
167     }
168     return msg != NULL ? msg->text().c_str() : "No reason provided";
169 }
170
171 void GromacsException::prependContext(const std::string &context)
172 {
173     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
174     GMX_RELEASE_ASSERT(msg != NULL, "Message should always be set");
175     *this << errinfo_message(msg->prependContext(context));
176 }
177
178 /********************************************************************
179  * Derived exception classes
180  */
181
182 int FileIOError::errorCode() const
183 {
184     return eeFileIO;
185 }
186
187 int InvalidInputError::errorCode() const
188 {
189     return eeInvalidInput;
190 }
191
192 int InconsistentInputError::errorCode() const
193 {
194     return eeInconsistentInput;
195 }
196
197 int SimulationInstabilityError::errorCode() const
198 {
199     return eeInstability;
200 }
201
202 int InternalError::errorCode() const
203 {
204     return eeInternalError;
205 }
206
207 int APIError::errorCode() const
208 {
209     return eeAPIError;
210 }
211
212 int NotImplementedError::errorCode() const
213 {
214     return eeNotImplemented;
215 }
216
217
218 /********************************************************************
219  * Global functions
220  */
221
222 namespace
223 {
224
225 /*! \brief
226  * Prints error information for an exception object.
227  *
228  * \param[in] fp      File to write the information out to (typically stderr).
229  * \param[in] ex      Exception object to print.
230  * \param[in] indent  Indentation for the information.
231  *
232  * If the exception contains nested exceptions, information from them is
233  * recursively printed.
234  */
235 void printExceptionMessage(FILE *fp, const std::exception &ex, int indent)
236 {
237     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
238     if (boostEx != NULL)
239     {
240         // TODO: Remove duplicate context if present in multiple nested exceptions.
241         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
242         if (msg != NULL)
243         {
244             while (msg != NULL && msg->isContext())
245             {
246                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
247                 ++indent;
248                 msg = &msg->child();
249             }
250             if (msg != NULL && !msg->text().empty())
251             {
252                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
253             }
254         }
255         else
256         {
257             internal::printFatalErrorMessageLine(fp, ex.what(), indent);
258         }
259
260         const int *errorNumber
261             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
262         if (errorNumber != NULL)
263         {
264             std::fprintf(fp, "%*sReason: %s\n", (indent+1)*2, "",
265                          std::strerror(*errorNumber));
266             const char * const *funcName
267                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
268             if (funcName != NULL)
269             {
270                 std::fprintf(fp, "%*s(call to %s() returned error code %d)\n",
271                              (indent+1)*2, "", *funcName, *errorNumber);
272             }
273         }
274
275         // TODO: Treat also boost::nested_exception (not currently used, though)
276
277         const internal::NestedExceptionList *nested
278             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
279         if (nested != NULL)
280         {
281             internal::NestedExceptionList::const_iterator ni;
282             for (ni = nested->begin(); ni != nested->end(); ++ni)
283             {
284                 try
285                 {
286                     rethrow_exception(*ni);
287                 }
288                 catch (const std::exception &nestedEx)
289                 {
290                     printExceptionMessage(fp, nestedEx, indent + 1);
291                 }
292             }
293         }
294     }
295     else
296     {
297         internal::printFatalErrorMessageLine(fp, ex.what(), indent);
298     }
299 }
300
301 }   // namespace
302
303 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
304 {
305     const char             *title      = "Unknown exception";
306     bool                    bPrintType = false;
307     const GromacsException *gmxEx      = dynamic_cast<const GromacsException *>(&ex);
308     // TODO: Treat more of the standard exceptions
309     if (gmxEx != NULL)
310     {
311         title = getErrorCodeString(gmxEx->errorCode());
312     }
313     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
314     {
315         title = "System error in thread synchronization";
316     }
317     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
318     {
319         title = "Memory allocation failed";
320     }
321     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
322     {
323         title      = "Standard library logic error (bug)";
324         bPrintType = true;
325     }
326     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
327     {
328         title      = "Standard library runtime error (possible bug)";
329         bPrintType = true;
330     }
331     else
332     {
333         bPrintType = true;
334     }
335     // We can't call get_error_info directly on ex since our internal boost
336     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
337     // here instead.
338     const char *const      *funcPtr = NULL;
339     const char *const      *filePtr = NULL;
340     const int              *linePtr = NULL;
341     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
342     if (boostEx != NULL)
343     {
344         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
345         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
346         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
347     }
348     internal::printFatalErrorHeader(fp, title,
349                                     funcPtr != NULL ? *funcPtr : NULL,
350                                     filePtr != NULL ? *filePtr : NULL,
351                                     linePtr != NULL ? *linePtr : 0);
352     if (bPrintType)
353     {
354         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
355     }
356     printExceptionMessage(fp, ex, 0);
357     internal::printFatalErrorFooter(fp);
358 }
359
360 std::string formatException(const std::exception &ex)
361 {
362     // TODO: It would be nicer to not duplicate the logic from
363     // printExceptionMessage().
364     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
365     if (boostEx != NULL)
366     {
367         const char *const *funcPtr =
368             boost::get_error_info<boost::throw_function>(*boostEx);
369         const char *const *filePtr =
370             boost::get_error_info<boost::throw_file>(*boostEx);
371         const int         *linePtr =
372             boost::get_error_info<boost::throw_line>(*boostEx);
373
374         std::string        result;
375         if (filePtr != NULL && linePtr != NULL)
376         {
377             result = formatString("%s:%d: %s\n", *filePtr, *linePtr,
378                                   funcPtr != NULL ? *funcPtr : "");
379         }
380
381         // TODO: Remove duplicate context if present in multiple nested exceptions.
382         const ErrorMessage *msg =
383             boost::get_error_info<errinfo_message>(*boostEx);
384         if (msg != NULL)
385         {
386             while (msg != NULL && msg->isContext())
387             {
388                 result.append(msg->text());
389                 result.append("\n");
390                 msg = &msg->child();
391             }
392             if (msg != NULL && !msg->text().empty())
393             {
394                 result.append(msg->text());
395                 result.append("\n");
396             }
397         }
398         else
399         {
400             result.append(ex.what());
401             result.append("\n");
402         }
403
404         const int *errorNumber
405             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
406         if (errorNumber != NULL)
407         {
408             result.append(formatString("Reason: %s\n",
409                                        std::strerror(*errorNumber)));
410             const char * const *funcName
411                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
412             if (funcName != NULL)
413             {
414                 result.append(formatString("(call to %s() returned error code %d)\n",
415                                            *funcName, *errorNumber));
416             }
417         }
418
419         // TODO: Treat also boost::nested_exception (not currently used, though)
420
421         const internal::NestedExceptionList *nested
422             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
423         if (nested != NULL)
424         {
425             internal::NestedExceptionList::const_iterator ni;
426             for (ni = nested->begin(); ni != nested->end(); ++ni)
427             {
428                 try
429                 {
430                     rethrow_exception(*ni);
431                 }
432                 catch (const std::exception &nestedEx)
433                 {
434                     result.append(formatException(nestedEx));
435                     result.append("\n");
436                 }
437             }
438         }
439         // Remove terminating line feed.
440         if (result.size() > 0U)
441         {
442             result.erase(result.size() - 1);
443         }
444         return result;
445     }
446     else
447     {
448         return ex.what();
449     }
450 }
451
452 } // namespace gmx