merge OpenGLZone and OpenCLZone into one generic code

To be used also by Skia code to detect problems with Vulkan drivers.
The watchdog does not handle OpenCL because neither did the original
code, but also because OpenCLZone::hardDisable() called from it
would deadlock on SolarMutex for some reason.

Change-Id: I483d8cb0b11a4e1e65c564f4e4c29ab68843ff71
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88008
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
This commit is contained in:
Luboš Luňák
2020-02-07 09:11:32 +01:00
parent bfb08ca517
commit 397ad713cf
14 changed files with 323 additions and 269 deletions
+1 -1
View File
@@ -130,7 +130,7 @@ for arg in "$@" $EXTRAOPT ; do
unset MALLOC_CHECK_ MALLOC_PERTURB_ G_SLICE
export SAL_DISABLE_FLOATGRAB=1
export OOO_DISABLE_RECOVERY=1
export SAL_DISABLE_GL_WATCHDOG=1
export SAL_DISABLE_WATCHDOG=1
export LD_BIND_NOW=1
;;
esac
+1 -1
View File
@@ -193,7 +193,7 @@ void Desktop::CheckOpenCLCompute(const Reference< XDesktop2 > &xDesktop)
SAL_INFO("opencl", "Initiating test of OpenCL device");
OpenCLZone aZone;
OpenCLZone::enterInitialTest();
OpenCLInitialZone aInitialZone;
OUString aDevice = officecfg::Office::Calc::Formula::Calculation::OpenCLDevice::get();
OUString aSelectedCLDeviceVersionID;
+72
View File
@@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef INCLUDED_COMPHELPER_CRASHZONE_H
#define INCLUDED_COMPHELPER_CRASHZONE_H
#include <sal/config.h>
#include <sal/types.h>
#include <comphelper/comphelperdllapi.h>
#include <atomic>
#include <csignal>
#include <type_traits>
struct CrashWatchdogTimingsValues
{
/// delays to take various actions in 1/4 of a second increments.
int mnDisableEntries;
int const mnAbortAfter;
};
/**
* A generic class for detecting if a given crash or a lock-up came from a specific
* area of code (such as OpenGL).
* Use this helper to track that.
* The class is a template so that there can be multiple instances of static variables.
*/
template <typename Dummy> class CrashZone
{
// gnEnterCount and gnLeaveCount are accessed both from multiple threads (cf.
// WatchdogThread::execute; so need to be of atomic type) and from signal handlers (cf.
// VCLExceptionSignal_impl; so need to be of lock-free atomic type). sig_atomic_t is chosen as
// the underlying type under the assumption that it is most likely to lead to an atomic type
// that is actually lock-free. However, gnEnterCount and gnLeaveCount are both monotonically
// increasing, so will eventually overflow, so the underlying type better be unsigned, which
// sig_atomic_t is not guaranteed to be:
#if !defined ARM32 || (defined ARM32 && defined __ARM_PCS_VFP)
using AtomicCounter = std::atomic<std::make_unsigned_t<std::sig_atomic_t>>;
static_assert(AtomicCounter::is_always_lock_free);
#else
using AtomicCounter = volatile std::make_unsigned_t<std::sig_atomic_t>;
#endif
/// how many times have we entered a zone
static inline AtomicCounter gnEnterCount = 0;
/// how many times have we left a new zone
static inline AtomicCounter gnLeaveCount = 0;
public:
CrashZone() { enter(); }
~CrashZone() { leave(); }
static bool isInZone() { return gnEnterCount != gnLeaveCount; }
static const AtomicCounter& enterCount() { return gnEnterCount; }
// prefer creating instances to manually calling enter()/leave()
static void enter() { gnEnterCount++; }
static void leave() { gnLeaveCount++; }
// these should be implemented for each specific zone if needed
// static void hardDisable();
// static const CrashWatchdogTimingsValues& getCrashWatchdogTimingsValues();
// static void checkDebug(int nUnchanged, const CrashWatchdogTimingsValues& aTimingValues);
// static const char* name();
};
#endif // INCLUDED_COMPHELPER_CRASHZONE_H
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+8 -34
View File
@@ -12,45 +12,19 @@
#include <sal/config.h>
#include <cassert>
#include <csignal>
#include <opencl/opencldllapi.h>
// FIXME: post back-port, templatize me and share with OpenGLZone.
class OPENCL_DLLPUBLIC OpenCLZone
#include <comphelper/crashzone.hxx>
class OPENCL_DLLPUBLIC OpenCLZone : public CrashZone< OpenCLZone >
{
/// how many times have we entered a CL zone and not yet left it
static volatile std::sig_atomic_t gnEnterCount;
static volatile bool gbInInitialTest;
public:
OpenCLZone()
{
gnEnterCount = gnEnterCount + 1; //TODO: overflow
}
~OpenCLZone()
{
// coverity[assert_side_effect]
assert(gnEnterCount > 0);
gnEnterCount = gnEnterCount - 1;
if (!isInZone())
gbInInitialTest = false;
}
static bool isInZone()
{
return gnEnterCount > 0;
}
static bool isInInitialTest()
{
return gbInInitialTest;
}
static void hardDisable();
static void enterInitialTest();
};
// Used during initial testing of OpenCL.
class OPENCL_DLLPUBLIC OpenCLInitialZone : public CrashZone< OpenCLInitialZone >
{
};
#endif // INCLUDED_OPENCL_INC_OPENCL_ZONE_HXX
+1
View File
@@ -504,6 +504,7 @@ certain functionality.
@li @c vcl.unx.dtrans
@li @c vcl.unx.print
@li @c vcl.virdev
@li @c vcl.watchdog
@li @c vcl.window
@li @c vcl.wmf
-10
View File
@@ -17,11 +17,6 @@
#include <com/sun/star/util/XFlushable.hpp>
#include <com/sun/star/configuration/theDefaultProvider.hpp>
// FIXME: templatize me vs. OpenGLZone.
std::sig_atomic_t volatile OpenCLZone::gnEnterCount = 0;
bool volatile OpenCLZone::gbInInitialTest = false;
/**
* Called from a signal handler if we get
* a crash or hang in some CL code.
@@ -47,9 +42,4 @@ void OpenCLZone::hardDisable()
}
}
void OpenCLZone::enterInitialTest()
{
gbInInitialTest = true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+1
View File
@@ -399,6 +399,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/app/unohelp2 \
vcl/source/app/unohelp \
vcl/source/app/vclevent \
vcl/source/app/watchdog \
vcl/source/app/weldutils \
vcl/source/app/winscheduler \
vcl/source/components/dtranscomp \
+1 -1
View File
@@ -36,7 +36,7 @@ OpenGL
SAL_FORCEGL - force enable OpenGL
SAL_GL_NO_SWAP - disable buffer swapping if set (should show nothing)
SAL_GL_SLEEP_ON_SWAP - sleep for half a second on each swap-buffers.
SAL_DISABLE_GL_WATCHDOG - don't start the thread that watches for broken GL drivers
SAL_DISABLE_WATCHDOG - don't start the thread that watches for broken GL/Vulkan/OpenCL drivers
Skia
----
-67
View File
@@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
#define INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
#include <sal/config.h>
#include <salhelper/thread.hxx>
#include <atomic>
#include <vector>
struct WatchdogTimingsValues
{
/// delays to take various actions in 1/4 of a second increments.
int mnDisableEntries;
int const mnAbortAfter;
};
enum class WatchdogTimingMode
{
NORMAL,
SHADER_COMPILE
};
class WatchdogTimings
{
private:
std::vector<WatchdogTimingsValues> maTimingValues;
std::atomic<bool> mbRelaxed;
public:
WatchdogTimings();
void setRelax(bool bRelaxed)
{
mbRelaxed = bRelaxed;
}
WatchdogTimingsValues const & getWatchdogTimingsValues(WatchdogTimingMode eMode)
{
size_t index = (eMode == WatchdogTimingMode::SHADER_COMPILE) ? 1 : 0;
index = mbRelaxed ? index + 2 : index;
return maTimingValues[index];
}
};
class OpenGLWatchdogThread : private salhelper::Thread
{
OpenGLWatchdogThread();
virtual void execute() override;
public:
using salhelper::Thread::acquire;
using salhelper::Thread::release;
static void start();
static void stop();
};
#endif // INCLUDED_VCL_INC_OPENGL_WATCHDOG_H
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+5 -34
View File
@@ -13,48 +13,19 @@
#include <sal/config.h>
#include <sal/types.h>
#include <vcl/dllapi.h>
#include <atomic>
#include <csignal>
#include <type_traits>
class OpenGLWatchdogThread;
#include <comphelper/crashzone.hxx>
/**
* We want to be able to detect if a given crash came
* from the OpenGL code, so use this helper to track that.
*/
class VCL_DLLPUBLIC OpenGLZone {
friend class OpenGLWatchdogThread;
friend class OpenGLSalGraphicsImpl;
// gnEnterCount and gnLeaveCount are accessed both from multiple threads (cf.
// OpenGLWatchdogThread::execute; so need to be of atomic type) and from signal handlers (cf.
// VCLExceptionSignal_impl; so need to be of lock-free atomic type). sig_atomic_t is chosen as
// the underlying type under the assumption that it is most likely to lead to an atomic type
// that is actually lock-free. However, gnEnterCount and gnLeaveCount are both monotonically
// increasing, so will eventually overflow, so the underlying type better be unsigned, which
// sig_atomic_t is not guaranteed to be:
#if !defined ARM32 || (defined ARM32 && defined __ARM_PCS_VFP)
using AtomicCounter = std::atomic<std::make_unsigned_t<std::sig_atomic_t>>;
static_assert(AtomicCounter::is_always_lock_free);
#else
using AtomicCounter = volatile std::make_unsigned_t<std::sig_atomic_t>;
#endif
/// how many times have we entered a GL zone
static AtomicCounter gnEnterCount;
/// how many times have we left a new GL zone
static AtomicCounter gnLeaveCount;
static void enter() { gnEnterCount++; }
static void leave() { gnLeaveCount++; }
class VCL_DLLPUBLIC OpenGLZone : public CrashZone< OpenGLZone > {
public:
OpenGLZone() { enter(); }
~OpenGLZone() { leave(); }
static bool isInZone() { return gnEnterCount != gnLeaveCount; }
static void hardDisable();
static void relaxWatchdogTimings();
static const CrashWatchdogTimingsValues& getCrashWatchdogTimingsValues();
static void checkDebug( int nUnchanged, const CrashWatchdogTimingsValues& aTimingValues );
static const char* name() { return "OpenGL"; }
};
/// Create this to not only enter the zone, but set VCL context.
+32
View File
@@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef INCLUDED_VCL_INC_WATCHDOG_H
#define INCLUDED_VCL_INC_WATCHDOG_H
#include <sal/config.h>
#include <salhelper/thread.hxx>
#include <atomic>
#include <vector>
class WatchdogThread : private salhelper::Thread
{
WatchdogThread();
virtual void execute() override;
public:
using salhelper::Thread::acquire;
using salhelper::Thread::release;
static void start();
static void stop();
};
#endif // INCLUDED_VCL_INC_WATCHDOG_H
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+3 -5
View File
@@ -88,7 +88,7 @@
#include <opencl/OpenCLZone.hxx>
#include <opengl/zone.hxx>
#include <opengl/watchdog.hxx>
#include <watchdog.hxx>
#include <basegfx/utils/systemdependentdata.hxx>
#include <tools/diagnose_ex.h>
@@ -128,7 +128,7 @@ static oslSignalAction VCLExceptionSignal_impl( void* /*pData*/, oslSignalInfo*
{
OpenCLZone::hardDisable();
#ifdef _WIN32
if (OpenCLZone::isInInitialTest())
if (OpenCLInitialZone::isInZone())
TerminateProcess(GetCurrentProcess(), EXITHELPER_NORMAL_RESTART);
#endif
}
@@ -212,9 +212,7 @@ int ImplSVMain()
pSVData->mxAccessBridge.clear();
}
#if HAVE_FEATURE_OPENGL
OpenGLWatchdogThread::stop();
#endif
WatchdogThread::stop();
DeInitVCL();
return nReturn;
+144
View File
@@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <watchdog.hxx>
#include <config_features.h>
#include <osl/conditn.hxx>
#include <rtl/ref.hxx>
#include <rtl/string.hxx>
#include <sal/log.hxx>
#include <opengl/zone.hxx>
#include <stdlib.h>
namespace
{
static volatile bool gbWatchdogFiring = false;
static osl::Condition* gpWatchdogExit = nullptr;
static rtl::Reference<WatchdogThread> gxWatchdog;
template <typename Zone> struct WatchdogHelper
{
static inline sal_uInt64 nLastEnters = 0;
static inline int nUnchanged = 0; // how many unchanged nEnters
static inline bool bFired = false;
static inline bool bAbortFired = false;
static void setLastEnters() { nLastEnters = Zone::enterCount(); }
static void check()
{
if (Zone::isInZone())
{
const CrashWatchdogTimingsValues& aTimingValues = Zone::getCrashWatchdogTimingsValues();
if (nLastEnters == Zone::enterCount())
nUnchanged++;
else
nUnchanged = 0;
Zone::checkDebug(nUnchanged, aTimingValues);
// Not making progress
if (nUnchanged >= aTimingValues.mnDisableEntries)
{
if (!bFired)
{
gbWatchdogFiring = true;
SAL_WARN("vcl.watchdog",
OStringLiteral("Watchdog triggered: hard disable ") + Zone::name());
Zone::hardDisable();
gbWatchdogFiring = false;
}
bFired = true;
// we can hang using VCL in the abort handling -> be impatient
if (bAbortFired)
{
SAL_WARN("vcl.watchdog",
OStringLiteral("Watchdog gave up: hard exiting ") + Zone::name());
_Exit(1);
}
}
// Not making even more progress
if (nUnchanged >= aTimingValues.mnAbortAfter)
{
if (!bAbortFired)
{
SAL_WARN("vcl.watchdog",
OStringLiteral("Watchdog gave up: aborting ") + Zone::name());
gbWatchdogFiring = true;
std::abort();
}
// coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet
bAbortFired = true;
}
}
else
{
nUnchanged = 0;
}
}
};
} // namespace
WatchdogThread::WatchdogThread()
: salhelper::Thread("Crash Watchdog")
{
}
void WatchdogThread::execute()
{
TimeValue aQuarterSecond(0, 1000 * 1000 * 1000 * 0.25);
do
{
#if HAVE_FEATURE_OPENGL
WatchdogHelper<OpenGLZone>::setLastEnters();
#endif
gpWatchdogExit->wait(&aQuarterSecond);
#if HAVE_FEATURE_OPENGL
WatchdogHelper<OpenGLZone>::check();
#endif
} while (!gpWatchdogExit->check());
}
void WatchdogThread::start()
{
if (gxWatchdog != nullptr)
return; // already running
if (getenv("SAL_DISABLE_WATCHDOG"))
return;
gpWatchdogExit = new osl::Condition();
gxWatchdog.set(new WatchdogThread());
gxWatchdog->launch();
}
void WatchdogThread::stop()
{
if (gbWatchdogFiring)
return; // in watchdog thread
if (gpWatchdogExit)
gpWatchdogExit->set();
if (gxWatchdog.is())
{
gxWatchdog->join();
gxWatchdog.clear();
}
delete gpWatchdogExit;
gpWatchdogExit = nullptr;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+54 -116
View File
@@ -29,12 +29,11 @@
#include <unordered_map>
#include <opengl/zone.hxx>
#include <opengl/watchdog.hxx>
#include <osl/conditn.hxx>
#include <vcl/opengl/OpenGLWrapper.hxx>
#include <vcl/opengl/OpenGLContext.hxx>
#include <desktop/crashreport.hxx>
#include <bitmapwriteaccess.hxx>
#include <watchdog.hxx>
#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined HAIKU
#include <opengl/x11/X11DeviceInfo.hxx>
@@ -45,8 +44,6 @@
#include "GLMHelper.hxx"
static bool volatile gbInShaderCompile = false;
OpenGLZone::AtomicCounter OpenGLZone::gnEnterCount = 0;
OpenGLZone::AtomicCounter OpenGLZone::gnLeaveCount = 0;
namespace {
@@ -793,14 +790,41 @@ bool OpenGLHelper::supportsVCLOpenGL()
return !bDisableGL && !bBlacklisted;
}
namespace {
static volatile bool gbWatchdogFiring = false;
static osl::Condition* gpWatchdogExit = nullptr;
static WatchdogTimings gWatchdogTimings;
static rtl::Reference<OpenGLWatchdogThread> gxWatchdog;
}
namespace
{
WatchdogTimings::WatchdogTimings()
enum class CrashWatchdogTimingMode
{
NORMAL,
SHADER_COMPILE
};
class CrashWatchdogTimings
{
private:
std::vector<CrashWatchdogTimingsValues> maTimingValues;
std::atomic<bool> mbRelaxed;
public:
CrashWatchdogTimings();
void setRelax(bool bRelaxed)
{
mbRelaxed = bRelaxed;
}
CrashWatchdogTimingsValues const & getWatchdogTimingsValues(CrashWatchdogTimingMode eMode)
{
size_t index = (eMode == CrashWatchdogTimingMode::SHADER_COMPILE) ? 1 : 0;
index = mbRelaxed ? index + 2 : index;
return maTimingValues[index];
}
};
static CrashWatchdogTimings gWatchdogTimings;
CrashWatchdogTimings::CrashWatchdogTimings()
: maTimingValues{
{{6, 20} /* 1.5s, 5s */, {20, 120} /* 5s, 30s */,
{60, 240} /* 15s, 60s */, {60, 240} /* 15s, 60s */}
@@ -809,105 +833,7 @@ WatchdogTimings::WatchdogTimings()
{
}
OpenGLWatchdogThread::OpenGLWatchdogThread()
: salhelper::Thread("OpenGL Watchdog")
{
}
void OpenGLWatchdogThread::execute()
{
int nUnchanged = 0; // how many unchanged nEnters
TimeValue aQuarterSecond(0, 1000*1000*1000*0.25);
bool bAbortFired = false;
do {
sal_uInt64 nLastEnters = OpenGLZone::gnEnterCount;
gpWatchdogExit->wait(&aQuarterSecond);
if (OpenGLZone::isInZone())
{
// The shader compiler can take a long time, first time.
WatchdogTimingMode eMode = gbInShaderCompile ? WatchdogTimingMode::SHADER_COMPILE : WatchdogTimingMode::NORMAL;
WatchdogTimingsValues aTimingValues = gWatchdogTimings.getWatchdogTimingsValues(eMode);
if (nLastEnters == OpenGLZone::gnEnterCount)
nUnchanged++;
else
nUnchanged = 0;
SAL_INFO("vcl.opengl", "GL watchdog - unchanged " <<
nUnchanged << " enter count " <<
OpenGLZone::gnEnterCount << " type " <<
(eMode == WatchdogTimingMode::SHADER_COMPILE ? "in shader" : "normal gl") <<
"breakpoints mid: " << aTimingValues.mnDisableEntries <<
" max " << aTimingValues.mnAbortAfter);
// Not making progress
if (nUnchanged >= aTimingValues.mnDisableEntries)
{
static bool bFired = false;
if (!bFired)
{
gbWatchdogFiring = true;
SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL");
OpenGLZone::hardDisable();
gbWatchdogFiring = false;
}
bFired = true;
// we can hang using VCL in the abort handling -> be impatient
if (bAbortFired)
{
SAL_WARN("vcl.opengl", "Watchdog gave up: hard exiting");
_exit(1);
}
}
// Not making even more progress
if (nUnchanged >= aTimingValues.mnAbortAfter)
{
if (!bAbortFired)
{
SAL_WARN("vcl.opengl", "Watchdog gave up: aborting");
gbWatchdogFiring = true;
std::abort();
}
// coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet
bAbortFired = true;
}
}
else
{
nUnchanged = 0;
}
} while (!gpWatchdogExit->check());
}
void OpenGLWatchdogThread::start()
{
assert (gxWatchdog == nullptr);
gpWatchdogExit = new osl::Condition();
gxWatchdog.set(new OpenGLWatchdogThread());
gxWatchdog->launch();
}
void OpenGLWatchdogThread::stop()
{
if (gbWatchdogFiring)
return; // in watchdog thread
if (gpWatchdogExit)
gpWatchdogExit->set();
if (gxWatchdog.is())
{
gxWatchdog->join();
gxWatchdog.clear();
}
delete gpWatchdogExit;
gpWatchdogExit = nullptr;
}
} // namespace
/**
* Called from a signal handler or watchdog thread if we get
@@ -932,8 +858,6 @@ void OpenGLZone::hardDisable()
css::configuration::theDefaultProvider::get(
comphelper::getProcessComponentContext()),
css::uno::UNO_QUERY_THROW)->flush();
OpenGLWatchdogThread::stop();
}
}
@@ -942,6 +866,22 @@ void OpenGLZone::relaxWatchdogTimings()
gWatchdogTimings.setRelax(true);
}
void OpenGLZone::checkDebug( int nUnchanged, const CrashWatchdogTimingsValues& aTimingValues )
{
SAL_INFO("vcl.watchdog", "GL watchdog - unchanged "
<< nUnchanged << " enter count " << enterCount() << " type "
<< (gbInShaderCompile ? "in shader" : "normal gl")
<< " breakpoints mid: " << aTimingValues.mnDisableEntries
<< " max " << aTimingValues.mnAbortAfter);
}
const CrashWatchdogTimingsValues& OpenGLZone::getCrashWatchdogTimingsValues()
{
// The shader compiler can take a long time, first time.
CrashWatchdogTimingMode eMode = gbInShaderCompile ? CrashWatchdogTimingMode::SHADER_COMPILE : CrashWatchdogTimingMode::NORMAL;
return gWatchdogTimings.getWatchdogTimingsValues(eMode);
}
OpenGLVCLContextZone::OpenGLVCLContextZone()
{
OpenGLContext::makeVCLCurrent();
@@ -1019,10 +959,8 @@ bool OpenGLHelper::isVCLOpenGLEnabled()
}
if (bRet)
{
if (!getenv("SAL_DISABLE_GL_WATCHDOG"))
OpenGLWatchdogThread::start();
}
WatchdogThread::start();
CrashReporter::addKeyValue("UseOpenGL", OUString::boolean(bRet), CrashReporter::Write);
return bRet;