diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index 25a6eab61..dfba11560 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -86,7 +86,7 @@ - HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEBUG;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NOMINMAX;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) + HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEBUG;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NOMINMAX;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async @@ -123,7 +123,7 @@ - HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEBUG;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NOMINMAX;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) + HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEBUG;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NOMINMAX;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRTDBG_MAP_ALLOC;_CRT_SECURE_NO_WARNINGS;_DEBUG;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async @@ -160,7 +160,7 @@ - HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NDEBUG;NOMINMAX;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) + HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NDEBUG;NOMINMAX;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async @@ -195,7 +195,7 @@ - HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NDEBUG;NOMINMAX;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) + HAVE_USLEEP=1;SOCI_CXX_C11=1;_WIN32_WINNT=0x6000;BOOST_NO_AUTO_PTR;DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER;NDEBUG;NOMINMAX;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) ..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories) 4800;4244;4267;4018 Async @@ -2016,6 +2016,12 @@ ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) + + True + True + ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) + ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) + True True @@ -2072,12 +2078,6 @@ ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) - - True - True - ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) - ..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories) - True True diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index 8214e8077..cf260b47e 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -2553,6 +2553,9 @@ ripple\core\impl + + ripple\core\impl + ripple\core\impl @@ -2607,9 +2610,6 @@ ripple\core\tests - - ripple\core\tests - ripple\core\tests diff --git a/SConstruct b/SConstruct index ca5c0adba..3e581ecb3 100644 --- a/SConstruct +++ b/SConstruct @@ -365,7 +365,6 @@ def config_base(env): ,{'SOCI_CXX_C11' : '1'} ,'_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS' ,'BOOST_NO_AUTO_PTR' - ,'NO_LOG_UNHANDLED_EXCEPTIONS' ]) if Beast.system.windows: @@ -385,6 +384,7 @@ def config_base(env): openssl = os.path.join(OSX_OPENSSL_ROOT, most_recent) env.Prepend(CPPPATH='%s/include' % openssl) env.Prepend(LIBPATH=['%s/lib' % openssl]) + env.Append(CPPDEFINES=['NO_LOG_UNHANDLED_EXCEPTIONS']) # handle command-line arguments profile_jemalloc = ARGUMENTS.get('profile-jemalloc') diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index fcd048394..0df70a6f8 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include + #if defined(BEAST_LINUX) || defined(BEAST_MAC) || defined(BEAST_BSD) #include #endif @@ -452,7 +454,8 @@ int run (int argc, char** argv) app->doStart(); // Block until we get a stop RPC. - app->run(); + ripple::threadEntry ( + app.get(), &Application::run, "Main::run()"); // Try to write out some entropy to use the next time we start. auto entropy = getEntropyFile (app->config()); @@ -517,6 +520,9 @@ int main (int argc, char** argv) #endif atexit(&google::protobuf::ShutdownProtobufLibrary); +#ifndef NO_LOG_UNHANDLED_EXCEPTIONS + std::set_terminate(ripple::terminateHandler); +#endif auto const result (ripple::run (argc, argv)); diff --git a/src/ripple/basics/contract.h b/src/ripple/basics/contract.h index 386aff65b..b2f481fdb 100644 --- a/src/ripple/basics/contract.h +++ b/src/ripple/basics/contract.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_BASICS_CONTRACT_H_INCLUDED #define RIPPLE_BASICS_CONTRACT_H_INCLUDED +#include #include #include #include @@ -62,7 +63,8 @@ Throw (Args&&... args) "Exception must derive from std::exception."); E e(std::forward(args)...); - LogThrow (std::string("Throwing exception: ") + e.what()); + LogThrow (std::string("Throwing exception of type " + + beast::type_name() +": ") + e.what()); throw e; } diff --git a/src/ripple/core/ThreadEntry.h b/src/ripple/core/ThreadEntry.h index f6108f9cc..1722218d4 100644 --- a/src/ripple/core/ThreadEntry.h +++ b/src/ripple/core/ThreadEntry.h @@ -20,35 +20,35 @@ #ifndef RIPPLE_CORE_THREAD_ENTRY_H_INCLUDED #define RIPPLE_CORE_THREAD_ENTRY_H_INCLUDED -#include -#include // forced_unwind exception -#include -#include -#include #include -#include namespace ripple { + +#ifndef NO_LOG_UNHANDLED_EXCEPTIONS +namespace detail +{ +void setThreadName(std::string name); +} + +void terminateHandler(); +#endif + /** Report uncaught exceptions to DebugLog and cerr -Catch all exceptions that escape the called function. Report as much -information as can be extracted from the exception to both the DebugLog -and cerr. +The actual reporting occurs in a terminate handler. This function +stores information about which thread is running in thread local +storage. That way the terminate handler can report not just the +exception, but also the thread the exception was thrown in. The idea is to use this routine at the top of a thread, since on many platforms the stack trace for an uncaught exception on a thread is almost useless. For those platforms where the stack trace from an uncaught exception is -useful (e.g., OS X) this routine is a no-op. That way a catch will not -interfere with the stack trace showing the real source of the uncaught -exception. - -Note that any extra information is passed using a lambda because we only -want to do the work of building the string in the unlikely event of an -uncaught exception. The lambda is only called in the error case. +useful (e.g., OS X) this routine is turned into a no-op (because the +preprocessor symbol NO_LOG_UNHANDLED_EXCEPTIONS is defined). Usage example @@ -87,65 +87,15 @@ int main () @param t Pointer to object to call. @param threadTop Pointer to member function of t to call. @param name Name of function to log. -@param lamdba Optional lambda that returns additional text for the log. */ -template -void threadEntry ( - T* t, R (T::*threadTop) (), char const* name, L&& lambda) -{ - // Enforce that lambda takes no parameters and returns std::string. - static_assert ( - std::is_convertible::value, - "Last argument must be a lamdba taking no arguments " - "and returning std::string."); - -#ifdef NO_LOG_UNHANDLED_EXCEPTIONS - // Don't use a try block so we can get a good call stack. - ((t)->*(threadTop)) (); -#else - // Local lambda for string formatting and re-throwing. - auto logUncaughtException = - [name, &lambda] (char const* exName) - { - std::stringstream ss; - ss << "Unhandled exception in " << name - << "; Exception: " << exName; - - std::string extra = lambda(); - if (! extra.empty()) - ss << "; " << extra; - - JLOG(debugLog().fatal()) << ss.str(); - std::cerr << ss.str() << std::endl; - throw; - }; - - try - { - // Call passed in member function. - ((t)->*(threadTop)) (); - } - catch (std::exception const& ex) - { - logUncaughtException (ex.what()); - } - catch (boost::coroutines::detail::forced_unwind const&) - { - logUncaughtException ("forced_unwind"); - } - catch (...) - { - logUncaughtException ("unknown exception type"); - } -#endif // NO_LOG_UNHANDLED_EXCEPTIONS else -} - -// Handle the common case where there is no additional local information. template -inline void threadEntry ( - T* t, R (T::*threadTop) (), char const* name) +void threadEntry ( + T* t, R (T::*threadTop) (), std::string name) { - threadEntry (t, threadTop, name, []{ return std::string(); }); +#ifndef NO_LOG_UNHANDLED_EXCEPTIONS + detail::setThreadName (std::move(name)); +#endif + ((t)->*(threadTop)) (); } } // namespace ripple diff --git a/src/ripple/core/impl/Job.cpp b/src/ripple/core/impl/Job.cpp index 5102d9078..3fa158304 100644 --- a/src/ripple/core/impl/Job.cpp +++ b/src/ripple/core/impl/Job.cpp @@ -77,15 +77,12 @@ bool Job::shouldCancel () const void Job::doJob () { - threadEntry (this, &Job::doJobImpl, "Job::doJob()", - [this] () - { - std::stringstream ss; - ss << "Job name: " << this->mName - << "; Job type: " << this->mType - << "; Job info: " << this->mJob.target_type().name(); - return ss.str(); - }); + std::stringstream ss; + ss << "Job::doJob(); Job name: " + << mName << "; Job type: " << mType + << "; Job info: " << mJob.target_type ().name (); + + threadEntry (this, &Job::doJobImpl, ss.str()); } void Job::rename (std::string const& newName) diff --git a/src/ripple/core/impl/ThreadEntry.cpp b/src/ripple/core/impl/ThreadEntry.cpp new file mode 100644 index 000000000..1649094de --- /dev/null +++ b/src/ripple/core/impl/ThreadEntry.cpp @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + 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. +*/ +//============================================================================== + +#include +#include +#include + +#include +#include +#include + +namespace ripple { + +#ifndef NO_LOG_UNHANDLED_EXCEPTIONS +namespace detail { +thread_local +std::string threadName; + +void setThreadName(std::string name) +{ + threadName = std::move(name); +} +} + +void terminateHandler() +{ + if (std::current_exception()) + { + try + { + throw; + } + catch (const std::exception& e) + { + std::cerr << detail::threadName << ": " << e.what () << '\n'; + JLOG(debugLog().fatal()) + << detail::threadName << ": " << e.what () << '\n'; + } + catch (boost::coroutines::detail::forced_unwind const&) + { + std::cerr << detail::threadName << ": forced_unwind\n"; + JLOG(debugLog().fatal()) + << detail::threadName << ": forced_unwind\n"; + } + catch (...) + { + std::cerr << detail::threadName << ": unknown exception\n"; + JLOG (debugLog ().fatal ()) + << detail::threadName << ": unknown exception\n"; + } + } +} +#endif + +} diff --git a/src/ripple/core/impl/Workers.cpp b/src/ripple/core/impl/Workers.cpp index ce8270cf2..fed9dce79 100644 --- a/src/ripple/core/impl/Workers.cpp +++ b/src/ripple/core/impl/Workers.cpp @@ -160,10 +160,7 @@ void Workers::Worker::run () { // Call runImpl() and report if any exceptions escape runImpl. threadEntry (this, &Workers::Worker::runImpl, - "Workers::Worker::run()", [this] () - { - return "Thread: " + Thread::getThreadName(); - }); + "Workers::Worker::run(); Thread: " + Thread::getThreadName()); } void Workers::Worker::runImpl () diff --git a/src/ripple/core/tests/ReportUncaughtException.test.cpp b/src/ripple/core/tests/ReportUncaughtException.test.cpp deleted file mode 100644 index 744259eb9..000000000 --- a/src/ripple/core/tests/ReportUncaughtException.test.cpp +++ /dev/null @@ -1,169 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2016 Ripple Labs Inc. - - 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. -*/ -//============================================================================== - -#include - -#include -#include -#include - -namespace ripple { - -// threadEntry is disabled if NO_LOG_UNHANDLED_EXCEPTIONS is defined. -#ifndef NO_LOG_UNHANDLED_EXCEPTIONS - -class ReportUncaughtException_test : public TestSuite -{ -public: - class TestSink : public beast::Journal::Sink - { - std::stringstream ss_; - - public: - TestSink() - : Sink (beast::severities::kFatal, false) - { - } - - std::string getText() const - { - return ss_.str(); - } - - void reset() - { - ss_.str(""); - } - - void - write (beast::severities::Severity level, std::string const& s) override - { - if (level >= threshold()) - ss_ << s; - } - }; - - class ExceptionGen - { - public: - // A place to keep methods that throw. - void dontThrow() - { - return; - } - - void throwStdExcept() - { - throw std::logic_error ("logic_error"); - } - - void throwForcedUnwind() - { - throw boost::coroutines::detail::forced_unwind (); - } - - void throwWeird() - { - throw std::string ("Pretty unusual..."); - } - }; - - void test () - { - // Install our own debug Sink so we can see what gets written. - // Retain the old Sink so we can put it back. - auto testSink = std::make_unique(); - TestSink& sinkRef = *testSink; - std::unique_ptr oldSink = - setDebugLogSink (std::move (testSink)); - - ExceptionGen exGen; - - // Make sure nothing gets logged if there's no exception. - threadEntry (&exGen, &ExceptionGen::dontThrow, "noThrow"); - expect (sinkRef.getText() == ""); - sinkRef.reset(); - - threadEntry (&exGen, &ExceptionGen::dontThrow, "noThrow", - [] { return "Just noise"; }); - - expect (sinkRef.getText() == ""); - sinkRef.reset(); - - using PExGenMemFn = void (ExceptionGen::*) (); - auto testCase = [this, &exGen, &sinkRef] ( - PExGenMemFn call, char const* description) - { - std::string want = std::string ("Unhandled exception in " - "testFn; Exception: ") + description; - - // Test the case without the closing lambda. - bool gotException = false; - try - { - threadEntry (&exGen, call, "testFn"); - } - catch (...) - { - gotException = true; - } - expect (gotException == true); - expect (sinkRef.getText() == want); - sinkRef.reset(); - - // Try the case with the closing lambda. - gotException = false; - try - { - threadEntry (&exGen, call, "testFn", - []{ return "extra info"; }); - } - catch (...) - { - gotException = true; - } - expect (gotException == true); - expect (sinkRef.getText() == want + "; extra info"); - sinkRef.reset(); - }; - - // Test logging for a stad::exception. - testCase (&ExceptionGen::throwStdExcept, "logic_error"); - - // Test logging for a forced_unwind. - testCase (&ExceptionGen::throwForcedUnwind, "forced_unwind"); - - // Test logging for none of the above. - testCase (&ExceptionGen::throwWeird, "unknown exception type"); - - // We're done with TestSink. Re-install the old Sink. - setDebugLogSink (std::move (oldSink)); - } - - void run () - { - test (); - } -}; - -BEAST_DEFINE_TESTSUITE (ReportUncaughtException, core, ripple); - -#endif // NO_LOG_UNHANDLED_EXCEPTIONS - -} // ripple diff --git a/src/ripple/unity/core.cpp b/src/ripple/unity/core.cpp index a3259f992..11a708a35 100644 --- a/src/ripple/unity/core.cpp +++ b/src/ripple/unity/core.cpp @@ -30,11 +30,11 @@ #include #include #include +#include #include #include #include #include -#include #include