From 21b422cda904d7b145cde5815bb0d520c490235e Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 5 Aug 2013 09:18:40 -0700 Subject: [PATCH] Add fatal_require and improved FatalError reporting interfaces --- BeastConfig.h | 11 +++ .../Builds/VisualStudio2012/BeastConfig.h | 10 ++- Subtrees/beast/TODO.txt | 4 + .../diagnostic/beast_FatalError.cpp | 87 +++++++++++-------- .../beast_core/diagnostic/beast_FatalError.h | 87 +++++++++++++++++-- .../main/ripple_FatalErrorReporter.cpp | 23 ++--- .../main/ripple_FatalErrorReporter.h | 9 +- 7 files changed, 166 insertions(+), 65 deletions(-) diff --git a/BeastConfig.h b/BeastConfig.h index 3b82014da9..d9dfee64cb 100644 --- a/BeastConfig.h +++ b/BeastConfig.h @@ -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(). diff --git a/Subtrees/beast/Builds/VisualStudio2012/BeastConfig.h b/Subtrees/beast/Builds/VisualStudio2012/BeastConfig.h index 602786b529..a57d834b55 100644 --- a/Subtrees/beast/Builds/VisualStudio2012/BeastConfig.h +++ b/Subtrees/beast/Builds/VisualStudio2012/BeastConfig.h @@ -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(). diff --git a/Subtrees/beast/TODO.txt b/Subtrees/beast/TODO.txt index cfee8af9b9..b352136784 100644 --- a/Subtrees/beast/TODO.txt +++ b/Subtrees/beast/TODO.txt @@ -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 diff --git a/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.cpp b/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.cpp index 68d864d509..d2887a328e 100644 --- a/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.cpp +++ b/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.cpp @@ -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 , 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); } }; diff --git a/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.h b/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.h index f65a39ad39..30835ccf0b 100644 --- a/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.h +++ b/Subtrees/beast/modules/beast_core/diagnostic/beast_FatalError.h @@ -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 , 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 diff --git a/modules/ripple_app/main/ripple_FatalErrorReporter.cpp b/modules/ripple_app/main/ripple_FatalErrorReporter.cpp index 5be5920c40..a0c72987bf 100644 --- a/modules/ripple_app/main/ripple_FatalErrorReporter.cpp +++ b/modules/ripple_app/main/ripple_FatalErrorReporter.cpp @@ -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__); } }; diff --git a/modules/ripple_app/main/ripple_FatalErrorReporter.h b/modules/ripple_app/main/ripple_FatalErrorReporter.h index 282177f0f2..58db5c3c08 100644 --- a/modules/ripple_app/main/ripple_FatalErrorReporter.h +++ b/modules/ripple_app/main/ripple_FatalErrorReporter.h @@ -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