/*
+ * This file is part of the GROMACS molecular simulation package.
*
- * This source code is part of
+ * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
*
- * G R O M A C S
- *
- * GROningen MAchine for Chemical Simulations
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
*
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * If you want to redistribute modifications, please consider that
- * scientific software is very special. Version control is crucial -
- * bugs must be traceable. We will be happy to consider code for
- * inclusion in the official distribution, but derived work must not
- * be called official GROMACS. Details are found in the README & COPYING
- * files - if they are missing, get the official version at www.gromacs.org.
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
*
* To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
+ * the research papers on the package. Check out http://www.gromacs.org.
*/
/*! \internal \file
* \brief
* Implements classes and functions in exceptions.h.
*
- * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_utility
*/
+#include "gmxpre.h"
+
#include "exceptions.h"
+#include "config.h"
+
#include <cstring>
#include <new>
#include <stdexcept>
#include <typeinfo>
-#include <boost/exception/get_error_info.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/exception/get_error_info.hpp>
+
+#include "thread_mpi/system_error.h"
-#include "gromacs/legacyheaders/thread_mpi/system_error.h"
+#include "gromacs/utility/basenetwork.h"
#include "gromacs/utility/errorcodes.h"
#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
#include "errorformat.h"
* ErrorMessage
*/
+/*! \brief
+ * Error message or error context text item.
+ *
+ * Error messages for an exception are represented as a chain of ErrorMessage
+ * objects: the elements at the bottom of the chain (with no children) is the
+ * error message, and other elements are the context strings added.
+ *
+ * \ingroup module_utility
+ */
class ErrorMessage
{
public:
explicit ErrorMessage(const std::string &text);
//! Whether this object is a context string.
- bool isContext() const { return child_; }
+ bool isContext() const { return static_cast<bool>(child_); }
//! Returns the text for this object.
const std::string &text() const { return text_; }
/*! \brief
const char *GromacsException::what() const throw()
{
const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
- while (msg != NULL && msg->isContext())
+ if (msg == NULL)
+ {
+ return "No reason provided";
+ }
+ while (msg->isContext())
{
msg = &msg->child();
}
- return msg != NULL ? msg->text().c_str() : "No reason provided";
+ return msg->text().c_str();
}
void GromacsException::prependContext(const std::string &context)
namespace
{
+//! \addtogroup module_utility
+//! \{
+
+/*! \brief
+ * Abstracts actual output from the other logic in exception formatting.
+ *
+ * Object that implements this interface is passed to
+ * formatExceptionMessageInternal(), and is responsible for composing the
+ * output. This allows using the same implementation of interpreting the
+ * exceptions while still supporting output to different formats (e.g., to a
+ * string or to \c stderr).
+ */
+class MessageWriterInterface
+{
+ public:
+ virtual ~MessageWriterInterface() {}
+
+ /*! \brief
+ * Writes a single line of text into the output.
+ *
+ * \param[in] text Text to write on the line.
+ * \param[in] indent Suggested number of spaces to indent the line.
+ */
+ virtual void writeLine(const char *text, int indent) = 0;
+ /*! \brief
+ * Writes information about a system error (errno-based).
+ *
+ * \param[in] errorNumber errno value
+ * \param[in] funcName Name of the system call (can be NULL).
+ * \param[in] indent Suggested number of spaces to indent the output.
+ */
+ virtual void writeErrNoInfo(int errorNumber, const char *funcName,
+ int indent) = 0;
+};
+
+/*! \brief
+ * Exception information writer for cases where exceptions should be avoided.
+ *
+ * Formats the messages into the provided FILE handle without checking for
+ * errors in std::fprintf() calls.
+ */
+class MessageWriterFileNoThrow : public MessageWriterInterface
+{
+ public:
+ //! Initializes a writer that writes to the given file handle.
+ explicit MessageWriterFileNoThrow(FILE *fp) : fp_(fp) {}
+
+ virtual void writeLine(const char *text, int indent)
+ {
+ internal::printFatalErrorMessageLine(fp_, text, indent);
+ }
+ virtual void writeErrNoInfo(int errorNumber, const char *funcName,
+ int indent)
+ {
+ std::fprintf(fp_, "%*sReason: %s\n", indent, "",
+ std::strerror(errorNumber));
+ if (funcName != NULL)
+ {
+ std::fprintf(fp_, "%*s(call to %s() returned error code %d)\n",
+ indent, "", funcName, errorNumber);
+ }
+ }
+
+ private:
+ FILE *fp_;
+};
+
+/*! \brief
+ * Exception information writer to format into an std::string.
+ */
+class MessageWriterString : public MessageWriterInterface
+{
+ public:
+ //! Post-processes the output string to not end in a line feed.
+ void removeTerminatingLineFeed()
+ {
+ if (result_.size() > 0U)
+ {
+ result_.erase(result_.size() - 1);
+ }
+ }
+ //! Returns the constructed string.
+ const std::string &result() const { return result_; }
+
+ virtual void writeLine(const char *text, int indent)
+ {
+ result_.append(indent, ' ');
+ result_.append(text);
+ result_.append("\n");
+ }
+ virtual void writeErrNoInfo(int errorNumber, const char *funcName,
+ int indent)
+ {
+ writeLine(formatString("Reason: %s", std::strerror(errorNumber)).c_str(),
+ indent);
+ if (funcName != NULL)
+ {
+ writeLine(formatString("(call to %s() returned error code %d)",
+ funcName, errorNumber).c_str(),
+ indent);
+ }
+ }
+
+ private:
+ std::string result_;
+};
+
/*! \brief
* Prints error information for an exception object.
*
- * \param[in] fp File to write the information out to (typically stderr).
+ * \param[in] writer Writer to write out the information.
* \param[in] ex Exception object to print.
* \param[in] indent Indentation for the information.
*
* If the exception contains nested exceptions, information from them is
* recursively printed.
+ *
+ * Does not throw unless the writer throws.
*/
-void printExceptionMessage(FILE *fp, const std::exception &ex, int indent)
+void formatExceptionMessageInternal(MessageWriterInterface *writer,
+ const std::exception &ex, int indent)
{
const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
if (boostEx != NULL)
{
+ // TODO: Add an option to print this information for the tests
+ // const char *const *funcPtr =
+ // boost::get_error_info<boost::throw_function>(*boostEx);
+ // const char *const *filePtr =
+ // boost::get_error_info<boost::throw_file>(*boostEx);
+ // const int *linePtr =
+ // boost::get_error_info<boost::throw_line>(*boostEx);
+
+ // std::string result;
+ // if (filePtr != NULL && linePtr != NULL)
+ // {
+ // result = formatString("%s:%d: %s\n", *filePtr, *linePtr,
+ // funcPtr != NULL ? *funcPtr : "");
+ // }
+
// TODO: Remove duplicate context if present in multiple nested exceptions.
const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
if (msg != NULL)
{
while (msg != NULL && msg->isContext())
{
- internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
+ writer->writeLine(msg->text().c_str(), indent*2);
++indent;
msg = &msg->child();
}
if (msg != NULL && !msg->text().empty())
{
- internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
+ writer->writeLine(msg->text().c_str(), indent*2);
}
}
else
{
- internal::printFatalErrorMessageLine(fp, ex.what(), indent);
+ writer->writeLine(ex.what(), indent*2);
}
const int *errorNumber
= boost::get_error_info<boost::errinfo_errno>(*boostEx);
if (errorNumber != NULL)
{
- std::fprintf(fp, "%*sReason: %s\n", (indent+1)*2, "",
- std::strerror(*errorNumber));
const char * const *funcName
= boost::get_error_info<boost::errinfo_api_function>(*boostEx);
- if (funcName != NULL)
- {
- std::fprintf(fp, "%*s(call to %s() returned error code %d)\n",
- (indent+1)*2, "", *funcName, *errorNumber);
- }
+ writer->writeErrNoInfo(*errorNumber,
+ funcName != NULL ? *funcName : NULL,
+ (indent+1)*2);
}
// TODO: Treat also boost::nested_exception (not currently used, though)
}
catch (const std::exception &nestedEx)
{
- printExceptionMessage(fp, nestedEx, indent + 1);
+ formatExceptionMessageInternal(writer, nestedEx, indent + 1);
}
}
}
}
else
{
- internal::printFatalErrorMessageLine(fp, ex.what(), indent);
+ writer->writeLine(ex.what(), indent*2);
}
}
+//! \}
+
} // namespace
void printFatalErrorMessage(FILE *fp, const std::exception &ex)
{
std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
}
- printExceptionMessage(fp, ex, 0);
+ MessageWriterFileNoThrow writer(fp);
+ formatExceptionMessageInternal(&writer, ex, 0);
internal::printFatalErrorFooter(fp);
}
+std::string formatExceptionMessageToString(const std::exception &ex)
+{
+ MessageWriterString writer;
+ formatExceptionMessageInternal(&writer, ex, 0);
+ writer.removeTerminatingLineFeed();
+ return writer.result();
+}
+
+void formatExceptionMessageToFile(FILE *fp, const std::exception &ex)
+{
+ MessageWriterFileNoThrow writer(fp);
+ formatExceptionMessageInternal(&writer, ex, 0);
+}
+
+int processExceptionAtExit(const std::exception & /*ex*/)
+{
+ int returnCode = 1;
+#ifdef GMX_LIB_MPI
+ // TODO: Consider moving the output done in gmx_abort() into the message
+ // printing routine above, so that this could become a simple MPI_Abort().
+ gmx_abort(returnCode);
+#endif
+ return returnCode;
+}
+
} // namespace gmx