Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohoswatchdog.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qohosutils.h"
6#include <hicollie/hicollie.h>
7#include <qarkui/qarkuiutils.h>
8#include <qohosbigdataeventlogging.h>
9#include <QtCore/private/qohoslogger_p.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qcoreevent.h>
12#include <QtCore/qobject.h>
13#include <functional>
14#include <mutex>
15
16namespace ch = std::chrono;
17
18QT_BEGIN_NAMESPACE
19
20namespace QtOhosWatchdog {
21
22namespace {
23
24constexpr int resetRatio = 2;
25
26constexpr auto bigDataEnterFreezeStateEventName = "enterFreezeState";
27constexpr auto bigDataRecoveredFromFreezeStateEventName = "recoveredFromFreezeState";
28
29constexpr auto bigDataEventDescriptionPropertyName = "eventDescription";
30constexpr auto bigDataFreezeStateDurationPropertyName = "freezeStateDurationMs";
31
32#ifdef SUPPORT_ASAN
33constexpr auto checkIntervalTime = ch::seconds(45);
34#else
35constexpr auto checkIntervalTime = ch::seconds(3);
36#endif
37
38void logEnterFreezeStateBigDataEvent(ch::time_point<ch::system_clock> eventTime)
39{
40 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
41
42 auto eventBuilder = QtOhos::makeBigEventLoggingEventBuilder(
43 bigDataEnterFreezeStateEventName, ::EventType::STATISTIC, eventTime);
44 eventBuilder->addParam(bigDataEventDescriptionPropertyName, "The application triggered a 6 second freeze");
45 eventBuilder->buildEvent()->trySend();
46}
47
48void logRecoveredFromFreezeStateBigDataEvent(
49 ch::time_point<ch::system_clock> eventTime, ch::system_clock::duration freezeStateDuration)
50{
51 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
52
53 auto eventBuilder = QtOhos::makeBigEventLoggingEventBuilder(
54 bigDataRecoveredFromFreezeStateEventName, ::EventType::STATISTIC, eventTime);
55 eventBuilder->addParam(bigDataEventDescriptionPropertyName, "Recovered from the frozen state");
56 eventBuilder->addParam(
57 bigDataFreezeStateDurationPropertyName,
58 static_cast<std::int64_t>(ch::duration_cast<ch::milliseconds>(freezeStateDuration).count()));
59 eventBuilder->buildEvent()->trySend();
60}
61
62class QtWatchdog : public QObject
63{
64public:
65 QtWatchdog();
66
67 void runHiCollieStuckDetectionTask();
68
69 bool event(QEvent *ev) override;
70
71private:
72 void handleAppMainThreadAliveNotification();
73 void reportStuckEvent();
74
75 ch::steady_clock::time_point m_lastWatchTime;
76 bool m_appMainThreadIsAlive = false;
77 bool m_isSixSecondEvent = false;
78 QOhosOptional<ch::system_clock::time_point> m_sixSecondEventDetectionTime;
79};
80
81QEvent::Type getCheckMainThreadIsAliveQEventType()
82{
83 static auto eventType = static_cast<QEvent::Type>(QEvent::registerEventType());
84 return eventType;
85}
86
87QtWatchdog::QtWatchdog()
88 : QObject()
89{
90 QCoreApplication::postEvent(this, new QEvent(getCheckMainThreadIsAliveQEventType()), Qt::HighEventPriority);
91}
92
93void QtWatchdog::handleAppMainThreadAliveNotification()
94{
95 qCDebug(QtForOhos, "QtWatchdog::handleAppMainThreadAliveNotification");
96 m_appMainThreadIsAlive = true;
97 m_isSixSecondEvent = false;
98 if (m_sixSecondEventDetectionTime.hasValue()) {
99 const auto now = ch::system_clock::now();
100 const auto elapsedTime = now - m_sixSecondEventDetectionTime.value();
101 logRecoveredFromFreezeStateBigDataEvent(now, elapsedTime);
102 m_sixSecondEventDetectionTime.reset();
103 }
104}
105
106void QtWatchdog::runHiCollieStuckDetectionTask()
107{
108 qCDebug(QtForOhos, "QtWatchdog::runHiCollieStuckDetectionTask start");
109
110 if (m_appMainThreadIsAlive) {
111 qCDebug(QtForOhos, "QtWatchdog: m_appMainThreadIsAlive store false");
112 m_appMainThreadIsAlive = false;
113 } else {
114 qCWarning(QtForOhos, "QtWatchdog: AppMainThread is not alive");
115 reportStuckEvent();
116 }
117
118 QCoreApplication::postEvent(this, new QEvent(getCheckMainThreadIsAliveQEventType()), Qt::HighEventPriority);
119
120 auto now = ch::steady_clock::now();
121 if ((now - m_lastWatchTime) >= (checkIntervalTime / resetRatio))
122 m_lastWatchTime = now;
123
124 qCDebug(QtForOhos, "QtWatchdog::runHiCollieStuckDetectionTask end");
125}
126
127bool QtWatchdog::event(QEvent *ev)
128{
129 if (ev->type() == getCheckMainThreadIsAliveQEventType()) {
130 qCDebug(QtForOhos, "QtWatchdog: CheckMainThreadIsAlive event start (%d)", static_cast<int>(getCheckMainThreadIsAliveQEventType()));
131 handleAppMainThreadAliveNotification();
132 qCDebug(QtForOhos, "QCoreApplication: CheckMainThreadIsAlive event end");
133 return true;
134 } else {
135 return QObject::event(ev);
136 }
137}
138
139void QtWatchdog::reportStuckEvent()
140{
141 auto now = ch::steady_clock::now();
142 auto timeDelta = now - m_lastWatchTime;
143 if (timeDelta > resetRatio * checkIntervalTime || timeDelta < checkIntervalTime / resetRatio) {
144 qCWarning(
145 QtForOhos,
146 "QtWatchdog: Thread may be blocked, do not report this time. currTime: %.3f, lastTime: %.3f",
147 ch::duration<double>(now.time_since_epoch()).count(),
148 ch::duration<double>(m_lastWatchTime.time_since_epoch()).count());
149 return;
150 }
151
152 qCDebug(QtForOhos, "QtWatchdog: calling OH_HiCollie_Report(), m_isSixSecondEvent = %d", m_isSixSecondEvent);
153 HiCollie_ErrorCode reportRes = OH_HiCollie_Report(&m_isSixSecondEvent);
154 if (reportRes == HICOLLIE_SUCCESS) {
155 qCDebug(QtForOhos, "QtWatchdog: after OH_HiCollie_Report(), m_isSixSecondEvent = %d", m_isSixSecondEvent);
156 if (m_isSixSecondEvent && !m_sixSecondEventDetectionTime.hasValue()) {
157 m_sixSecondEventDetectionTime = ch::system_clock::now();
158 logEnterFreezeStateBigDataEvent(m_sixSecondEventDetectionTime.value());
159 }
160 } else {
161 qCWarning(QtForOhos, "QtWatchdog: OH_HiCollie_Report() failed with code %d", static_cast<int>(reportRes));
162 }
163}
164
165void runWithQtWatchdogSharedPtr(const std::function<void(std::shared_ptr<QtWatchdog> &)> &runFunc)
166{
167 static std::mutex runMutex;
168 static std::shared_ptr<QtWatchdog> qtWatchdogPtr;
169
170 {
171 std::lock_guard<std::mutex> runLock(runMutex);
172 runFunc(qtWatchdogPtr);
173 }
174}
175
176}
177
178std::shared_ptr<void> makeWatchdog()
179{
180 runWithQtWatchdogSharedPtr(
181 [](auto &watchdogSharedPtr) {
182 watchdogSharedPtr = std::make_shared<QtWatchdog>();
183 });
184
185 auto watchdogHandle = QtOhos::makeDestroyNotifier(
186 []() {
187 runWithQtWatchdogSharedPtr(
188 [](auto &watchdogSharedPtr) {
189 watchdogSharedPtr.reset();
190 });
191 });
192
193 HiCollie_ErrorCode stuckDetectionInitRes = OH_HiCollie_Init_StuckDetection(
194 []() {
195 runWithQtWatchdogSharedPtr(
196 [](auto &watchdogSharedPtr) {
197 if (watchdogSharedPtr)
198 watchdogSharedPtr->runHiCollieStuckDetectionTask();
199 });
200 });
201
202 std::shared_ptr<void> result;
203 if (stuckDetectionInitRes == HICOLLIE_SUCCESS) {
204 result = watchdogHandle;
205 } else {
206 qCWarning(
207 QtForOhos,
208 "OH_HiCollie_Init_StuckDetection() failed with code %d, disabling QtWatchdog",
209 static_cast<int>(stuckDetectionInitRes));
210 result = nullptr;
211 }
212
213 return result;
214}
215
216}
217
218QT_END_NAMESPACE
std::enable_if_t< qohosplugincore_h_detail::isQOhosOptional< QOhosInvokeResult< Func, T > >, QOhosInvokeResult< Func, T > > andThen(Func &&func) const
std::shared_ptr< void > makeWatchdog()