Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/app/watchdog.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <watchdog.hxx>
11
12
#include <config_features.h>
13
14
#include <osl/conditn.hxx>
15
#include <rtl/ref.hxx>
16
#include <rtl/string.hxx>
17
#include <sal/log.hxx>
18
#include <comphelper/debuggerinfo.hxx>
19
#include <opengl/zone.hxx>
20
#include <skia/zone.hxx>
21
22
#include <stdlib.h>
23
24
#if defined HAVE_VALGRIND_HEADERS
25
#include <valgrind/memcheck.h>
26
#endif
27
28
#include <atomic>
29
30
namespace
31
{
32
std::atomic<bool> gbWatchdogFiring = false;
33
osl::Condition* gpWatchdogExit = nullptr;
34
rtl::Reference<WatchdogThread> gxWatchdog;
35
36
template <typename Zone> struct WatchdogHelper
37
{
38
    static inline sal_uInt64 nLastEnters = 0;
39
    static inline int nUnchanged = 0; // how many unchanged nEnters
40
    static inline bool bFired = false;
41
    static inline bool bAbortFired = false;
42
    static void setLastEnters() { nLastEnters = Zone::enterCount(); }
43
    static void check()
44
    {
45
        if (Zone::isInZone())
46
        {
47
            const CrashWatchdogTimingsValues& aTimingValues = Zone::getCrashWatchdogTimingsValues();
48
49
            if (nLastEnters == Zone::enterCount())
50
                nUnchanged++;
51
            else
52
                nUnchanged = 0;
53
            Zone::checkDebug(nUnchanged, aTimingValues);
54
55
            // Not making progress
56
            if (nUnchanged >= aTimingValues.mnDisableEntries)
57
            {
58
                if (!bFired)
59
                {
60
                    gbWatchdogFiring = true;
61
                    SAL_WARN("vcl.watchdog", "Watchdog triggered: hard disable " << Zone::name());
62
#ifdef DBG_UTIL
63
                    std::abort();
64
#else
65
                    Zone::hardDisable();
66
                    gbWatchdogFiring = false;
67
#endif
68
                }
69
                bFired = true;
70
71
                // we can hang using VCL in the abort handling -> be impatient
72
                if (bAbortFired)
73
                {
74
                    SAL_WARN("vcl.watchdog", "Watchdog gave up: hard exiting " << Zone::name());
75
                    _Exit(1);
76
                }
77
            }
78
79
            // Not making even more progress
80
            if (nUnchanged >= aTimingValues.mnAbortAfter)
81
            {
82
                if (!bAbortFired)
83
                {
84
                    SAL_WARN("vcl.watchdog", "Watchdog gave up: aborting " << Zone::name());
85
                    gbWatchdogFiring = true;
86
                    std::abort();
87
                }
88
                // coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet
89
                bAbortFired = true;
90
            }
91
        }
92
        else
93
        {
94
            nUnchanged = 0;
95
        }
96
    }
97
};
98
99
} // namespace
100
101
WatchdogThread::WatchdogThread()
102
0
    : salhelper::Thread("Crash Watchdog")
103
0
{
104
0
}
105
106
void WatchdogThread::execute()
107
0
{
108
0
    TimeValue aQuarterSecond(0, 1000 * 1000 * 1000 * 0.25);
109
0
    do
110
0
    {
111
#if HAVE_FEATURE_OPENGL
112
        WatchdogHelper<OpenGLZone>::setLastEnters();
113
#endif
114
#if HAVE_FEATURE_SKIA
115
        WatchdogHelper<SkiaZone>::setLastEnters();
116
#endif
117
118
0
        gpWatchdogExit->wait(&aQuarterSecond);
119
120
#if defined HAVE_VALGRIND_HEADERS
121
        if (RUNNING_ON_VALGRIND)
122
            continue;
123
#endif
124
#if defined DBG_UTIL
125
        if (comphelper::isDebuggerAttached())
126
            continue;
127
#endif
128
129
#if HAVE_FEATURE_OPENGL
130
        WatchdogHelper<OpenGLZone>::check();
131
#endif
132
#if HAVE_FEATURE_SKIA
133
        WatchdogHelper<SkiaZone>::check();
134
#endif
135
136
0
    } while (!gpWatchdogExit->check());
137
0
}
138
139
void WatchdogThread::start()
140
0
{
141
0
    if (gxWatchdog != nullptr)
142
0
        return; // already running
143
0
    if (getenv("SAL_DISABLE_WATCHDOG"))
144
0
        return;
145
0
    gpWatchdogExit = new osl::Condition();
146
0
    gxWatchdog.set(new WatchdogThread());
147
0
    gxWatchdog->launch();
148
0
}
149
150
void WatchdogThread::stop()
151
0
{
152
0
    if (gbWatchdogFiring)
153
0
        return; // in watchdog thread
154
155
0
    if (gpWatchdogExit)
156
0
        gpWatchdogExit->set();
157
158
0
    if (gxWatchdog.is())
159
0
    {
160
0
        gxWatchdog->join();
161
0
        gxWatchdog.clear();
162
0
    }
163
164
0
    delete gpWatchdogExit;
165
0
    gpWatchdogExit = nullptr;
166
0
}
167
168
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */