[MOAB-dev] commit/MOAB: danwu: Framework code for enhanced error handling.
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Wed Jul 9 13:21:44 CDT 2014
1 new commit in MOAB:
https://bitbucket.org/fathomteam/moab/commits/04229f2f1c6f/
Changeset: 04229f2f1c6f
Branch: danwu/error_handling_enhancement
User: danwu
Date: 2014-07-09 20:20:20
Summary: Framework code for enhanced error handling.
Affected #: 6 files
diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp
new file mode 100644
index 0000000..9f0fdee
--- /dev/null
+++ b/src/ErrorHandler.cpp
@@ -0,0 +1,75 @@
+#include "moab/ErrorHandler.hpp"
+#include "ErrorOutput.hpp"
+#ifdef USE_MPI
+#include "moab_mpi.h"
+#endif
+
+#include <stdlib.h>
+
+namespace moab {
+
+static ErrorOutput* errorOutput = NULL;
+
+void MBErrorHandler_Init()
+{
+ if (NULL == errorOutput) {
+ errorOutput = new ErrorOutput(stderr);
+ errorOutput->use_world_rank();
+ }
+}
+
+void MBErrorHandler_Finalize()
+{
+ if (NULL != errorOutput) {
+ delete errorOutput;
+ errorOutput = NULL;
+ }
+}
+
+bool MBErrorHandler_Initialized()
+{
+ return (NULL != errorOutput);
+}
+
+void MBTraceBackErrorHandler(int line, const char* func, const char* file, const char* dir, const char* err_msg, ErrorType err_type)
+{
+ // For a globally fatal error, get world rank of current processor, so that it is only printed from processor 0
+ // For a per-processor relevant error, set rank of current processor to 0, so that it is always printed
+ int rank = 0;
+ if (MB_ERROR_TYPE_NEW_GLOBAL == err_type && NULL != errorOutput && errorOutput->have_rank())
+ rank = errorOutput->get_rank();
+
+ if (0 == rank) {
+ // Print the error messages if it is a new error
+ if (MB_ERROR_TYPE_EXISTING != err_type && NULL != errorOutput && NULL != err_msg) {
+ errorOutput->print("--------------------- Error Message ------------------------------------\n");
+ errorOutput->printf("%s!\n", err_msg);
+ }
+
+ // Print a line of stack trace
+ if (NULL != errorOutput)
+ errorOutput->printf("%s() line %d in %s%s\n", func, line, dir, file);
+ }
+ else {
+ // Do not print the error messages, since processor 0 will print them
+ // Sleep 10 seconds before aborting so it will not accidently kill process 0
+ sleep(10);
+ abort();
+ }
+}
+
+ErrorCode MBError(int line, const char* func, const char* file, const char* dir, ErrorCode err_code, const char* err_msg, ErrorType err_type)
+{
+ MBTraceBackErrorHandler(line, func, file, dir, err_msg, err_type);
+
+#ifdef USE_MPI
+ // If this is called from the main() routine we call MPI_Abort() to allow
+ // the parallel program to be properly shutdown
+ if (strncmp(func, "main", 4) == 0)
+ MPI_Abort(MPI_COMM_WORLD, err_code);
+#endif
+
+ return err_code;
+}
+
+} // namespace moab
diff --git a/src/ErrorOutput.cpp b/src/ErrorOutput.cpp
new file mode 100644
index 0000000..e71e010
--- /dev/null
+++ b/src/ErrorOutput.cpp
@@ -0,0 +1,169 @@
+#include "ErrorOutput.hpp"
+
+#include <iostream>
+#include <string.h>
+#include <algorithm>
+#include <assert.h>
+
+#ifdef USE_MPI
+#include "moab_mpi.h"
+#endif
+
+namespace moab {
+
+class FILEErrorStream : public ErrorOutputStream
+{
+private:
+ FILE* filePtr;
+
+public:
+ FILEErrorStream(FILE* filep) : filePtr(filep) {}
+ void println(int rank, const char* str);
+ void println(const char* str);
+};
+
+void FILEErrorStream::println(int rank, const char* str)
+{
+ fprintf(filePtr, "[%d]MOAB ERROR: %s\n", rank, str);
+ fflush(filePtr);
+}
+
+void FILEErrorStream::println(const char* str)
+{
+ fprintf(filePtr, "MOAB ERROR: %s\n", str);
+ fflush(filePtr);
+}
+
+class CxxErrorStream : public ErrorOutputStream
+{
+private:
+ std::ostream& outStr;
+
+public:
+ CxxErrorStream(std::ostream& str) : outStr(str) {}
+ void println(int rank, const char* str);
+ void println(const char* str);
+};
+
+void CxxErrorStream::println(int rank, const char* str)
+{
+ outStr << "[" << rank << "]MOAB ERROR: " << str << std::endl;
+ outStr.flush();
+}
+
+void CxxErrorStream::println(const char* str)
+{
+ outStr << "MOAB ERROR: " << str << std::endl;
+ outStr.flush();
+}
+
+ErrorOutput::ErrorOutput(FILE* impl)
+ : outputImpl(new FILEErrorStream(impl)),
+ mpiRank(-1)
+{
+ lineBuffer.reserve(1024);
+}
+
+ErrorOutput::ErrorOutput(std::ostream& str)
+ : outputImpl(new CxxErrorStream(str)),
+ mpiRank(-1)
+{
+ lineBuffer.reserve(1024);
+}
+
+ErrorOutput::~ErrorOutput()
+{
+ if (!lineBuffer.empty()) {
+ lineBuffer.push_back('\n');
+ process_line_buffer();
+ }
+
+ if (NULL != outputImpl) {
+ delete outputImpl;
+ outputImpl = NULL;
+ }
+}
+
+void ErrorOutput::use_world_rank()
+{
+#ifdef USE_MPI
+ int flag1;
+ MPI_Initialized(&flag1);
+ int flag2;
+ MPI_Finalized(&flag2);
+ if (flag1 && !flag2)
+ MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
+#endif
+}
+
+void ErrorOutput::print_real(const char* buffer)
+{
+ lineBuffer.insert(lineBuffer.end(), buffer, buffer + strlen(buffer));
+ process_line_buffer();
+}
+
+void ErrorOutput::print_real(const std::string& str)
+{
+ lineBuffer.insert(lineBuffer.end(), str.begin(), str.end());
+ process_line_buffer();
+}
+
+void ErrorOutput::print_real(const char* fmt, va_list args1, va_list args2)
+{
+ size_t idx = lineBuffer.size();
+#ifdef HAVE_VSNPRINTF
+ // try once with remaining space in buffer
+ lineBuffer.resize(lineBuffer.capacity());
+ unsigned size = vsnprintf(&lineBuffer[idx], lineBuffer.size() - idx, fmt, args1);
+ ++size; // trailing null
+ // if necessary, increase buffer size and retry
+ if (size > (lineBuffer.size() - idx)) {
+ lineBuffer.resize(idx + size);
+ size = vsnprintf(&lineBuffer[idx], lineBuffer.size() - idx, fmt, args2);
+ ++size; // trailing null
+ }
+#else
+ // Guess how much space might be required.
+ // If every character is a format code then there are len/3 format codes.
+ // Guess a random large value of 81 characters per formatted argument.
+ unsigned exp_size = 27 * strlen(fmt);
+ lineBuffer.resize(idx + exp_size);
+ unsigned size = vsprintf(&lineBuffer[idx], fmt, args1);
+ ++size; // trailing null
+ // check if we overflowed the buffer
+ if (size > exp_size) {
+ // crap!
+ fprintf(stderr, "ERROR: Buffer overflow at %s:%d\n", __FILE__, __LINE__);
+ lineBuffer.resize(idx + exp_size);
+ size = vsprintf(&lineBuffer[idx], fmt, args2);
+ ++size; // trailing null
+ }
+#endif
+
+ // less one because we don't want the trailing '\0'
+ lineBuffer.resize(idx + size - 1);
+ process_line_buffer();
+}
+
+void ErrorOutput::process_line_buffer()
+{
+ size_t last_idx = 0;
+ std::vector<char>::iterator i;
+ for (i = std::find(lineBuffer.begin(), lineBuffer.end(), '\n');
+ i != lineBuffer.end(); i = std::find(i, lineBuffer.end(), '\n')) {
+ *i = '\0';
+ if (have_rank())
+ outputImpl->println(get_rank(), &lineBuffer[last_idx]);
+ else
+ outputImpl->println(&lineBuffer[last_idx]);
+ ++i;
+ last_idx = i - lineBuffer.begin();
+ }
+
+ if (last_idx) {
+ i = std::copy(lineBuffer.begin() + last_idx, lineBuffer.end(), lineBuffer.begin());
+ lineBuffer.erase(i, lineBuffer.end());
+ }
+}
+
+} // namespace moab
diff --git a/src/ErrorOutput.hpp b/src/ErrorOutput.hpp
new file mode 100644
index 0000000..cd9a03e
--- /dev/null
+++ b/src/ErrorOutput.hpp
@@ -0,0 +1,101 @@
+#ifndef moab_ERROR_OUTPUT_HPP
+#define moab_ERROR_OUTPUT_HPP
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <vector>
+#include <iosfwd>
+#include <string>
+
+#include "moab/Compiler.hpp"
+
+namespace moab {
+
+class ErrorOutputStream;
+
+/**\brief Utility class for printing error output
+ *
+ * This class implements line-oriented output. That is, it buffers
+ * output data until a newline is encountered, at which point it
+ * sends the output to the output stream followed by an explicit
+ * flush, and optionally prefixed with the MPI rank.
+ *
+ * \Note Any output not terminated with an newline character or
+ * followed by later output containing a newline character
+ * will not be flushed until the destructor is invoked.
+ */
+class ErrorOutput {
+public:
+ /**
+ *\param str Output stream to which to flush output
+ */
+ ErrorOutput(FILE* str);
+
+ /**
+ *\param str Output stream to which to flush output
+ */
+ ErrorOutput(std::ostream& str);
+
+ /**
+ * Destructor flushes any remaining output that wasn't followed
+ * by a newline character.
+ */
+ ~ErrorOutput();
+
+ //!\brief Check if MPI rank has been set.
+ bool have_rank() const { return mpiRank >= 0; }
+ //!\brief Get MPI rank.
+ int get_rank() const { return mpiRank; }
+ //!\brief Set MPI rank.
+ void set_rank(int rank) { mpiRank = rank; }
+ //!\brief Set MPI rank to the rank of this process in MPI_COMM_WORLD,
+ //! if MOAB is built with MPI and MPI_Init has been called
+ void use_world_rank();
+
+ //!\brief Output the specified string
+ void print(const char* str) { print_real(str); }
+
+ //!\brief Output the specified string
+ void print(const std::string& str) { print_real(str); }
+
+ //!\brief Output the specified printf-formatted output
+ void printf(const char* fmt, ...) MB_PRINTF(1);
+
+private:
+ ErrorOutputStream* outputImpl;
+ int mpiRank;
+
+ void print_real(const char* buffer);
+ void print_real(const std::string& str);
+
+ // Function must be passed to copies of the same va_list because
+ // a) it might have to call vs(n)printf twice, b) vs(n)printf modifies
+ // the va_list such that it cannot be reused, and c) va_copy is not
+ // (yet) portable (c99, no c++ standard).
+ void print_real(const char* buffer, va_list args1, va_list args2);
+ void process_line_buffer();
+
+ std::vector<char> lineBuffer;
+};
+
+inline void ErrorOutput::printf(const char* fmt, ...)
+{
+ va_list args1, args2;
+ va_start(args1, fmt);
+ va_start(args2, fmt);
+ print_real(fmt, args1, args2);
+ va_end(args2);
+ va_end(args1);
+}
+
+class ErrorOutputStream {
+public:
+ ErrorOutputStream() {}
+ virtual ~ErrorOutputStream() {}
+ virtual void println(const char* str) = 0;
+ virtual void println(int rank, const char* str) = 0;
+};
+
+} // namespace moab
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index a9adf0d..f55d82c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -56,6 +56,9 @@ libMOAB_la_SOURCES = \
ElementSequence.hpp \
EntitySequence.cpp \
EntitySequence.hpp \
+ ErrorHandler.cpp \
+ ErrorOutput.cpp \
+ ErrorOutput.hpp \
Factory.cpp \
FBEngine.cpp \
FileOptions.cpp \
@@ -161,6 +164,7 @@ nobase_libMOAB_la_include_HEADERS = \
moab/HomXform.hpp \
moab/EntityType.hpp \
moab/EntityHandle.hpp \
+ moab/ErrorHandler.hpp \
moab/FBEngine.hpp \
moab/FileOptions.hpp \
moab/FindPtFuncs.h \
diff --git a/src/moab/ErrorHandler.hpp b/src/moab/ErrorHandler.hpp
new file mode 100644
index 0000000..ec91113
--- /dev/null
+++ b/src/moab/ErrorHandler.hpp
@@ -0,0 +1,144 @@
+#ifndef MOAB_ERROR_HANDLER_HPP
+#define MOAB_ERROR_HANDLER_HPP
+
+#include "moab/Types.hpp"
+
+#include <sstream>
+#include <string.h>
+
+namespace moab {
+
+//! ErrorType - passed to the error handling routines indicating if this is a new error (globally fatal
+//! or per-processor relevant) or an existing one
+enum ErrorType {MB_ERROR_TYPE_NEW_GLOBAL = 0, MB_ERROR_TYPE_NEW_LOCAL = 1, MB_ERROR_TYPE_EXISTING = 2};
+
+//! Initialize MOAB error handler (e.g. create a utility object for printing error output)
+void MBErrorHandler_Init();
+
+//! Finalize MOAB error handler (e.g. delete the utility object for printing error output)
+void MBErrorHandler_Finalize();
+
+//! Indicates whether MBErrorHandler_Init has been called
+bool MBErrorHandler_Initialized();
+
+//! Routine that is called when an error has been detected
+ErrorCode MBError(int line, const char* func, const char* file, const char* dir,
+ ErrorCode err_code, const char* err_msg, ErrorType err_type);
+
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+#define MBSTRINGIFY_(X) #X
+#define MBSTRINGIFY(X) MBSTRINGIFY_(X)
+
+#ifdef LOCDIR
+#define __SDIR__ MBSTRINGIFY(LOCDIR)
+#else
+#define __SDIR__ ""
+#endif
+
+//! Set a new error with passed error code and passed error message string
+#define SET_ERR(err_code, err_msg) \
+ return MBError(__LINE__, __func__, __FILENAME__, __SDIR__, err_code, err_msg, MB_ERROR_TYPE_NEW_LOCAL)
+
+//! Set a new error with passed error code and passed error message string stream
+#define SET_ERR_STR(err_code, err_msg_str) \
+ do { \
+ std::ostringstream ostr; \
+ ostr << err_msg_str; \
+ return MBError(__LINE__, __func__, __FILENAME__, __SDIR__, err_code, ostr.str().c_str(), MB_ERROR_TYPE_NEW_LOCAL); \
+ } while (false)
+
+//! Set a new global error with passed error code and passed error message string
+#define SET_GLB_ERR(err_code, err_msg) \
+ return MBError(__LINE__, __func__, __FILENAME__, __SDIR__, err_code, err_msg, MB_ERROR_TYPE_NEW_GLOBAL)
+
+//! Set a new global error with passed error code and passed error message string stream
+#define SET_GLB_ERR_STR(err_code, err_msg_str) \
+ do { \
+ std::ostringstream ostr; \
+ ostr << err_msg_str; \
+ return MBError(__LINE__, __func__, __FILENAME__, __SDIR__, err_code, ostr.str().c_str(), MB_ERROR_TYPE_NEW_GLOBAL); \
+ } while (false)
+
+//! Check returned error code against MB_SUCCESS
+#define CHK_ERR(err_code) \
+ do { \
+ if (MB_SUCCESS != err_code) \
+ return MBError(__LINE__, __func__, __FILENAME__, __SDIR__, err_code, "", MB_ERROR_TYPE_EXISTING); \
+ } while (false)
+
+//! Check returned error code against MB_SUCCESS
+//! Set a new error with the returned error code and passed error message string
+#define CHK_ERR1(err_code, err_msg_to_set) \
+ do { \
+ if (MB_SUCCESS != err_code) \
+ SET_ERR(err_code, err_msg_to_set); \
+ } while (false)
+
+//! Check returned error code against MB_SUCCESS
+//! Set a new error with the returned error code and passed error message string stream
+#define CHK_ERR1_STR(err_code, err_msg_str_to_set) \
+ do { \
+ if (MB_SUCCESS != err_code) \
+ SET_ERR_STR(err_code, err_msg_str_to_set); \
+ } while (false)
+
+//! Check returned error code against MB_SUCCESS
+//! Set a new error with passed error code and passed error message string
+#define CHK_ERR2(err_code, err_code_to_set, err_msg_to_set) \
+ do { \
+ if (MB_SUCCESS != err_code) \
+ SET_ERR(err_code_to_set, err_msg_to_set); \
+ } while (false)
+
+//! Check returned error code against MB_SUCCESS
+//! Set a new error with passed error code and passed error message string stream
+#define CHK_ERR2_STR(err_code, err_code_to_set, err_msg_str_to_set) \
+ do { \
+ if (MB_SUCCESS != err_code) \
+ SET_ERR_STR(err_code_to_set, err_msg_str_to_set); \
+ } while (false)
+
+//! Check returned error code against an expected one
+//! Set a new error with default error code and default error message string
+#define CHK_EQL(err_code, exp_err_code) \
+ do { \
+ if (exp_err_code != err_code) \
+ SET_ERR(MB_FAILURE, "Returned error code is not expected"); \
+ } while (false)
+
+//! Check returned error code against an expected one
+//! Set a new error with default error code and passed error message string
+#define CHK_EQL1(err_code, exp_err_code, err_msg_to_set) \
+ do { \
+ if (exp_err_code != err_code) { \
+ SET_ERR(MB_FAILURE, err_msg_to_set); \
+ } \
+ } while (false)
+
+//! Check returned error code against an expected one
+//! Set a new error with default error code and passed error message string stream
+#define CHK_EQL1_STR(err_code, exp_err_code, err_msg_str_to_set) \
+ do { \
+ if (exp_err_code != err_code) \
+ SET_ERR_STR(MB_FAILURE, err_msg_str_to_set); \
+ } while (false)
+
+//! Check returned error code against an expected one
+//! Set a new error with passed error code and passed error message string
+#define CHK_EQL2(err_code, exp_err_code, err_code_to_set, err_msg_to_set) \
+ do { \
+ if (exp_err_code != err_code) \
+ SET_ERR(err_code_to_set, err_msg_to_set); \
+ } while (false)
+
+//! Check returned error code against an expected one
+//! Set a new error with passed error code and passed error message string stream
+#define CHK_EQL2_STR(err_code, exp_err_code, err_code_to_set, err_msg_str_to_set) \
+ do { \
+ if (exp_err_code != err_code) \
+ SET_ERR_STR(err_code_to_set, err_msg_str_to_set); \
+ } while (false)
+#endif
+
+} // namespace moab
diff --git a/src/moab/Interface.hpp b/src/moab/Interface.hpp
index 09f4219..8ffe2a3 100644
--- a/src/moab/Interface.hpp
+++ b/src/moab/Interface.hpp
@@ -43,6 +43,7 @@
#include "moab/Forward.hpp"
#include "moab/Range.hpp"
#include "moab/Compiler.hpp"
+#include "moab/ErrorHandler.hpp"
// include files
#include <string>
Repository URL: https://bitbucket.org/fathomteam/moab/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the moab-dev
mailing list