[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