[MOAB-dev] commit/MOAB: danwu: Current error handler uses std::cout to print error messages and stack traces in each processor, which can result in a messy output. This is a known issue for MPI with std::cout, and it seems that existing DebugOutput class has solved this issue with buffered lines. A new class ErrorOutput (similar to DebugOutput) is now used by the error handler to print each line prefixed with the MPI rank.

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed Jan 29 14:19:13 CST 2014


1 new commit in MOAB:

https://bitbucket.org/fathomteam/moab/commits/3f1c796c5e84/
Changeset:   3f1c796c5e84
Branch:      error_handling_enhancement
User:        danwu
Date:        2014-01-29 21:18:30
Summary:     Current error handler uses std::cout to print error messages and stack traces in each processor, which can result in a messy output. This is a known issue for MPI with std::cout, and it seems that existing DebugOutput class has solved this issue with buffered lines. A new class ErrorOutput (similar to DebugOutput) is now used by the error handler to print each line prefixed with the MPI rank.

Affected #:  5 files

diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp
index 18c7150..1f67c52 100644
--- a/src/ErrorHandler.cpp
+++ b/src/ErrorHandler.cpp
@@ -1,21 +1,46 @@
 #include "moab/ErrorHandler.hpp"
+#include "ErrorOutput.hpp"
 
-#include <iostream>
+namespace moab {
 
-using namespace std;
+static ErrorOutput* errorOutput = NULL;
 
-namespace moab {
+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* err_msg, ErrorType err_type)
 {
   // Print the error message
   if (MB_ERROR_TYPE_NEW == err_type) {
-    if (err_msg)
-      cout << "[Error message]: " << err_msg << endl;
+    if (err_msg) {
+      if (NULL != errorOutput) {
+        errorOutput->print("--------------------- Error Message ------------------------------------\n");
+        errorOutput->printf("%s!\n", err_msg);
+      }
+    }
   }
 
   // Print a line of stack trace
-  cout << func << "() at line " << line << ", in file " << file << endl;
+  if (NULL != errorOutput)
+    errorOutput->printf("%s at line %d, in file %s\n", func, line, file);
 }
 
 ErrorInfo MBError(int line, const char* func, const char* file, const ErrorInfo& err_info, ErrorType err_type)

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..3d0c4bb
--- /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 proccess 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 0ec5d2c..31c522e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,6 +57,8 @@ libMOAB_la_SOURCES = \
   EntitySequence.cpp \
   EntitySequence.hpp \
   ErrorHandler.cpp \
+  ErrorOutput.cpp \
+  ErrorOutput.hpp \
   Factory.cpp \
   FBEngine.cpp \
   FileOptions.cpp \

diff --git a/src/moab/ErrorHandler.hpp b/src/moab/ErrorHandler.hpp
index 60ef0fd..6323335 100644
--- a/src/moab/ErrorHandler.hpp
+++ b/src/moab/ErrorHandler.hpp
@@ -9,6 +9,7 @@ namespace moab {
 
 enum ErrorType { MB_ERROR_TYPE_NEW = 0, MB_ERROR_TYPE_EXISTING = 1 };
 
+//! This class will replace the ErrorCode return codes in most cases
 class ErrorInfo {
   ErrorCode mErrorCode;
   std::ostringstream mErrorMsg;
@@ -60,7 +61,7 @@ public:
     return *this;
   }
 
-  // Use C++ string stream to build up an error message
+  //! Use C++ string stream to build up an error message
   template<typename T>
   ErrorInfo& operator<<(T input)
   {
@@ -68,14 +69,23 @@ public:
     return *this;
   }
 
-  // A conversion operator for converting ErrorInfo to ErrorCode
+  //! A conversion operator for converting ErrorInfo to ErrorCode
   operator ErrorCode() const
   {
     return mErrorCode;
   }
 };
 
-// Errors are handled through routine MBError()
+//! 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
 ErrorInfo MBError(int line, const char* func, const char* file, const ErrorInfo& err_info, ErrorType err_type);
 
 #define ERROR_INFO_SUCCESS ErrorInfo(MB_SUCCESS)
@@ -113,7 +123,7 @@ ErrorInfo MBError(int line, const char* func, const char* file, const ErrorInfo&
   do { \
     if (exp_err_code != err_info) { \
       err_info.set_error_code(MB_FAILURE); \
-      err_info.set_error_msg("Returned error code is not expected."); \
+      err_info.set_error_msg("Returned error code is not expected"); \
       SET_ERR(err_info); \
     } \
   } while (false)

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