[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