Kaydet (Commit) ccba41f1 authored tarafından Michael Meeks's avatar Michael Meeks Kaydeden (comit) Andras Timar

tdf#93614 - detect hanging OpenGL drivers with a watchdog.

If an OpenGL zone takes >2s to make progress, disable OpenGL.
If an OpenGL zone takes >5s to make progress, abort the app.

Reviewed-on: https://gerrit.libreoffice.org/17986Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMichael Meeks <michael.meeks@collabora.com>
Tested-by: 's avatarMichael Meeks <michael.meeks@collabora.com>

Conflicts:
	vcl/workben/vcldemo.cxx

Change-Id: I776c06a3f8ba460ff9842a9130c21f9ee2147eee
Reviewed-on: https://gerrit.libreoffice.org/17995Reviewed-by: 's avatarMarkus Mohrhard <markus.mohrhard@googlemail.com>
Reviewed-by: 's avatarMichael Meeks <michael.meeks@collabora.com>
Tested-by: 's avatarMichael Meeks <michael.meeks@collabora.com>
üst 335ff329
/* -*- 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 <sal/types.h>
#include <rtl/ref.hxx>
#include <salhelper/thread.hxx>
class OpenGLWatchdogThread : private salhelper::Thread
{
OpenGLWatchdogThread();
virtual void execute() SAL_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: */
...@@ -7,28 +7,39 @@ ...@@ -7,28 +7,39 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
#ifndef INCLUDED_VCL_INC_OPENGL_GUARD_H #ifndef INCLUDED_VCL_INC_OPENGL_ZONE_H
#define INCLUDED_VCL_INC_OPENGL_GUARD_H #define INCLUDED_VCL_INC_OPENGL_ZONE_H
#include <sal/config.h> #include <sal/config.h>
#include <sal/types.h>
#include <vcl/dllapi.h>
class OpenGLZoneTest;
class OpenGLWatchdogThread;
/** /**
* We want to be able to detect if a given crash came * We want to be able to detect if a given crash came
* from the OpenGL code, so use this helper to track that. * from the OpenGL code, so use this helper to track that.
*/ */
class OpenGLSalGraphicsImpl;
class OpenGLZone { class OpenGLZone {
static int gnInOpenGLZone; friend class OpenGLZoneTest;
friend class OpenGLWatchdogThread;
friend class OpenGLSalGraphicsImpl; friend class OpenGLSalGraphicsImpl;
static void enter() { gnInOpenGLZone++; }
static void leave() { gnInOpenGLZone--; } /// how many times have we entered a GL zone
static volatile sal_uInt64 gnEnterCount;
/// how many times have we left a new GL zone
static volatile sal_uInt64 gnLeaveCount;
static VCL_DLLPUBLIC void enter();
static VCL_DLLPUBLIC void leave();
public: public:
OpenGLZone() { enter(); } OpenGLZone() { gnEnterCount++; }
~OpenGLZone() { leave(); } ~OpenGLZone() { gnLeaveCount++; }
static bool isInZone() { return gnInOpenGLZone > 0; } static bool isInZone() { return gnEnterCount != gnLeaveCount; }
static void hardDisable(); static void hardDisable();
}; };
#endif // INCLUDED_VCL_INC_OPENGL_PROGRAM_H #endif // INCLUDED_VCL_INC_OPENGL_ZONE_H
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
#include "uno/current_context.hxx" #include "uno/current_context.hxx"
#include "opengl/zone.hxx" #include "opengl/zone.hxx"
#include "opengl/watchdog.hxx"
#if OSL_DEBUG_LEVEL > 0 #if OSL_DEBUG_LEVEL > 0
#include <typeinfo> #include <typeinfo>
...@@ -189,7 +190,10 @@ int ImplSVMain() ...@@ -189,7 +190,10 @@ int ImplSVMain()
pSVData->mxAccessBridge.clear(); pSVData->mxAccessBridge.clear();
} }
OpenGLWatchdogThread::stop();
DeInitVCL(); DeInitVCL();
return nReturn; return nReturn;
} }
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include <vector> #include <vector>
#include "opengl/zone.hxx" #include "opengl/zone.hxx"
#include "opengl/watchdog.hxx"
#include <osl/conditn.h>
#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID
#include "opengl/x11/X11DeviceInfo.hxx" #include "opengl/x11/X11DeviceInfo.hxx"
...@@ -446,11 +448,101 @@ bool OpenGLHelper::supportsVCLOpenGL() ...@@ -446,11 +448,101 @@ bool OpenGLHelper::supportsVCLOpenGL()
return true; return true;
} }
/// How many nested OpenGL code-paths are we inside ? sal_uInt64 volatile OpenGLZone::gnEnterCount = 0;
int OpenGLZone::gnInOpenGLZone = 0; sal_uInt64 volatile OpenGLZone::gnLeaveCount = 0;
void OpenGLZone::enter() { gnEnterCount++; }
void OpenGLZone::leave() { gnLeaveCount++; }
namespace {
static volatile bool gbWatchdogFiring = false;
static oslCondition gpWatchdogExit = NULL;
static rtl::Reference<OpenGLWatchdogThread> gxWatchdog;
}
OpenGLWatchdogThread::OpenGLWatchdogThread()
: salhelper::Thread("OpenGL Watchdog")
{
}
void OpenGLWatchdogThread::execute()
{
static const int nDisableEntries = 4; // 2 seconds - disable GL
static const int nAbortAfter = 10; // 5 seconds - not coming back; abort
int nUnchanged = 0; // how many unchanged nEnters
TimeValue aHalfSecond;
aHalfSecond.Seconds = 0;
aHalfSecond.Nanosec = 1000*1000*1000/2;
do {
sal_uInt64 nLastEnters = OpenGLZone::gnEnterCount;
osl_waitCondition(gpWatchdogExit, &aHalfSecond);
if (OpenGLZone::isInZone())
{
if (nLastEnters == OpenGLZone::gnEnterCount)
nUnchanged++;
else
nUnchanged = 0;
SAL_INFO("vcl.opengl", "GL watchdog - unchanged " <<
nUnchanged << " enter count " <<
OpenGLZone::gnEnterCount);
// Not making progress
if (nUnchanged == nDisableEntries)
{
gbWatchdogFiring = true;
SAL_WARN("vcl.opengl", "Watchdog triggered: hard disable GL");
OpenGLZone::hardDisable();
gbWatchdogFiring = false;
}
if (nUnchanged == nAbortAfter)
{
SAL_WARN("vcl.opengl", "Watchdog gave up: aborting");
gbWatchdogFiring = true;
std::abort();
}
}
else
{
nUnchanged = 0;
}
} while (!osl_checkCondition(gpWatchdogExit));
}
void OpenGLWatchdogThread::start()
{
assert (gxWatchdog == NULL);
gpWatchdogExit = osl_createCondition();
gxWatchdog = rtl::Reference<OpenGLWatchdogThread>(new OpenGLWatchdogThread());
gxWatchdog->launch();
}
void OpenGLWatchdogThread::stop()
{
if (gbWatchdogFiring)
return; // in watchdog thread
if (gpWatchdogExit)
osl_setCondition(gpWatchdogExit);
if (gxWatchdog.is())
{
gxWatchdog->join();
gxWatchdog.clear();
}
if (gpWatchdogExit)
osl_destroyCondition(gpWatchdogExit);
gpWatchdogExit = NULL;
}
/** /**
* Called from a signal handler if we get a crash in some GL code * Called from a signal handler or watchdog thread if we get
* a crash or hang in some GL code.
*/ */
void OpenGLZone::hardDisable() void OpenGLZone::hardDisable()
{ {
...@@ -471,6 +563,8 @@ void OpenGLZone::hardDisable() ...@@ -471,6 +563,8 @@ void OpenGLZone::hardDisable()
css::configuration::theDefaultProvider::get( css::configuration::theDefaultProvider::get(
comphelper::getProcessComponentContext()), comphelper::getProcessComponentContext()),
css::uno::UNO_QUERY_THROW)->flush(); css::uno::UNO_QUERY_THROW)->flush();
OpenGLWatchdogThread::stop();
} }
} }
...@@ -502,25 +596,31 @@ bool OpenGLHelper::isVCLOpenGLEnabled() ...@@ -502,25 +596,31 @@ bool OpenGLHelper::isVCLOpenGLEnabled()
bSet = true; bSet = true;
bForceOpenGL = !!getenv("SAL_FORCEGL") || officecfg::Office::Common::VCL::ForceOpenGL::get(); bForceOpenGL = !!getenv("SAL_FORCEGL") || officecfg::Office::Common::VCL::ForceOpenGL::get();
bool bRet = false;
if (bForceOpenGL) if (bForceOpenGL)
return true; bRet = true;
if (!supportsVCLOpenGL()) else if (!supportsVCLOpenGL())
bRet = false;
else
{ {
return false; static bool bEnableGLEnv = !!getenv("SAL_ENABLEGL");
}
static bool bEnableGLEnv = !!getenv("SAL_ENABLEGL"); bEnable = bEnableGLEnv;
bEnable = bEnableGLEnv; static bool bDuringBuild = getenv("VCL_HIDE_WINDOWS");
if (bDuringBuild && !bEnable /* env. enable overrides */)
bEnable = false;
else if (officecfg::Office::Common::VCL::UseOpenGL::get())
bEnable = true;
static bool bDuringBuild = getenv("VCL_HIDE_WINDOWS"); bRet = bEnable;
if (bDuringBuild && !bEnable /* env. enable overrides */) }
bEnable = false; if (bRet)
else if (officecfg::Office::Common::VCL::UseOpenGL::get()) OpenGLWatchdogThread::start();
bEnable = true;
return bEnable; return bRet;
} }
#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined(LIBO_HEADLESS) #if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined(LIBO_HEADLESS)
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <basegfx/numeric/ftools.hxx> #include <basegfx/numeric/ftools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/matrix/b2dhommatrix.hxx>
#include <vcldemo-debug.hxx> #include <vcldemo-debug.hxx>
#include <opengl/zone.hxx>
#include <rtl/math.hxx> #include <rtl/math.hxx>
...@@ -1457,13 +1458,23 @@ class DemoWidgets : public WorkWindow ...@@ -1457,13 +1458,23 @@ class DemoWidgets : public WorkWindow
VclPtr<VclBox> mpBox; VclPtr<VclBox> mpBox;
VclPtr<ToolBox> mpToolbox; VclPtr<ToolBox> mpToolbox;
VclPtr<PushButton> mpButton; VclPtr<PushButton> mpButton;
VclPtr<VclHBox> mpHBox;
VclPtr<CheckBox> mpGLCheck;
VclPtr<ComboBox> mpGLCombo;
VclPtr<PushButton> mpGLButton;
DECL_LINK(GLTestClick, void *);
public: public:
DemoWidgets() : DemoWidgets() :
WorkWindow(NULL, WB_STDWORK), WorkWindow(NULL, WB_STDWORK),
mpBox(VclPtrInstance<VclVBox>(this, false, 3)), mpBox(VclPtrInstance<VclVBox>(this, false, 3)),
mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())), mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())),
mpButton(VclPtrInstance<PushButton>(mpBox.get())) mpButton(VclPtrInstance<PushButton>(mpBox.get())),
mpHBox(VclPtrInstance<VclHBox>(mpBox.get(), true, 3)),
mpGLCheck(VclPtrInstance<CheckBox>(mpHBox.get())),
mpGLCombo(VclPtrInstance<ComboBox>(mpHBox.get())),
mpGLButton(VclPtrInstance<PushButton>(mpHBox.get()))
{ {
SetText("VCL widget demo"); SetText("VCL widget demo");
...@@ -1484,11 +1495,27 @@ public: ...@@ -1484,11 +1495,27 @@ public:
mpButton->SetText("Click me; go on"); mpButton->SetText("Click me; go on");
mpButton->Show(); mpButton->Show();
mpGLCheck->SetText("Test in OGL zone");
mpGLCheck->Show();
mpGLCombo->InsertEntry("sleep 1 second");
mpGLCombo->InsertEntry("sleep 3 seconds");
mpGLCombo->InsertEntry("sleep 7 seconds");
mpGLCombo->SelectEntryPos(2);
mpGLCombo->Show();
mpGLButton->SetText("Execute test");
mpGLButton->SetClickHdl(LINK(this,DemoWidgets,GLTestClick));
mpGLButton->Show();
mpHBox->Show();
Show(); Show();
} }
virtual ~DemoWidgets() { disposeOnce(); } virtual ~DemoWidgets() { disposeOnce(); }
virtual void dispose() SAL_OVERRIDE virtual void dispose() SAL_OVERRIDE
{ {
mpGLButton.disposeAndClear();
mpGLCombo.disposeAndClear();
mpGLCheck.disposeAndClear();
mpHBox.disposeAndClear();
mpBox.disposeAndClear(); mpBox.disposeAndClear();
mpToolbox.disposeAndClear(); mpToolbox.disposeAndClear();
mpButton.disposeAndClear(); mpButton.disposeAndClear();
...@@ -1521,6 +1548,46 @@ public: ...@@ -1521,6 +1548,46 @@ public:
} }
}; };
class OpenGLZoneTest {
public:
static void enter() { OpenGLZone::enter(); }
static void leave() { OpenGLZone::leave(); }
};
IMPL_LINK_NOARG(DemoWidgets,GLTestClick)
{
sal_Int32 nSelected = mpGLCombo->GetSelectEntryPos();
TimeValue aDelay;
aDelay.Seconds = 0;
aDelay.Nanosec = 0;
switch (nSelected)
{
case 0:
aDelay.Seconds = 1;
break;
case 1:
aDelay.Seconds = 3;
break;
case 2:
aDelay.Seconds = 7;
break;
default:
break;
}
bool bEnterLeave = mpGLCheck->IsChecked();
if (bEnterLeave)
OpenGLZoneTest::enter();
osl_waitThread(&aDelay);
if (bEnterLeave)
OpenGLZoneTest::leave();
return 0;
}
class DemoPopup : public FloatingWindow class DemoPopup : public FloatingWindow
{ {
public: public:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment