Add fatal_require and improved FatalError reporting interfaces

This commit is contained in:
Vinnie Falco
2013-08-05 09:18:40 -07:00
parent d559bdf24e
commit 21b422cda9
7 changed files with 166 additions and 65 deletions

View File

@@ -110,6 +110,17 @@
#define BEAST_BOOST_IS_AVAILABLE 1
#endif
//------------------------------------------------------------------------------
/** Config: BEAST_DISABLE_BEAST_VERSION_PRINTING
Turns off the debugging display of the beast version number
*/
#ifndef BEAST_DISABLE_BEAST_VERSION_PRINTING
#define BEAST_DISABLE_BEAST_VERSION_PRINTING 1
#endif
//------------------------------------------------------------------------------
/** Bind source configuration.
Set one of these to manually force a particular implementation of bind().

View File

@@ -103,7 +103,6 @@
//------------------------------------------------------------------------------
/** Config: BEAST_BOOST_IS_AVAILABLE
This activates boost specific features and improvements.
*/
#ifndef BEAST_BOOST_IS_AVAILABLE
@@ -112,6 +111,15 @@
//------------------------------------------------------------------------------
/** Config: BEAST_DISABLE_BEAST_VERSION_PRINTING
Turns off the debugging display of the beast version number
*/
#ifndef BEAST_DISABLE_BEAST_VERSION_PRINTING
//#define BEAST_DISABLE_BEAST_VERSION_PRINTING 1
#endif
//------------------------------------------------------------------------------
/** Bind source configuration.
Set one of these to manually force a particular implementation of bind().

View File

@@ -2,6 +2,10 @@
BEAST TODO
--------------------------------------------------------------------------------
- Use SemanticVersion for beast version numbers to replace BEAST_VERSION
- add support for a __PRETTY_FUNCTION__ equivalent for all environments
- add expectThrow() to UnitTest, where it expects an exception
- Import secp256k1 from sipa

View File

@@ -17,6 +17,56 @@
*/
//==============================================================================
//
// FatalError::Reporter
//
void FatalError::Reporter::onFatalError (
char const* message, char const* stackBacktrace, char const* filePath, int lineNumber)
{
String formattedMessage = formatMessage (
message, stackBacktrace, filePath, lineNumber);
reportMessage (formattedMessage);
}
void FatalError::Reporter::reportMessage (String& formattedMessage)
{
std::cerr << formattedMessage.toRawUTF8 ();
}
String FatalError::Reporter::formatMessage (
char const* message, char const* stackBacktrace, char const* filePath, int lineNumber)
{
String formattedMessage;
formattedMessage.preallocateBytes (16 * 1024);
formattedMessage << message;
if (filePath != nullptr && filePath [0] != 0)
{
formattedMessage << ", in " << formatFilePath (filePath)
<< " line " << String (lineNumber);
}
formattedMessage << newLine;
if (stackBacktrace != nullptr && stackBacktrace [0] != 0)
{
formattedMessage << "Stack:" << newLine;
formattedMessage << stackBacktrace;
}
return formattedMessage;
}
String FatalError::Reporter::formatFilePath (char const* filePath)
{
return File::createFileWithoutCheckingPath (filePath).getFileName ();
}
//------------------------------------------------------------------------------
Static::Storage <Atomic <FatalError::Reporter*>, FatalError> FatalError::s_reporter;
void FatalError::setReporter (Reporter& reporter)
@@ -66,47 +116,14 @@ public:
{
}
class TestReporter
: public FatalError::Reporter
, public Uncopyable
{
public:
explicit TestReporter (UnitTest& test)
: m_test (test)
{
}
void onFatalError (char const* message,
char const* stackBacktrace,
char const* fileName,
int lineNumber)
{
String s;
s << "Message = '" << message << "'" << newLine;
s << "File = '" << fileName << "' Line " << String (lineNumber) << newLine;
s << "Stack Trace:" << newLine;
s << stackBacktrace;
m_test.logMessage (s);
}
private:
UnitTest& m_test;
};
void runTest ()
{
beginTestCase ("raise");
TestReporter reporter (*this);
FatalError::setReporter (reporter);
// We don't really expect the program to run after this
// but the unit test is here so you can manually test it.
FatalError ("unit test", __FILE__, __LINE__);
int shouldBeZero (1);
fatal_require (shouldBeZero == 0);
}
};

View File

@@ -36,35 +36,93 @@ class FatalError : Uncopyable
public:
struct Reporter
{
virtual ~Reporter () { }
/** Called when a fatal error is raised.
Because the program is likely in an inconsistent state, it is a
good idea to do as little as possible from within this function.
It will be called from the thread that raised the fatal error.
The default implementation of this function first calls
formatMessage to produce the string, then calls reportMessage
to report the results.
You can override this to perform custom formatting.
@note filePath may be a zero length string if identifying
information was stripped from the executable for security.
@note stackBacktrace will be a string with zero characters for
platforms for which which don't support stack crawls, or
when symbolic information is missing from the executable.
@param message The error message.
@param stackBackTrace The stack of the thread that raised the error.
@param fileName The source file that raised the error.
@param filePath A full or partial path to the source file that raised the error.
@param lineNumber The line number in the source file.
*/
virtual void onFatalError (char const* message,
char const* stackBacktrace,
char const* fileName,
int lineNumber) = 0;
char const* filePath,
int lineNumber);
/** Called to report the message.
The default implementation simply writes this to standard error.
You can override this to perform additional things like logging
to a file or sending the message to another process.
@param formattedMessage The message to report.
*/
virtual void reportMessage (String& formattedMessage);
protected:
/** Called to format the message.
The default implementation calls formatFilePath to produce
a formatted file name, and then creates a suitable string
containing all of the information.
You can override this function to format your own messages.
@param message The message from the report.
@param stackBacktrace The stack backtrace from the report.
@param filePath The file path from the report.
@param lineNumber The line number from the report
*/
virtual String formatMessage (char const* message,
char const* stackBacktrace,
char const* filePath,
int lineNumber);
/** Call to reformat the file path.
Usually the file is a full path, which we really don't care
to see and can also be a security hole.
The default implementation removes most of the useless
directory components from the front.
You can override this to do a custom format on the file path.
*/
virtual String formatFilePath (char const* filePath);
};
/** Set the fatal error reporter.
Note that if a fatal error is raised during the construction of
objects with static storage duration, it might not be possible to
set the reporter before the error is raised.
set the reporter before the error is raised. The solution is not
to use objects with static storage duration that have non-trivial
constructors, use SharedSingleton instead.
The solution is not to use objects with static storage duration
that have non-trivial constructors, use SharedSingleton instead.
The default behavior when no reporter is set is to invoke
the base class version of Reporter::onFatalError.
If a reporter was previously set, this routine will do nothing.
@see SharedSingleton
@see SharedSingleton, Reporter
*/
static void setReporter (Reporter& reporter);
@@ -81,13 +139,24 @@ public:
other threads will be blocked before the process terminates.
@param message A null terminated string, which should come from a constant.
@param fileName Pass __FILE__ here.
@param filePath Pass __FILE__ here.
@param lineNumber Pass __LINE__ here.
*/
FatalError (char const* message, char const* fileName, int lineNumber);
FatalError (char const* message, char const* filePath, int lineNumber);
private:
static Static::Storage <Atomic <Reporter*>, FatalError> s_reporter;
};
/** Fatal assertion macro.
These get compiled into the code regardless of the BEAST_DEBUG
setting, and call FatalError.
@see FatalError
*/
#define fatal_require_report(expression) \
{ if (beast::beast_isRunningUnderDebugger()) beast_breakDebugger; \
FatalError ("Assertion '" BEAST_STRINGIFY(expression) "' failed", __FILE__, __LINE__); \
BEAST_ANALYZER_NORETURN }
#define fatal_require(condition) { if (! (condition)) { fatal_require_report(condition); } }
#endif

View File

@@ -4,6 +4,12 @@
*/
//==============================================================================
#ifdef TWICE
#error die
#endif
#define TWICE
FatalErrorReporter::FatalErrorReporter ()
{
FatalError::setReporter (*this);
@@ -14,20 +20,9 @@ FatalErrorReporter::~FatalErrorReporter ()
FatalError::resetReporter (*this);
}
void FatalErrorReporter::onFatalError (
char const* message,
char const* stackBacktrace,
char const* fileName,
int lineNumber)
void FatalErrorReporter::reportMessage (String& formattedMessage)
{
String s;
s << "Message = '" << message << "'" << newLine;
s << "File = '" << fileName << "' Line " << String (lineNumber) << newLine;
s << "Stack Trace:" << newLine;
s << stackBacktrace;
Log::out() << s;
Log::out() << formattedMessage.toRawUTF8 ();
}
//------------------------------------------------------------------------------
@@ -48,7 +43,7 @@ public:
// We don't really expect the program to run after this
// but the unit test is here so you can manually test it.
FatalError ("unit test", __FILE__, __LINE__);
FatalError ("The unit test intentionally failed", __FILE__, __LINE__);
}
};

View File

@@ -4,8 +4,8 @@
*/
//==============================================================================
#ifndef RIPPLE_SCOPEDFATALERRORREPORTER_H_INCLUDED
#define RIPPLE_SCOPEDFATALERRORREPORTER_H_INCLUDED
#ifndef RIPPLE_FATALERRORREPORTER_H_INCLUDED
#define RIPPLE_FATALERRORREPORTER_H_INCLUDED
/** FatalError reporter.
@@ -24,10 +24,7 @@ public:
FatalErrorReporter ();
~FatalErrorReporter ();
void onFatalError (char const* message,
char const* stackBacktrace,
char const* fileName,
int lineNumber);
void reportMessage (String& formattedMessage);
};
#endif