mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 06:25:51 +00:00
Log uncaught exceptions at the top of threads (RIPD-1166)
This commit is contained in:
committed by
Nik Bougalis
parent
7295d7f4bb
commit
fdd1f2ec36
@@ -2047,6 +2047,8 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\LoadMonitor.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\ReportUncaughtException.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\SociDB.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\Stoppable.h">
|
||||
@@ -2069,6 +2071,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\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>
|
||||
|
||||
@@ -2592,6 +2592,9 @@
|
||||
<ClInclude Include="..\..\src\ripple\core\LoadMonitor.h">
|
||||
<Filter>ripple\core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\ReportUncaughtException.h">
|
||||
<Filter>ripple\core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\core\SociDB.h">
|
||||
<Filter>ripple\core</Filter>
|
||||
</ClInclude>
|
||||
@@ -2607,6 +2610,9 @@
|
||||
<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>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/core/LoadFeeTrack.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
|
||||
@@ -241,6 +242,12 @@ private:
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &LedgerCleanerImp::runImpl, "LedgerCleanerImp::run()");
|
||||
}
|
||||
|
||||
void runImpl ()
|
||||
{
|
||||
beast::Thread::setCurrentThreadName ("LedgerCleaner");
|
||||
JLOG (j_.debug()) << "Started";
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/basics/UptimeTimer.h>
|
||||
#include <ripple/core/LoadFeeTrack.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <memory>
|
||||
@@ -106,6 +107,11 @@ void LoadManager::onStop ()
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LoadManager::run ()
|
||||
{
|
||||
reportUncaughtException (this, &LoadManager::runImpl, "LoadManager::run()");
|
||||
}
|
||||
|
||||
void LoadManager::runImpl ()
|
||||
{
|
||||
beast::Thread::setCurrentThreadName ("LoadManager");
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
|
||||
private:
|
||||
void run ();
|
||||
void runImpl ();
|
||||
|
||||
private:
|
||||
Application& app_;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@@ -296,6 +297,13 @@ SHAMapStoreImp::copyNode (std::uint64_t& nodeCount,
|
||||
|
||||
void
|
||||
SHAMapStoreImp::run()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &SHAMapStoreImp::runImpl, "SHAMapStoreImp::run()");
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapStoreImp::runImpl()
|
||||
{
|
||||
LedgerIndex lastRotated = state_db_.getState().lastRotated;
|
||||
netOPs_ = &app_.getOPs();
|
||||
|
||||
@@ -175,6 +175,7 @@ private:
|
||||
// callback for visitNodes
|
||||
bool copyNode (std::uint64_t& nodeCount, SHAMapAbstractNode const &node);
|
||||
void run();
|
||||
void runImpl();
|
||||
void dbPaths();
|
||||
std::shared_ptr <NodeStore::Backend> makeBackendRotating (
|
||||
std::string path = std::string());
|
||||
|
||||
@@ -249,8 +249,12 @@ private:
|
||||
//------------------------------------------------------------------------------
|
||||
// Debug logging:
|
||||
|
||||
/** Set the sink for the debug journal. */
|
||||
void
|
||||
/** Set the sink for the debug journal.
|
||||
|
||||
@param sink unique_ptr to new debug Sink.
|
||||
@return unique_ptr to the previous Sink. nullptr if there was no Sink.
|
||||
*/
|
||||
std::unique_ptr<beast::Journal::Sink>
|
||||
setDebugLogSink(
|
||||
std::unique_ptr<beast::Journal::Sink> sink);
|
||||
|
||||
|
||||
@@ -364,17 +364,20 @@ public:
|
||||
DebugSink(DebugSink&&) = delete;
|
||||
DebugSink& operator=(DebugSink&&) = delete;
|
||||
|
||||
void
|
||||
std::unique_ptr<beast::Journal::Sink>
|
||||
set(std::unique_ptr<beast::Journal::Sink> sink)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(m_);
|
||||
|
||||
holder_ = std::move(sink);
|
||||
using std::swap;
|
||||
swap (holder_, sink);
|
||||
|
||||
if (holder_)
|
||||
sink_ = *holder_;
|
||||
else
|
||||
sink_ = beast::Journal::getNullSink();
|
||||
|
||||
return sink;
|
||||
}
|
||||
|
||||
beast::Journal::Sink&
|
||||
@@ -393,11 +396,11 @@ debugSink()
|
||||
return _;
|
||||
}
|
||||
|
||||
void
|
||||
std::unique_ptr<beast::Journal::Sink>
|
||||
setDebugLogSink(
|
||||
std::unique_ptr<beast::Journal::Sink> sink)
|
||||
{
|
||||
debugSink().set(std::move(sink));
|
||||
return debugSink().set(std::move(sink));
|
||||
}
|
||||
|
||||
beast::Journal::Stream
|
||||
|
||||
@@ -134,6 +134,9 @@ public:
|
||||
bool operator<= (const Job& j) const;
|
||||
bool operator>= (const Job& j) const;
|
||||
|
||||
private:
|
||||
void doJobImpl();
|
||||
|
||||
private:
|
||||
CancelCallback m_cancelCallback;
|
||||
JobType mType;
|
||||
|
||||
153
src/ripple/core/ReportUncaughtException.h
Normal file
153
src/ripple/core/ReportUncaughtException.h
Normal file
@@ -0,0 +1,153 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_REPORT_UNCAUGHT_EXCEPTION_H_INCLUDED
|
||||
#define RIPPLE_CORE_REPORT_UNCAUGHT_EXCEPTION_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
|
||||
{
|
||||
/**
|
||||
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 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.
|
||||
|
||||
Usage example
|
||||
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
|
||||
class ThreadedHandler
|
||||
{
|
||||
public:
|
||||
void operator() ()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &ThreadedHandler::runImpl, "ThreadedHandler::operator()");
|
||||
}
|
||||
|
||||
void runImpl()
|
||||
{
|
||||
// do stuff.
|
||||
throw std::logic_error("logic_error: What was I thinking?");
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
ThreadedHandler handler;
|
||||
std::thread t {handler};
|
||||
std::this_thread::sleep_for (1s);
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@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 reportUncaughtException (
|
||||
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 __APPLE__
|
||||
// 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()) << 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& ex)
|
||||
{
|
||||
logUncaughtException ("forced_unwind");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logUncaughtException ("unknown exception type");
|
||||
}
|
||||
#endif // __APPLE__ else
|
||||
}
|
||||
|
||||
// Handle the common case where there is no additional local information.
|
||||
template <typename T, typename R>
|
||||
inline void reportUncaughtException (
|
||||
T* t, R (T::*threadTop) (), char const* name)
|
||||
{
|
||||
reportUncaughtException (t, threadTop, name, []{ return std::string(); });
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/core/DeadlineTimer.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -97,7 +98,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void run ()
|
||||
void run () override
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &Manager::runImpl, "DeadlineTimer::Manager::run()");
|
||||
}
|
||||
|
||||
void runImpl ()
|
||||
{
|
||||
while (! threadShouldExit ())
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/core/Job.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace ripple {
|
||||
@@ -76,14 +77,15 @@ bool Job::shouldCancel () const
|
||||
|
||||
void Job::doJob ()
|
||||
{
|
||||
m_loadEvent->start ();
|
||||
m_loadEvent->reName (mName);
|
||||
|
||||
mJob (*this);
|
||||
|
||||
// Destroy the lambda, otherwise we won't include
|
||||
// its duration in the time measurement
|
||||
mJob = std::function<void(Job&)>();
|
||||
reportUncaughtException (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();
|
||||
});
|
||||
}
|
||||
|
||||
void Job::rename (std::string const& newName)
|
||||
@@ -135,4 +137,16 @@ bool Job::operator<= (const Job& j) const
|
||||
return mJobIndex <= j.mJobIndex;
|
||||
}
|
||||
|
||||
void Job::doJobImpl ()
|
||||
{
|
||||
m_loadEvent->start ();
|
||||
m_loadEvent->reName (mName);
|
||||
|
||||
mJob (*this);
|
||||
|
||||
// Destroy the lambda, otherwise we won't include
|
||||
// its duration in the time measurement
|
||||
mJob = std::function<void(Job&)>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/core/impl/SNTPClock.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/ThreadName.h>
|
||||
#include <ripple/basics/random.h>
|
||||
#include <ripple/core/impl/SNTPClock.h>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cmath>
|
||||
@@ -203,7 +204,12 @@ public:
|
||||
void doRun ()
|
||||
{
|
||||
setCallingThreadName("SNTPClock");
|
||||
io_service_.run();
|
||||
|
||||
// Get the address of an overloaded asio method
|
||||
using Pio_service_mem = std::size_t (boost::asio::io_service::*)();
|
||||
Pio_service_mem pRun = &boost::asio::io_service::run;
|
||||
|
||||
reportUncaughtException (&io_service_, pRun, "SNTPClientImp::doRun()");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/core/impl/Workers.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <cassert>
|
||||
|
||||
@@ -156,6 +157,16 @@ Workers::Worker::~Worker ()
|
||||
}
|
||||
|
||||
void Workers::Worker::run ()
|
||||
{
|
||||
// Call runImpl() and report if any exceptions escape runImpl.
|
||||
reportUncaughtException (this, &Workers::Worker::runImpl,
|
||||
"Workers::Worker::run()", [this] ()
|
||||
{
|
||||
return "Thread: " + Thread::getThreadName();
|
||||
});
|
||||
}
|
||||
|
||||
void Workers::Worker::runImpl ()
|
||||
{
|
||||
while (! threadShouldExit ())
|
||||
{
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
|
||||
Active: Running the task processing loop.
|
||||
Idle: Active, but blocked on waiting for a task.
|
||||
Pausd: Blocked waiting to exit or become active.
|
||||
Paused: Blocked waiting to exit or become active.
|
||||
*/
|
||||
class Worker
|
||||
: public beast::LockFreeStack <Worker>::Node
|
||||
@@ -127,7 +127,8 @@ private:
|
||||
~Worker ();
|
||||
|
||||
private:
|
||||
void run ();
|
||||
void run () override;
|
||||
void runImpl ();
|
||||
|
||||
private:
|
||||
Workers& m_workers;
|
||||
|
||||
169
src/ripple/core/tests/ReportUncaughtException.test.cpp
Normal file
169
src/ripple/core/tests/ReportUncaughtException.test.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/ReportUncaughtException.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// reportUncaughtException is disabled if __APPLE__ is defined.
|
||||
#ifndef __APPLE__
|
||||
|
||||
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.
|
||||
reportUncaughtException (&exGen, &ExceptionGen::dontThrow, "noThrow");
|
||||
expect (sinkRef.getText() == "");
|
||||
sinkRef.reset();
|
||||
|
||||
reportUncaughtException (&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
|
||||
{
|
||||
reportUncaughtException (&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
|
||||
{
|
||||
reportUncaughtException (&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 // __APPLE__
|
||||
|
||||
} // ripple
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <ripple/basics/KeyCache.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/basics/TaggedCache.h>
|
||||
@@ -333,8 +334,15 @@ public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Entry point for async read threads
|
||||
// Uncaught exception handling for async read threads
|
||||
void threadEntry ()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &DatabaseImp::threadEntryImpl, "DatabaseImp::threadEntry()");
|
||||
}
|
||||
|
||||
// Entry point for async read threads
|
||||
void threadEntryImpl ()
|
||||
{
|
||||
beast::Thread::setCurrentThreadName ("prefetch");
|
||||
while (1)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <ripple/resource/impl/Logic.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
@@ -112,6 +113,12 @@ public:
|
||||
|
||||
private:
|
||||
void run ()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &ManagerImp::runImpl, "Resource::Manager::run()");
|
||||
}
|
||||
|
||||
void runImpl ()
|
||||
{
|
||||
beast::Thread::setCurrentThreadName ("Resource::Manager");
|
||||
for(;;)
|
||||
|
||||
@@ -35,5 +35,6 @@
|
||||
#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>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define RIPPLE_WEBSOCKET_SERVER_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/ReportUncaughtException.h>
|
||||
#include <ripple/websocket/WebSocket.h>
|
||||
#include <ripple/beast/core/Thread.h>
|
||||
#include <condition_variable>
|
||||
@@ -58,6 +59,12 @@ public:
|
||||
|
||||
private:
|
||||
void run ()
|
||||
{
|
||||
reportUncaughtException (
|
||||
this, &Server::runImpl, "Server<WebSocket>::run()");
|
||||
}
|
||||
|
||||
void runImpl ()
|
||||
{
|
||||
beast::Thread::setCurrentThreadName ("WebSocket");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user