mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 11:15:56 +00:00
Better unhandled exception handling:
Log thread name and exception type on unhandled exceptions and use a terminate handler to get a stack trace that includes the function that thows the exception.
This commit is contained in:
@@ -86,7 +86,7 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">
|
||||
<ClCompile>
|
||||
<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;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)</PreprocessorDefinitions>
|
||||
<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)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4800;4244;4267;4018</DisableSpecificWarnings>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
@@ -123,7 +123,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='debug|x64'">
|
||||
<ClCompile>
|
||||
<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;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)</PreprocessorDefinitions>
|
||||
<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)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4800;4244;4267;4018</DisableSpecificWarnings>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
@@ -160,7 +160,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">
|
||||
<ClCompile>
|
||||
<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;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<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)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4800;4244;4267;4018</DisableSpecificWarnings>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
@@ -195,7 +195,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release|x64'">
|
||||
<ClCompile>
|
||||
<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;NO_LOG_UNHANDLED_EXCEPTIONS;OPENSSL_NO_SSL2;WIN32_CONSOLE;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<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)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\build\proto;..\..\src;..\..\src\beast;..\..\src\beast\extras;..\..\src\beast\include;..\..\src\protobuf\src;..\..\src\protobuf\vsprojects;..\..\src\soci\include;..\..\src\soci\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4800;4244;4267;4018</DisableSpecificWarnings>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
@@ -2016,6 +2016,12 @@
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\impl\ThreadEntry.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\impl\TimeKeeper.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
@@ -2072,12 +2078,6 @@
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\tests\ReportUncaughtException.test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\tests\SociDB.test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
|
||||
@@ -2553,6 +2553,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\core\impl\Stoppable.cpp">
|
||||
<Filter>ripple\core\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\impl\ThreadEntry.cpp">
|
||||
<Filter>ripple\core\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\impl\TimeKeeper.cpp">
|
||||
<Filter>ripple\core\impl</Filter>
|
||||
</ClCompile>
|
||||
@@ -2607,9 +2610,6 @@
|
||||
<ClCompile Include="..\..\src\ripple\core\tests\LoadFeeTrack.test.cpp">
|
||||
<Filter>ripple\core\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\tests\ReportUncaughtException.test.cpp">
|
||||
<Filter>ripple\core\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\core\tests\SociDB.test.cpp">
|
||||
<Filter>ripple\core\tests</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <ripple/basics/ThreadName.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/core/ThreadEntry.h>
|
||||
#include <ripple/core/TimeKeeper.h>
|
||||
#include <ripple/crypto/csprng.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
@@ -45,6 +46,7 @@
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
|
||||
#if defined(BEAST_LINUX) || defined(BEAST_MAC) || defined(BEAST_BSD)
|
||||
#include <sys/resource.h>
|
||||
#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));
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef RIPPLE_BASICS_CONTRACT_H_INCLUDED
|
||||
#define RIPPLE_BASICS_CONTRACT_H_INCLUDED
|
||||
|
||||
#include <ripple/beast/type_name.h>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
@@ -62,7 +63,8 @@ Throw (Args&&... args)
|
||||
"Exception must derive from std::exception.");
|
||||
|
||||
E e(std::forward<Args>(args)...);
|
||||
LogThrow (std::string("Throwing exception: ") + e.what());
|
||||
LogThrow (std::string("Throwing exception of type " +
|
||||
beast::type_name<E>() +": ") + e.what());
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,35 +20,35 @@
|
||||
#ifndef RIPPLE_CORE_THREAD_ENTRY_H_INCLUDED
|
||||
#define RIPPLE_CORE_THREAD_ENTRY_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <boost/coroutine/exceptions.hpp> // forced_unwind exception
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
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 <typename T, typename R, typename L>
|
||||
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<decltype (lambda()), std::string const>::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 <typename T, typename R>
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
71
src/ripple/core/impl/ThreadEntry.cpp
Normal file
71
src/ripple/core/impl/ThreadEntry.cpp
Normal file
@@ -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 <BeastConfig.h>
|
||||
#include <ripple/core/ThreadEntry.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
|
||||
#include <boost/coroutine/exceptions.hpp>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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 <BeastConfig.h>
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/TestSuite.h>
|
||||
#include <ripple/core/ThreadEntry.h>
|
||||
|
||||
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>();
|
||||
TestSink& sinkRef = *testSink;
|
||||
std::unique_ptr<beast::Journal::Sink> 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
|
||||
@@ -30,11 +30,11 @@
|
||||
#include <ripple/core/impl/SNTPClock.cpp>
|
||||
#include <ripple/core/impl/Stoppable.cpp>
|
||||
#include <ripple/core/impl/TimeKeeper.cpp>
|
||||
#include <ripple/core/impl/ThreadEntry.cpp>
|
||||
#include <ripple/core/impl/Workers.cpp>
|
||||
|
||||
#include <ripple/core/tests/Config.test.cpp>
|
||||
#include <ripple/core/tests/Coroutine.test.cpp>
|
||||
#include <ripple/core/tests/LoadFeeTrack.test.cpp>
|
||||
#include <ripple/core/tests/ReportUncaughtException.test.cpp>
|
||||
#include <ripple/core/tests/Stoppable.test.cpp>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user