From a937b97a61598ac519b398af0048da0c01a4c2ac Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 30 Jul 2013 19:19:16 -0700 Subject: [PATCH] Add FatalError --- Builds/VisualStudio2012/beast.vcxproj | 5 + Builds/VisualStudio2012/beast.vcxproj.filters | 20 ++-- TODO.txt | 1 + modules/beast_core/beast_core.cpp | 1 + modules/beast_core/beast_core.h | 3 +- .../diagnostic/beast_FatalError.cpp | 109 ++++++++++++++++++ .../beast_core/diagnostic/beast_FatalError.h | 97 ++++++++++++++++ .../diagnostic/beast_ProtectedCall.cpp | 2 +- .../beast_core/diagnostic/beast_UnitTest.h | 2 +- 9 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 modules/beast_core/diagnostic/beast_FatalError.cpp create mode 100644 modules/beast_core/diagnostic/beast_FatalError.h diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index 227c1f85..71e0701f 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -116,6 +116,7 @@ + @@ -402,6 +403,10 @@ true true + + true + true + true true diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index 709802ca..873dc89a 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -44,9 +44,6 @@ {abe24d69-c1d1-49e2-98a1-1e7bf760b97a} - - {0fa6b76b-305f-473c-9b94-c1028a3af3fc} - {e2339099-bb8e-4437-ae8b-d4f64ef1e3f3} @@ -140,6 +137,9 @@ {10ef778f-6b81-437e-a6ef-558ff4163acc} + + {0fa6b76b-305f-473c-9b94-c1028a3af3fc} + @@ -212,10 +212,10 @@ beast_core\json - beast_core\logging + beast_core\memory\logging - beast_core\logging + beast_core\memory\logging beast_core\maths @@ -731,6 +731,9 @@ beast_basics\threads + + beast_core\diagnostic + @@ -773,10 +776,10 @@ beast_core\json - beast_core\logging + beast_core\memory\logging - beast_core\logging + beast_core\memory\logging beast_core\maths @@ -1141,6 +1144,9 @@ beast_basics\threads + + beast_core\diagnostic + diff --git a/TODO.txt b/TODO.txt index 4d11e8dd..21133462 100644 --- a/TODO.txt +++ b/TODO.txt @@ -13,6 +13,7 @@ BEAST TODO - Return size_t from hash function, take out upperLimit, move mod % to caller - Make hash function a functor using operator() - Implement HardenedHashFunctions + - Fix problem with assigning to the result of operator[] maybe use a proxy? - Set sqlite thread safety model to '2' in beast_sqlite diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp index becc39af..e3ae5c0f 100644 --- a/modules/beast_core/beast_core.cpp +++ b/modules/beast_core/beast_core.cpp @@ -146,6 +146,7 @@ namespace beast #include "diagnostic/beast_Debug.cpp" #include "diagnostic/beast_Error.cpp" +#include "diagnostic/beast_FatalError.cpp" #include "diagnostic/beast_FPUFlags.cpp" #include "diagnostic/beast_LeakChecked.cpp" #include "diagnostic/beast_ProtectedCall.cpp" diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h index 08eceb44..fc0a3361 100644 --- a/modules/beast_core/beast_core.h +++ b/modules/beast_core/beast_core.h @@ -213,6 +213,8 @@ namespace beast #include "text/beast_String.h" +#include "threads/beast_CriticalSection.h" +#include "diagnostic/beast_FatalError.h" #include "diagnostic/beast_SafeBool.h" #include "diagnostic/beast_Error.h" #include "diagnostic/beast_Debug.h" @@ -309,7 +311,6 @@ namespace beast #include "text/beast_StringPool.h" #include "text/beast_TextDiff.h" #include "threads/beast_ChildProcess.h" -#include "threads/beast_CriticalSection.h" #include "threads/beast_DynamicLibrary.h" #include "threads/beast_HighResolutionTimer.h" #include "threads/beast_InterProcessLock.h" diff --git a/modules/beast_core/diagnostic/beast_FatalError.cpp b/modules/beast_core/diagnostic/beast_FatalError.cpp new file mode 100644 index 00000000..6737f9db --- /dev/null +++ b/modules/beast_core/diagnostic/beast_FatalError.cpp @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +Static::Storage , FatalError> FatalError::s_reporter; + +void FatalError::setReporter (Reporter& reporter) +{ + s_reporter->compareAndSetBool (&reporter, nullptr); +} + +void FatalError::resetReporter (Reporter& reporter) +{ + s_reporter->compareAndSetBool (nullptr, &reporter); +} + +FatalError::FatalError (char const* message, char const* fileName, int lineNumber) +{ + LockType::ScopedLockType lock (m_mutex); + + String const backtraceString = SystemStats::getStackBacktrace (); + + char const* const szStackBacktrace = backtraceString.toRawUTF8 (); + + String const fileNameString = File (fileName).getFileName (); + + char const* const szFileName = fileNameString.toRawUTF8 (); + + Reporter* const reporter = s_reporter->get (); + + if (reporter != nullptr) + { + reporter->onFatalError (message, szStackBacktrace, szFileName, lineNumber); + } + + Process::terminate (); +} + +//------------------------------------------------------------------------------ + +// Yes even this class can have a unit test. It's manually triggered though. +// +class FatalErrorTests : public UnitTest +{ +public: + FatalErrorTests () : UnitTest ("FatalError", "beast", runManual) + { + } + + 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__); + } +}; + +static FatalErrorTests fatalErrorTests; diff --git a/modules/beast_core/diagnostic/beast_FatalError.h b/modules/beast_core/diagnostic/beast_FatalError.h new file mode 100644 index 00000000..130524ea --- /dev/null +++ b/modules/beast_core/diagnostic/beast_FatalError.h @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_FATALERROR_H_INCLUDED +#define BEAST_FATALERROR_H_INCLUDED + +/** Signal a fatal error. + + A fatal error indicates that the program has encountered an unexpected + situation and cannot continue safely. Reasons for raising a fatal error + would be to protect data integrity, prevent valuable resources from being + wasted, or to ensure that the user does not experience undefined behavior. + + This function will end the process with exit code EXIT_FAILURE. Before + the process is terminated, a listener object gets notified so that the + client application can perform logging or emit further diagnostics. +*/ +class FatalError : Uncopyable +{ +public: + struct 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. + + @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 lineNumber The line number in the source file. + */ + virtual void onFatalError (char const* message, + char const* stackBacktrace, + char const* fileName, + int lineNumber) = 0; + }; + + /** 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. + + The solution is not to use objects with static storage duration + that have non-trivial constructors, use SharedSingleton instead. + + If a reporter was previously set, this routine will do nothing. + + @see SharedSingleton + */ + static void setReporter (Reporter& reporter); + + /** Clear the fatal error reporter. + + If the current reporter is the same as the one passed in, this + will remove the reporter. + */ + static void resetReporter (Reporter& reporter); + + /** Raise a fatal error. + + If multiple threads raise an error, only one will succeed. The + 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 lineNumber Pass __LINE__ here. + */ + FatalError (char const* message, char const* fileName, int lineNumber); + +private: + typedef CriticalSection LockType; + + static Static::Storage , FatalError> s_reporter; + + LockType m_mutex; +}; + +#endif diff --git a/modules/beast_core/diagnostic/beast_ProtectedCall.cpp b/modules/beast_core/diagnostic/beast_ProtectedCall.cpp index b5b7c190..ae23396c 100644 --- a/modules/beast_core/diagnostic/beast_ProtectedCall.cpp +++ b/modules/beast_core/diagnostic/beast_ProtectedCall.cpp @@ -277,7 +277,7 @@ bool CatchAny (Function f, bool returnFromException) //------------------------------------------------------------------------------ -void ProtectedCall::DefaultHandler::onException (ProtectedCall::Exception const& e) const +void ProtectedCall::DefaultHandler::onException (ProtectedCall::Exception const&) const { } diff --git a/modules/beast_core/diagnostic/beast_UnitTest.h b/modules/beast_core/diagnostic/beast_UnitTest.h index 80dbfd83..1f950d9d 100644 --- a/modules/beast_core/diagnostic/beast_UnitTest.h +++ b/modules/beast_core/diagnostic/beast_UnitTest.h @@ -64,7 +64,7 @@ class UnitTests; @see UnitTests */ -class BEAST_API UnitTest : Uncopyable +class BEAST_API UnitTest : public Uncopyable { public: /** When the test should be run.